sync
[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      // private
2638     dialogEl: false,
2639     bodyEl:  false,
2640     footerEl:  false,
2641     titleEl:  false,
2642     closeEl:  false,
2643
2644     size: '',
2645     
2646     max_width: 0,
2647     
2648     max_height: 0,
2649     
2650     fit_content: false,
2651
2652     onRender : function(ct, position)
2653     {
2654         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2655
2656         if(!this.el){
2657             var cfg = Roo.apply({},  this.getAutoCreate());
2658             cfg.id = Roo.id();
2659             //if(!cfg.name){
2660             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2661             //}
2662             //if (!cfg.name.length) {
2663             //    delete cfg.name;
2664            // }
2665             if (this.cls) {
2666                 cfg.cls += ' ' + this.cls;
2667             }
2668             if (this.style) {
2669                 cfg.style = this.style;
2670             }
2671             this.el = Roo.get(document.body).createChild(cfg, position);
2672         }
2673         //var type = this.el.dom.type;
2674
2675
2676         if(this.tabIndex !== undefined){
2677             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2678         }
2679
2680         this.dialogEl = this.el.select('.modal-dialog',true).first();
2681         this.bodyEl = this.el.select('.modal-body',true).first();
2682         this.closeEl = this.el.select('.modal-header .close', true).first();
2683         this.headerEl = this.el.select('.modal-header',true).first();
2684         this.titleEl = this.el.select('.modal-title',true).first();
2685         this.footerEl = this.el.select('.modal-footer',true).first();
2686
2687         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2688         
2689         //this.el.addClass("x-dlg-modal");
2690
2691         if (this.buttons.length) {
2692             Roo.each(this.buttons, function(bb) {
2693                 var b = Roo.apply({}, bb);
2694                 b.xns = b.xns || Roo.bootstrap;
2695                 b.xtype = b.xtype || 'Button';
2696                 if (typeof(b.listeners) == 'undefined') {
2697                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2698                 }
2699
2700                 var btn = Roo.factory(b);
2701
2702                 btn.render(this.el.select('.modal-footer div').first());
2703
2704             },this);
2705         }
2706         // render the children.
2707         var nitems = [];
2708
2709         if(typeof(this.items) != 'undefined'){
2710             var items = this.items;
2711             delete this.items;
2712
2713             for(var i =0;i < items.length;i++) {
2714                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2715             }
2716         }
2717
2718         this.items = nitems;
2719
2720         // where are these used - they used to be body/close/footer
2721
2722
2723         this.initEvents();
2724         //this.el.addClass([this.fieldClass, this.cls]);
2725
2726     },
2727
2728     getAutoCreate : function()
2729     {
2730         var bdy = {
2731                 cls : 'modal-body',
2732                 html : this.html || ''
2733         };
2734
2735         var title = {
2736             tag: 'h4',
2737             cls : 'modal-title',
2738             html : this.title
2739         };
2740
2741         if(this.specificTitle){
2742             title = this.title;
2743
2744         };
2745
2746         var header = [];
2747         if (this.allow_close) {
2748             header.push({
2749                 tag: 'button',
2750                 cls : 'close',
2751                 html : '&times'
2752             });
2753         }
2754
2755         header.push(title);
2756
2757         var size = '';
2758
2759         if(this.size.length){
2760             size = 'modal-' + this.size;
2761         }
2762
2763         var modal = {
2764             cls: "modal",
2765              cn : [
2766                 {
2767                     cls: "modal-dialog " + size,
2768                     cn : [
2769                         {
2770                             cls : "modal-content",
2771                             cn : [
2772                                 {
2773                                     cls : 'modal-header',
2774                                     cn : header
2775                                 },
2776                                 bdy,
2777                                 {
2778                                     cls : 'modal-footer',
2779                                     cn : [
2780                                         {
2781                                             tag: 'div',
2782                                             cls: 'btn-' + this.buttonPosition
2783                                         }
2784                                     ]
2785
2786                                 }
2787
2788
2789                             ]
2790
2791                         }
2792                     ]
2793
2794                 }
2795             ]
2796         };
2797
2798         if(this.animate){
2799             modal.cls += ' fade';
2800         }
2801
2802         return modal;
2803
2804     },
2805     getChildContainer : function() {
2806
2807          return this.bodyEl;
2808
2809     },
2810     getButtonContainer : function() {
2811          return this.el.select('.modal-footer div',true).first();
2812
2813     },
2814     initEvents : function()
2815     {
2816         if (this.allow_close) {
2817             this.closeEl.on('click', this.hide, this);
2818         }
2819         Roo.EventManager.onWindowResize(this.resize, this, true);
2820
2821
2822     },
2823
2824     resize : function()
2825     {
2826         this.maskEl.setSize(
2827             Roo.lib.Dom.getViewWidth(true),
2828             Roo.lib.Dom.getViewHeight(true)
2829         );
2830         
2831         if (this.fitwindow) {
2832             this.setSize(
2833                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2834                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2835             );
2836             return;
2837         }
2838         
2839         if(this.max_width !== 0) {
2840             
2841             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2842             
2843             if(this.height) {
2844                 this.setSize(w, this.height);
2845                 return;
2846             }
2847             
2848             if(this.max_height) {
2849                 this.setSize(w,Math.min(
2850                     this.max_height,
2851                     Roo.lib.Dom.getViewportHeight(true) - 60
2852                 ));
2853                 
2854                 return;
2855             }
2856             
2857             if(!this.fit_content) {
2858                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2859                 return;
2860             }
2861             
2862             this.setSize(w, Math.min(
2863                 60 +
2864                 this.headerEl.getHeight() + 
2865                 this.footerEl.getHeight() + 
2866                 this.getChildHeight(this.bodyEl.dom.childNodes),
2867                 Roo.lib.Dom.getViewportHeight(true) - 60)
2868             );
2869         }
2870         
2871     },
2872
2873     setSize : function(w,h)
2874     {
2875         if (!w && !h) {
2876             return;
2877         }
2878         
2879         this.resizeTo(w,h);
2880     },
2881
2882     show : function() {
2883
2884         if (!this.rendered) {
2885             this.render();
2886         }
2887
2888         //this.el.setStyle('display', 'block');
2889         this.el.removeClass('hideing');        
2890         this.el.addClass('show');
2891  
2892         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2893             var _this = this;
2894             (function(){
2895                 this.el.addClass('in');
2896             }).defer(50, this);
2897         }else{
2898             this.el.addClass('in');
2899         }
2900
2901         // not sure how we can show data in here..
2902         //if (this.tmpl) {
2903         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2904         //}
2905
2906         Roo.get(document.body).addClass("x-body-masked");
2907         
2908         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2909         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910         this.maskEl.addClass('show');
2911         
2912         this.resize();
2913         
2914         this.fireEvent('show', this);
2915
2916         // set zindex here - otherwise it appears to be ignored...
2917         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2918
2919         (function () {
2920             this.items.forEach( function(e) {
2921                 e.layout ? e.layout() : false;
2922
2923             });
2924         }).defer(100,this);
2925
2926     },
2927     hide : function()
2928     {
2929         if(this.fireEvent("beforehide", this) !== false){
2930             this.maskEl.removeClass('show');
2931             Roo.get(document.body).removeClass("x-body-masked");
2932             this.el.removeClass('in');
2933             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2934
2935             if(this.animate){ // why
2936                 this.el.addClass('hideing');
2937                 (function(){
2938                     if (!this.el.hasClass('hideing')) {
2939                         return; // it's been shown again...
2940                     }
2941                     this.el.removeClass('show');
2942                     this.el.removeClass('hideing');
2943                 }).defer(150,this);
2944                 
2945             }else{
2946                  this.el.removeClass('show');
2947             }
2948             this.fireEvent('hide', this);
2949         }
2950     },
2951     isVisible : function()
2952     {
2953         
2954         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2955         
2956     },
2957
2958     addButton : function(str, cb)
2959     {
2960
2961
2962         var b = Roo.apply({}, { html : str } );
2963         b.xns = b.xns || Roo.bootstrap;
2964         b.xtype = b.xtype || 'Button';
2965         if (typeof(b.listeners) == 'undefined') {
2966             b.listeners = { click : cb.createDelegate(this)  };
2967         }
2968
2969         var btn = Roo.factory(b);
2970
2971         btn.render(this.el.select('.modal-footer div').first());
2972
2973         return btn;
2974
2975     },
2976
2977     setDefaultButton : function(btn)
2978     {
2979         //this.el.select('.modal-footer').()
2980     },
2981     diff : false,
2982
2983     resizeTo: function(w,h)
2984     {
2985         // skip.. ?? why??
2986
2987         this.dialogEl.setWidth(w);
2988         if (this.diff === false) {
2989             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2990         }
2991
2992         this.bodyEl.setHeight(h - this.diff);
2993
2994         this.fireEvent('resize', this);
2995
2996     },
2997     setContentSize  : function(w, h)
2998     {
2999
3000     },
3001     onButtonClick: function(btn,e)
3002     {
3003         //Roo.log([a,b,c]);
3004         this.fireEvent('btnclick', btn.name, e);
3005     },
3006      /**
3007      * Set the title of the Dialog
3008      * @param {String} str new Title
3009      */
3010     setTitle: function(str) {
3011         this.titleEl.dom.innerHTML = str;
3012     },
3013     /**
3014      * Set the body of the Dialog
3015      * @param {String} str new Title
3016      */
3017     setBody: function(str) {
3018         this.bodyEl.dom.innerHTML = str;
3019     },
3020     /**
3021      * Set the body of the Dialog using the template
3022      * @param {Obj} data - apply this data to the template and replace the body contents.
3023      */
3024     applyBody: function(obj)
3025     {
3026         if (!this.tmpl) {
3027             Roo.log("Error - using apply Body without a template");
3028             //code
3029         }
3030         this.tmpl.overwrite(this.bodyEl, obj);
3031     },
3032     
3033     getChildHeight : function(child_nodes)
3034     {
3035         if(
3036             !child_nodes ||
3037             child_nodes.length == 0
3038         ) {
3039             return;
3040         }
3041         
3042         var child_height = 0;
3043         
3044         for(var i = 0; i < child_nodes.length; i++) {
3045             
3046             /*
3047             * for modal with tabs...
3048             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3049                 
3050                 var layout_childs = child_nodes[i].childNodes;
3051                 
3052                 for(var j = 0; j < layout_childs.length; j++) {
3053                     
3054                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3055                         
3056                         var layout_body_childs = layout_childs[j].childNodes;
3057                         
3058                         for(var k = 0; k < layout_body_childs.length; k++) {
3059                             
3060                             if(layout_body_childs[k].classList.contains('navbar')) {
3061                                 child_height += layout_body_childs[k].offsetHeight;
3062                                 continue;
3063                             }
3064                             
3065                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3066                                 
3067                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3068                                 
3069                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3070                                     
3071                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3072                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3073                                         continue;
3074                                     }
3075                                     
3076                                 }
3077                                 
3078                             }
3079                             
3080                         }
3081                     }
3082                 }
3083                 continue;
3084             }
3085             */
3086             
3087             child_height += child_nodes[i].offsetHeight;
3088             // Roo.log(child_nodes[i].offsetHeight);
3089         }
3090         
3091         return child_height;
3092     }
3093
3094 });
3095
3096
3097 Roo.apply(Roo.bootstrap.Modal,  {
3098     /**
3099          * Button config that displays a single OK button
3100          * @type Object
3101          */
3102         OK :  [{
3103             name : 'ok',
3104             weight : 'primary',
3105             html : 'OK'
3106         }],
3107         /**
3108          * Button config that displays Yes and No buttons
3109          * @type Object
3110          */
3111         YESNO : [
3112             {
3113                 name  : 'no',
3114                 html : 'No'
3115             },
3116             {
3117                 name  :'yes',
3118                 weight : 'primary',
3119                 html : 'Yes'
3120             }
3121         ],
3122
3123         /**
3124          * Button config that displays OK and Cancel buttons
3125          * @type Object
3126          */
3127         OKCANCEL : [
3128             {
3129                name : 'cancel',
3130                 html : 'Cancel'
3131             },
3132             {
3133                 name : 'ok',
3134                 weight : 'primary',
3135                 html : 'OK'
3136             }
3137         ],
3138         /**
3139          * Button config that displays Yes, No and Cancel buttons
3140          * @type Object
3141          */
3142         YESNOCANCEL : [
3143             {
3144                 name : 'yes',
3145                 weight : 'primary',
3146                 html : 'Yes'
3147             },
3148             {
3149                 name : 'no',
3150                 html : 'No'
3151             },
3152             {
3153                 name : 'cancel',
3154                 html : 'Cancel'
3155             }
3156         ],
3157         
3158         zIndex : 10001
3159 });
3160 /*
3161  * - LGPL
3162  *
3163  * messagebox - can be used as a replace
3164  * 
3165  */
3166 /**
3167  * @class Roo.MessageBox
3168  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3169  * Example usage:
3170  *<pre><code>
3171 // Basic alert:
3172 Roo.Msg.alert('Status', 'Changes saved successfully.');
3173
3174 // Prompt for user data:
3175 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3176     if (btn == 'ok'){
3177         // process text value...
3178     }
3179 });
3180
3181 // Show a dialog using config options:
3182 Roo.Msg.show({
3183    title:'Save Changes?',
3184    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3185    buttons: Roo.Msg.YESNOCANCEL,
3186    fn: processResult,
3187    animEl: 'elId'
3188 });
3189 </code></pre>
3190  * @singleton
3191  */
3192 Roo.bootstrap.MessageBox = function(){
3193     var dlg, opt, mask, waitTimer;
3194     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3195     var buttons, activeTextEl, bwidth;
3196
3197     
3198     // private
3199     var handleButton = function(button){
3200         dlg.hide();
3201         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3202     };
3203
3204     // private
3205     var handleHide = function(){
3206         if(opt && opt.cls){
3207             dlg.el.removeClass(opt.cls);
3208         }
3209         //if(waitTimer){
3210         //    Roo.TaskMgr.stop(waitTimer);
3211         //    waitTimer = null;
3212         //}
3213     };
3214
3215     // private
3216     var updateButtons = function(b){
3217         var width = 0;
3218         if(!b){
3219             buttons["ok"].hide();
3220             buttons["cancel"].hide();
3221             buttons["yes"].hide();
3222             buttons["no"].hide();
3223             //dlg.footer.dom.style.display = 'none';
3224             return width;
3225         }
3226         dlg.footerEl.dom.style.display = '';
3227         for(var k in buttons){
3228             if(typeof buttons[k] != "function"){
3229                 if(b[k]){
3230                     buttons[k].show();
3231                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3232                     width += buttons[k].el.getWidth()+15;
3233                 }else{
3234                     buttons[k].hide();
3235                 }
3236             }
3237         }
3238         return width;
3239     };
3240
3241     // private
3242     var handleEsc = function(d, k, e){
3243         if(opt && opt.closable !== false){
3244             dlg.hide();
3245         }
3246         if(e){
3247             e.stopEvent();
3248         }
3249     };
3250
3251     return {
3252         /**
3253          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3254          * @return {Roo.BasicDialog} The BasicDialog element
3255          */
3256         getDialog : function(){
3257            if(!dlg){
3258                 dlg = new Roo.bootstrap.Modal( {
3259                     //draggable: true,
3260                     //resizable:false,
3261                     //constraintoviewport:false,
3262                     //fixedcenter:true,
3263                     //collapsible : false,
3264                     //shim:true,
3265                     //modal: true,
3266                 //    width: 'auto',
3267                   //  height:100,
3268                     //buttonAlign:"center",
3269                     closeClick : function(){
3270                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3271                             handleButton("no");
3272                         }else{
3273                             handleButton("cancel");
3274                         }
3275                     }
3276                 });
3277                 dlg.render();
3278                 dlg.on("hide", handleHide);
3279                 mask = dlg.mask;
3280                 //dlg.addKeyListener(27, handleEsc);
3281                 buttons = {};
3282                 this.buttons = buttons;
3283                 var bt = this.buttonText;
3284                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3285                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3286                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3287                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3288                 //Roo.log(buttons);
3289                 bodyEl = dlg.bodyEl.createChild({
3290
3291                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3292                         '<textarea class="roo-mb-textarea"></textarea>' +
3293                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3294                 });
3295                 msgEl = bodyEl.dom.firstChild;
3296                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3297                 textboxEl.enableDisplayMode();
3298                 textboxEl.addKeyListener([10,13], function(){
3299                     if(dlg.isVisible() && opt && opt.buttons){
3300                         if(opt.buttons.ok){
3301                             handleButton("ok");
3302                         }else if(opt.buttons.yes){
3303                             handleButton("yes");
3304                         }
3305                     }
3306                 });
3307                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3308                 textareaEl.enableDisplayMode();
3309                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3310                 progressEl.enableDisplayMode();
3311                 
3312                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3313                 var pf = progressEl.dom.firstChild;
3314                 if (pf) {
3315                     pp = Roo.get(pf.firstChild);
3316                     pp.setHeight(pf.offsetHeight);
3317                 }
3318                 
3319             }
3320             return dlg;
3321         },
3322
3323         /**
3324          * Updates the message box body text
3325          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3326          * the XHTML-compliant non-breaking space character '&amp;#160;')
3327          * @return {Roo.MessageBox} This message box
3328          */
3329         updateText : function(text)
3330         {
3331             if(!dlg.isVisible() && !opt.width){
3332                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3333                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3334             }
3335             msgEl.innerHTML = text || '&#160;';
3336       
3337             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3338             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3339             var w = Math.max(
3340                     Math.min(opt.width || cw , this.maxWidth), 
3341                     Math.max(opt.minWidth || this.minWidth, bwidth)
3342             );
3343             if(opt.prompt){
3344                 activeTextEl.setWidth(w);
3345             }
3346             if(dlg.isVisible()){
3347                 dlg.fixedcenter = false;
3348             }
3349             // to big, make it scroll. = But as usual stupid IE does not support
3350             // !important..
3351             
3352             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3353                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3354                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3355             } else {
3356                 bodyEl.dom.style.height = '';
3357                 bodyEl.dom.style.overflowY = '';
3358             }
3359             if (cw > w) {
3360                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3361             } else {
3362                 bodyEl.dom.style.overflowX = '';
3363             }
3364             
3365             dlg.setContentSize(w, bodyEl.getHeight());
3366             if(dlg.isVisible()){
3367                 dlg.fixedcenter = true;
3368             }
3369             return this;
3370         },
3371
3372         /**
3373          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3374          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3375          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3376          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3377          * @return {Roo.MessageBox} This message box
3378          */
3379         updateProgress : function(value, text){
3380             if(text){
3381                 this.updateText(text);
3382             }
3383             
3384             if (pp) { // weird bug on my firefox - for some reason this is not defined
3385                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3386                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3387             }
3388             return this;
3389         },        
3390
3391         /**
3392          * Returns true if the message box is currently displayed
3393          * @return {Boolean} True if the message box is visible, else false
3394          */
3395         isVisible : function(){
3396             return dlg && dlg.isVisible();  
3397         },
3398
3399         /**
3400          * Hides the message box if it is displayed
3401          */
3402         hide : function(){
3403             if(this.isVisible()){
3404                 dlg.hide();
3405             }  
3406         },
3407
3408         /**
3409          * Displays a new message box, or reinitializes an existing message box, based on the config options
3410          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3411          * The following config object properties are supported:
3412          * <pre>
3413 Property    Type             Description
3414 ----------  ---------------  ------------------------------------------------------------------------------------
3415 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3416                                    closes (defaults to undefined)
3417 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3418                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3419 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3420                                    progress and wait dialogs will ignore this property and always hide the
3421                                    close button as they can only be closed programmatically.
3422 cls               String           A custom CSS class to apply to the message box element
3423 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3424                                    displayed (defaults to 75)
3425 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3426                                    function will be btn (the name of the button that was clicked, if applicable,
3427                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3428                                    Progress and wait dialogs will ignore this option since they do not respond to
3429                                    user actions and can only be closed programmatically, so any required function
3430                                    should be called by the same code after it closes the dialog.
3431 icon              String           A CSS class that provides a background image to be used as an icon for
3432                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3433 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3434 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3435 modal             Boolean          False to allow user interaction with the page while the message box is
3436                                    displayed (defaults to true)
3437 msg               String           A string that will replace the existing message box body text (defaults
3438                                    to the XHTML-compliant non-breaking space character '&#160;')
3439 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3440 progress          Boolean          True to display a progress bar (defaults to false)
3441 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3442 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3443 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3444 title             String           The title text
3445 value             String           The string value to set into the active textbox element if displayed
3446 wait              Boolean          True to display a progress bar (defaults to false)
3447 width             Number           The width of the dialog in pixels
3448 </pre>
3449          *
3450          * Example usage:
3451          * <pre><code>
3452 Roo.Msg.show({
3453    title: 'Address',
3454    msg: 'Please enter your address:',
3455    width: 300,
3456    buttons: Roo.MessageBox.OKCANCEL,
3457    multiline: true,
3458    fn: saveAddress,
3459    animEl: 'addAddressBtn'
3460 });
3461 </code></pre>
3462          * @param {Object} config Configuration options
3463          * @return {Roo.MessageBox} This message box
3464          */
3465         show : function(options)
3466         {
3467             
3468             // this causes nightmares if you show one dialog after another
3469             // especially on callbacks..
3470              
3471             if(this.isVisible()){
3472                 
3473                 this.hide();
3474                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3475                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3476                 Roo.log("New Dialog Message:" +  options.msg )
3477                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3478                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3479                 
3480             }
3481             var d = this.getDialog();
3482             opt = options;
3483             d.setTitle(opt.title || "&#160;");
3484             d.closeEl.setDisplayed(opt.closable !== false);
3485             activeTextEl = textboxEl;
3486             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3487             if(opt.prompt){
3488                 if(opt.multiline){
3489                     textboxEl.hide();
3490                     textareaEl.show();
3491                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3492                         opt.multiline : this.defaultTextHeight);
3493                     activeTextEl = textareaEl;
3494                 }else{
3495                     textboxEl.show();
3496                     textareaEl.hide();
3497                 }
3498             }else{
3499                 textboxEl.hide();
3500                 textareaEl.hide();
3501             }
3502             progressEl.setDisplayed(opt.progress === true);
3503             this.updateProgress(0);
3504             activeTextEl.dom.value = opt.value || "";
3505             if(opt.prompt){
3506                 dlg.setDefaultButton(activeTextEl);
3507             }else{
3508                 var bs = opt.buttons;
3509                 var db = null;
3510                 if(bs && bs.ok){
3511                     db = buttons["ok"];
3512                 }else if(bs && bs.yes){
3513                     db = buttons["yes"];
3514                 }
3515                 dlg.setDefaultButton(db);
3516             }
3517             bwidth = updateButtons(opt.buttons);
3518             this.updateText(opt.msg);
3519             if(opt.cls){
3520                 d.el.addClass(opt.cls);
3521             }
3522             d.proxyDrag = opt.proxyDrag === true;
3523             d.modal = opt.modal !== false;
3524             d.mask = opt.modal !== false ? mask : false;
3525             if(!d.isVisible()){
3526                 // force it to the end of the z-index stack so it gets a cursor in FF
3527                 document.body.appendChild(dlg.el.dom);
3528                 d.animateTarget = null;
3529                 d.show(options.animEl);
3530             }
3531             return this;
3532         },
3533
3534         /**
3535          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3536          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3537          * and closing the message box when the process is complete.
3538          * @param {String} title The title bar text
3539          * @param {String} msg The message box body text
3540          * @return {Roo.MessageBox} This message box
3541          */
3542         progress : function(title, msg){
3543             this.show({
3544                 title : title,
3545                 msg : msg,
3546                 buttons: false,
3547                 progress:true,
3548                 closable:false,
3549                 minWidth: this.minProgressWidth,
3550                 modal : true
3551             });
3552             return this;
3553         },
3554
3555         /**
3556          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3557          * If a callback function is passed it will be called after the user clicks the button, and the
3558          * id of the button that was clicked will be passed as the only parameter to the callback
3559          * (could also be the top-right close button).
3560          * @param {String} title The title bar text
3561          * @param {String} msg The message box body text
3562          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3563          * @param {Object} scope (optional) The scope of the callback function
3564          * @return {Roo.MessageBox} This message box
3565          */
3566         alert : function(title, msg, fn, scope)
3567         {
3568             this.show({
3569                 title : title,
3570                 msg : msg,
3571                 buttons: this.OK,
3572                 fn: fn,
3573                 closable : false,
3574                 scope : scope,
3575                 modal : true
3576             });
3577             return this;
3578         },
3579
3580         /**
3581          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3582          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3583          * You are responsible for closing the message box when the process is complete.
3584          * @param {String} msg The message box body text
3585          * @param {String} title (optional) The title bar text
3586          * @return {Roo.MessageBox} This message box
3587          */
3588         wait : function(msg, title){
3589             this.show({
3590                 title : title,
3591                 msg : msg,
3592                 buttons: false,
3593                 closable:false,
3594                 progress:true,
3595                 modal:true,
3596                 width:300,
3597                 wait:true
3598             });
3599             waitTimer = Roo.TaskMgr.start({
3600                 run: function(i){
3601                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3602                 },
3603                 interval: 1000
3604             });
3605             return this;
3606         },
3607
3608         /**
3609          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3610          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3611          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3612          * @param {String} title The title bar text
3613          * @param {String} msg The message box body text
3614          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3615          * @param {Object} scope (optional) The scope of the callback function
3616          * @return {Roo.MessageBox} This message box
3617          */
3618         confirm : function(title, msg, fn, scope){
3619             this.show({
3620                 title : title,
3621                 msg : msg,
3622                 buttons: this.YESNO,
3623                 fn: fn,
3624                 scope : scope,
3625                 modal : true
3626             });
3627             return this;
3628         },
3629
3630         /**
3631          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3632          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3633          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3634          * (could also be the top-right close button) and the text that was entered will be passed as the two
3635          * parameters to the callback.
3636          * @param {String} title The title bar text
3637          * @param {String} msg The message box body text
3638          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3639          * @param {Object} scope (optional) The scope of the callback function
3640          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3641          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3642          * @return {Roo.MessageBox} This message box
3643          */
3644         prompt : function(title, msg, fn, scope, multiline){
3645             this.show({
3646                 title : title,
3647                 msg : msg,
3648                 buttons: this.OKCANCEL,
3649                 fn: fn,
3650                 minWidth:250,
3651                 scope : scope,
3652                 prompt:true,
3653                 multiline: multiline,
3654                 modal : true
3655             });
3656             return this;
3657         },
3658
3659         /**
3660          * Button config that displays a single OK button
3661          * @type Object
3662          */
3663         OK : {ok:true},
3664         /**
3665          * Button config that displays Yes and No buttons
3666          * @type Object
3667          */
3668         YESNO : {yes:true, no:true},
3669         /**
3670          * Button config that displays OK and Cancel buttons
3671          * @type Object
3672          */
3673         OKCANCEL : {ok:true, cancel:true},
3674         /**
3675          * Button config that displays Yes, No and Cancel buttons
3676          * @type Object
3677          */
3678         YESNOCANCEL : {yes:true, no:true, cancel:true},
3679
3680         /**
3681          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3682          * @type Number
3683          */
3684         defaultTextHeight : 75,
3685         /**
3686          * The maximum width in pixels of the message box (defaults to 600)
3687          * @type Number
3688          */
3689         maxWidth : 600,
3690         /**
3691          * The minimum width in pixels of the message box (defaults to 100)
3692          * @type Number
3693          */
3694         minWidth : 100,
3695         /**
3696          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3697          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3698          * @type Number
3699          */
3700         minProgressWidth : 250,
3701         /**
3702          * An object containing the default button text strings that can be overriden for localized language support.
3703          * Supported properties are: ok, cancel, yes and no.
3704          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3705          * @type Object
3706          */
3707         buttonText : {
3708             ok : "OK",
3709             cancel : "Cancel",
3710             yes : "Yes",
3711             no : "No"
3712         }
3713     };
3714 }();
3715
3716 /**
3717  * Shorthand for {@link Roo.MessageBox}
3718  */
3719 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3720 Roo.Msg = Roo.Msg || Roo.MessageBox;
3721 /*
3722  * - LGPL
3723  *
3724  * navbar
3725  * 
3726  */
3727
3728 /**
3729  * @class Roo.bootstrap.Navbar
3730  * @extends Roo.bootstrap.Component
3731  * Bootstrap Navbar class
3732
3733  * @constructor
3734  * Create a new Navbar
3735  * @param {Object} config The config object
3736  */
3737
3738
3739 Roo.bootstrap.Navbar = function(config){
3740     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3741     this.addEvents({
3742         // raw events
3743         /**
3744          * @event beforetoggle
3745          * Fire before toggle the menu
3746          * @param {Roo.EventObject} e
3747          */
3748         "beforetoggle" : true
3749     });
3750 };
3751
3752 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3753     
3754     
3755    
3756     // private
3757     navItems : false,
3758     loadMask : false,
3759     
3760     
3761     getAutoCreate : function(){
3762         
3763         
3764         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3765         
3766     },
3767     
3768     initEvents :function ()
3769     {
3770         //Roo.log(this.el.select('.navbar-toggle',true));
3771         this.el.select('.navbar-toggle',true).on('click', function() {
3772             if(this.fireEvent('beforetoggle', this) !== false){
3773                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3774             }
3775             
3776         }, this);
3777         
3778         var mark = {
3779             tag: "div",
3780             cls:"x-dlg-mask"
3781         };
3782         
3783         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3784         
3785         var size = this.el.getSize();
3786         this.maskEl.setSize(size.width, size.height);
3787         this.maskEl.enableDisplayMode("block");
3788         this.maskEl.hide();
3789         
3790         if(this.loadMask){
3791             this.maskEl.show();
3792         }
3793     },
3794     
3795     
3796     getChildContainer : function()
3797     {
3798         if (this.el.select('.collapse').getCount()) {
3799             return this.el.select('.collapse',true).first();
3800         }
3801         
3802         return this.el;
3803     },
3804     
3805     mask : function()
3806     {
3807         this.maskEl.show();
3808     },
3809     
3810     unmask : function()
3811     {
3812         this.maskEl.hide();
3813     } 
3814     
3815     
3816     
3817     
3818 });
3819
3820
3821
3822  
3823
3824  /*
3825  * - LGPL
3826  *
3827  * navbar
3828  * 
3829  */
3830
3831 /**
3832  * @class Roo.bootstrap.NavSimplebar
3833  * @extends Roo.bootstrap.Navbar
3834  * Bootstrap Sidebar class
3835  *
3836  * @cfg {Boolean} inverse is inverted color
3837  * 
3838  * @cfg {String} type (nav | pills | tabs)
3839  * @cfg {Boolean} arrangement stacked | justified
3840  * @cfg {String} align (left | right) alignment
3841  * 
3842  * @cfg {Boolean} main (true|false) main nav bar? default false
3843  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3844  * 
3845  * @cfg {String} tag (header|footer|nav|div) default is nav 
3846
3847  * 
3848  * 
3849  * 
3850  * @constructor
3851  * Create a new Sidebar
3852  * @param {Object} config The config object
3853  */
3854
3855
3856 Roo.bootstrap.NavSimplebar = function(config){
3857     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3858 };
3859
3860 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3861     
3862     inverse: false,
3863     
3864     type: false,
3865     arrangement: '',
3866     align : false,
3867     
3868     
3869     
3870     main : false,
3871     
3872     
3873     tag : false,
3874     
3875     
3876     getAutoCreate : function(){
3877         
3878         
3879         var cfg = {
3880             tag : this.tag || 'div',
3881             cls : 'navbar'
3882         };
3883           
3884         
3885         cfg.cn = [
3886             {
3887                 cls: 'nav',
3888                 tag : 'ul'
3889             }
3890         ];
3891         
3892          
3893         this.type = this.type || 'nav';
3894         if (['tabs','pills'].indexOf(this.type)!==-1) {
3895             cfg.cn[0].cls += ' nav-' + this.type
3896         
3897         
3898         } else {
3899             if (this.type!=='nav') {
3900                 Roo.log('nav type must be nav/tabs/pills')
3901             }
3902             cfg.cn[0].cls += ' navbar-nav'
3903         }
3904         
3905         
3906         
3907         
3908         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3909             cfg.cn[0].cls += ' nav-' + this.arrangement;
3910         }
3911         
3912         
3913         if (this.align === 'right') {
3914             cfg.cn[0].cls += ' navbar-right';
3915         }
3916         
3917         if (this.inverse) {
3918             cfg.cls += ' navbar-inverse';
3919             
3920         }
3921         
3922         
3923         return cfg;
3924     
3925         
3926     }
3927     
3928     
3929     
3930 });
3931
3932
3933
3934  
3935
3936  
3937        /*
3938  * - LGPL
3939  *
3940  * navbar
3941  * 
3942  */
3943
3944 /**
3945  * @class Roo.bootstrap.NavHeaderbar
3946  * @extends Roo.bootstrap.NavSimplebar
3947  * Bootstrap Sidebar class
3948  *
3949  * @cfg {String} brand what is brand
3950  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3951  * @cfg {String} brand_href href of the brand
3952  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3953  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3954  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3955  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3956  * 
3957  * @constructor
3958  * Create a new Sidebar
3959  * @param {Object} config The config object
3960  */
3961
3962
3963 Roo.bootstrap.NavHeaderbar = function(config){
3964     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3965       
3966 };
3967
3968 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3969     
3970     position: '',
3971     brand: '',
3972     brand_href: false,
3973     srButton : true,
3974     autohide : false,
3975     desktopCenter : false,
3976    
3977     
3978     getAutoCreate : function(){
3979         
3980         var   cfg = {
3981             tag: this.nav || 'nav',
3982             cls: 'navbar',
3983             role: 'navigation',
3984             cn: []
3985         };
3986         
3987         var cn = cfg.cn;
3988         if (this.desktopCenter) {
3989             cn.push({cls : 'container', cn : []});
3990             cn = cn[0].cn;
3991         }
3992         
3993         if(this.srButton){
3994             cn.push({
3995                 tag: 'div',
3996                 cls: 'navbar-header',
3997                 cn: [
3998                     {
3999                         tag: 'button',
4000                         type: 'button',
4001                         cls: 'navbar-toggle',
4002                         'data-toggle': 'collapse',
4003                         cn: [
4004                             {
4005                                 tag: 'span',
4006                                 cls: 'sr-only',
4007                                 html: 'Toggle navigation'
4008                             },
4009                             {
4010                                 tag: 'span',
4011                                 cls: 'icon-bar'
4012                             },
4013                             {
4014                                 tag: 'span',
4015                                 cls: 'icon-bar'
4016                             },
4017                             {
4018                                 tag: 'span',
4019                                 cls: 'icon-bar'
4020                             }
4021                         ]
4022                     }
4023                 ]
4024             });
4025         }
4026         
4027         cn.push({
4028             tag: 'div',
4029             cls: 'collapse navbar-collapse',
4030             cn : []
4031         });
4032         
4033         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4034         
4035         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4036             cfg.cls += ' navbar-' + this.position;
4037             
4038             // tag can override this..
4039             
4040             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4041         }
4042         
4043         if (this.brand !== '') {
4044             cn[0].cn.push({
4045                 tag: 'a',
4046                 href: this.brand_href ? this.brand_href : '#',
4047                 cls: 'navbar-brand',
4048                 cn: [
4049                 this.brand
4050                 ]
4051             });
4052         }
4053         
4054         if(this.main){
4055             cfg.cls += ' main-nav';
4056         }
4057         
4058         
4059         return cfg;
4060
4061         
4062     },
4063     getHeaderChildContainer : function()
4064     {
4065         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4066             return this.el.select('.navbar-header',true).first();
4067         }
4068         
4069         return this.getChildContainer();
4070     },
4071     
4072     
4073     initEvents : function()
4074     {
4075         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4076         
4077         if (this.autohide) {
4078             
4079             var prevScroll = 0;
4080             var ft = this.el;
4081             
4082             Roo.get(document).on('scroll',function(e) {
4083                 var ns = Roo.get(document).getScroll().top;
4084                 var os = prevScroll;
4085                 prevScroll = ns;
4086                 
4087                 if(ns > os){
4088                     ft.removeClass('slideDown');
4089                     ft.addClass('slideUp');
4090                     return;
4091                 }
4092                 ft.removeClass('slideUp');
4093                 ft.addClass('slideDown');
4094                  
4095               
4096           },this);
4097         }
4098     }    
4099     
4100 });
4101
4102
4103
4104  
4105
4106  /*
4107  * - LGPL
4108  *
4109  * navbar
4110  * 
4111  */
4112
4113 /**
4114  * @class Roo.bootstrap.NavSidebar
4115  * @extends Roo.bootstrap.Navbar
4116  * Bootstrap Sidebar class
4117  * 
4118  * @constructor
4119  * Create a new Sidebar
4120  * @param {Object} config The config object
4121  */
4122
4123
4124 Roo.bootstrap.NavSidebar = function(config){
4125     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4126 };
4127
4128 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4129     
4130     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4131     
4132     getAutoCreate : function(){
4133         
4134         
4135         return  {
4136             tag: 'div',
4137             cls: 'sidebar sidebar-nav'
4138         };
4139     
4140         
4141     }
4142     
4143     
4144     
4145 });
4146
4147
4148
4149  
4150
4151  /*
4152  * - LGPL
4153  *
4154  * nav group
4155  * 
4156  */
4157
4158 /**
4159  * @class Roo.bootstrap.NavGroup
4160  * @extends Roo.bootstrap.Component
4161  * Bootstrap NavGroup class
4162  * @cfg {String} align (left|right)
4163  * @cfg {Boolean} inverse
4164  * @cfg {String} type (nav|pills|tab) default nav
4165  * @cfg {String} navId - reference Id for navbar.
4166
4167  * 
4168  * @constructor
4169  * Create a new nav group
4170  * @param {Object} config The config object
4171  */
4172
4173 Roo.bootstrap.NavGroup = function(config){
4174     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4175     this.navItems = [];
4176    
4177     Roo.bootstrap.NavGroup.register(this);
4178      this.addEvents({
4179         /**
4180              * @event changed
4181              * Fires when the active item changes
4182              * @param {Roo.bootstrap.NavGroup} this
4183              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4184              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4185          */
4186         'changed': true
4187      });
4188     
4189 };
4190
4191 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4192     
4193     align: '',
4194     inverse: false,
4195     form: false,
4196     type: 'nav',
4197     navId : '',
4198     // private
4199     
4200     navItems : false, 
4201     
4202     getAutoCreate : function()
4203     {
4204         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4205         
4206         cfg = {
4207             tag : 'ul',
4208             cls: 'nav' 
4209         };
4210         
4211         if (['tabs','pills'].indexOf(this.type)!==-1) {
4212             cfg.cls += ' nav-' + this.type
4213         } else {
4214             if (this.type!=='nav') {
4215                 Roo.log('nav type must be nav/tabs/pills')
4216             }
4217             cfg.cls += ' navbar-nav'
4218         }
4219         
4220         if (this.parent() && this.parent().sidebar) {
4221             cfg = {
4222                 tag: 'ul',
4223                 cls: 'dashboard-menu sidebar-menu'
4224             };
4225             
4226             return cfg;
4227         }
4228         
4229         if (this.form === true) {
4230             cfg = {
4231                 tag: 'form',
4232                 cls: 'navbar-form'
4233             };
4234             
4235             if (this.align === 'right') {
4236                 cfg.cls += ' navbar-right';
4237             } else {
4238                 cfg.cls += ' navbar-left';
4239             }
4240         }
4241         
4242         if (this.align === 'right') {
4243             cfg.cls += ' navbar-right';
4244         }
4245         
4246         if (this.inverse) {
4247             cfg.cls += ' navbar-inverse';
4248             
4249         }
4250         
4251         
4252         return cfg;
4253     },
4254     /**
4255     * sets the active Navigation item
4256     * @param {Roo.bootstrap.NavItem} the new current navitem
4257     */
4258     setActiveItem : function(item)
4259     {
4260         var prev = false;
4261         Roo.each(this.navItems, function(v){
4262             if (v == item) {
4263                 return ;
4264             }
4265             if (v.isActive()) {
4266                 v.setActive(false, true);
4267                 prev = v;
4268                 
4269             }
4270             
4271         });
4272
4273         item.setActive(true, true);
4274         this.fireEvent('changed', this, item, prev);
4275         
4276         
4277     },
4278     /**
4279     * gets the active Navigation item
4280     * @return {Roo.bootstrap.NavItem} the current navitem
4281     */
4282     getActive : function()
4283     {
4284         
4285         var prev = false;
4286         Roo.each(this.navItems, function(v){
4287             
4288             if (v.isActive()) {
4289                 prev = v;
4290                 
4291             }
4292             
4293         });
4294         return prev;
4295     },
4296     
4297     indexOfNav : function()
4298     {
4299         
4300         var prev = false;
4301         Roo.each(this.navItems, function(v,i){
4302             
4303             if (v.isActive()) {
4304                 prev = i;
4305                 
4306             }
4307             
4308         });
4309         return prev;
4310     },
4311     /**
4312     * adds a Navigation item
4313     * @param {Roo.bootstrap.NavItem} the navitem to add
4314     */
4315     addItem : function(cfg)
4316     {
4317         var cn = new Roo.bootstrap.NavItem(cfg);
4318         this.register(cn);
4319         cn.parentId = this.id;
4320         cn.onRender(this.el, null);
4321         return cn;
4322     },
4323     /**
4324     * register a Navigation item
4325     * @param {Roo.bootstrap.NavItem} the navitem to add
4326     */
4327     register : function(item)
4328     {
4329         this.navItems.push( item);
4330         item.navId = this.navId;
4331     
4332     },
4333     
4334     /**
4335     * clear all the Navigation item
4336     */
4337    
4338     clearAll : function()
4339     {
4340         this.navItems = [];
4341         this.el.dom.innerHTML = '';
4342     },
4343     
4344     getNavItem: function(tabId)
4345     {
4346         var ret = false;
4347         Roo.each(this.navItems, function(e) {
4348             if (e.tabId == tabId) {
4349                ret =  e;
4350                return false;
4351             }
4352             return true;
4353             
4354         });
4355         return ret;
4356     },
4357     
4358     setActiveNext : function()
4359     {
4360         var i = this.indexOfNav(this.getActive());
4361         if (i > this.navItems.length) {
4362             return;
4363         }
4364         this.setActiveItem(this.navItems[i+1]);
4365     },
4366     setActivePrev : function()
4367     {
4368         var i = this.indexOfNav(this.getActive());
4369         if (i  < 1) {
4370             return;
4371         }
4372         this.setActiveItem(this.navItems[i-1]);
4373     },
4374     clearWasActive : function(except) {
4375         Roo.each(this.navItems, function(e) {
4376             if (e.tabId != except.tabId && e.was_active) {
4377                e.was_active = false;
4378                return false;
4379             }
4380             return true;
4381             
4382         });
4383     },
4384     getWasActive : function ()
4385     {
4386         var r = false;
4387         Roo.each(this.navItems, function(e) {
4388             if (e.was_active) {
4389                r = e;
4390                return false;
4391             }
4392             return true;
4393             
4394         });
4395         return r;
4396     }
4397     
4398     
4399 });
4400
4401  
4402 Roo.apply(Roo.bootstrap.NavGroup, {
4403     
4404     groups: {},
4405      /**
4406     * register a Navigation Group
4407     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4408     */
4409     register : function(navgrp)
4410     {
4411         this.groups[navgrp.navId] = navgrp;
4412         
4413     },
4414     /**
4415     * fetch a Navigation Group based on the navigation ID
4416     * @param {string} the navgroup to add
4417     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4418     */
4419     get: function(navId) {
4420         if (typeof(this.groups[navId]) == 'undefined') {
4421             return false;
4422             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4423         }
4424         return this.groups[navId] ;
4425     }
4426     
4427     
4428     
4429 });
4430
4431  /*
4432  * - LGPL
4433  *
4434  * row
4435  * 
4436  */
4437
4438 /**
4439  * @class Roo.bootstrap.NavItem
4440  * @extends Roo.bootstrap.Component
4441  * Bootstrap Navbar.NavItem class
4442  * @cfg {String} href  link to
4443  * @cfg {String} html content of button
4444  * @cfg {String} badge text inside badge
4445  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4446  * @cfg {String} glyphicon name of glyphicon
4447  * @cfg {String} icon name of font awesome icon
4448  * @cfg {Boolean} active Is item active
4449  * @cfg {Boolean} disabled Is item disabled
4450  
4451  * @cfg {Boolean} preventDefault (true | false) default false
4452  * @cfg {String} tabId the tab that this item activates.
4453  * @cfg {String} tagtype (a|span) render as a href or span?
4454  * @cfg {Boolean} animateRef (true|false) link to element default false  
4455   
4456  * @constructor
4457  * Create a new Navbar Item
4458  * @param {Object} config The config object
4459  */
4460 Roo.bootstrap.NavItem = function(config){
4461     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4462     this.addEvents({
4463         // raw events
4464         /**
4465          * @event click
4466          * The raw click event for the entire grid.
4467          * @param {Roo.EventObject} e
4468          */
4469         "click" : true,
4470          /**
4471             * @event changed
4472             * Fires when the active item active state changes
4473             * @param {Roo.bootstrap.NavItem} this
4474             * @param {boolean} state the new state
4475              
4476          */
4477         'changed': true,
4478         /**
4479             * @event scrollto
4480             * Fires when scroll to element
4481             * @param {Roo.bootstrap.NavItem} this
4482             * @param {Object} options
4483             * @param {Roo.EventObject} e
4484              
4485          */
4486         'scrollto': true
4487     });
4488    
4489 };
4490
4491 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4492     
4493     href: false,
4494     html: '',
4495     badge: '',
4496     icon: false,
4497     glyphicon: false,
4498     active: false,
4499     preventDefault : false,
4500     tabId : false,
4501     tagtype : 'a',
4502     disabled : false,
4503     animateRef : false,
4504     was_active : false,
4505     
4506     getAutoCreate : function(){
4507          
4508         var cfg = {
4509             tag: 'li',
4510             cls: 'nav-item'
4511             
4512         };
4513         
4514         if (this.active) {
4515             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4516         }
4517         if (this.disabled) {
4518             cfg.cls += ' disabled';
4519         }
4520         
4521         if (this.href || this.html || this.glyphicon || this.icon) {
4522             cfg.cn = [
4523                 {
4524                     tag: this.tagtype,
4525                     href : this.href || "#",
4526                     html: this.html || ''
4527                 }
4528             ];
4529             
4530             if (this.icon) {
4531                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4532             }
4533
4534             if(this.glyphicon) {
4535                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4536             }
4537             
4538             if (this.menu) {
4539                 
4540                 cfg.cn[0].html += " <span class='caret'></span>";
4541              
4542             }
4543             
4544             if (this.badge !== '') {
4545                  
4546                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4547             }
4548         }
4549         
4550         
4551         
4552         return cfg;
4553     },
4554     initEvents: function() 
4555     {
4556         if (typeof (this.menu) != 'undefined') {
4557             this.menu.parentType = this.xtype;
4558             this.menu.triggerEl = this.el;
4559             this.menu = this.addxtype(Roo.apply({}, this.menu));
4560         }
4561         
4562         this.el.select('a',true).on('click', this.onClick, this);
4563         
4564         if(this.tagtype == 'span'){
4565             this.el.select('span',true).on('click', this.onClick, this);
4566         }
4567        
4568         // at this point parent should be available..
4569         this.parent().register(this);
4570     },
4571     
4572     onClick : function(e)
4573     {
4574         if (e.getTarget('.dropdown-menu-item')) {
4575             // did you click on a menu itemm.... - then don't trigger onclick..
4576             return;
4577         }
4578         
4579         if(
4580                 this.preventDefault || 
4581                 this.href == '#' 
4582         ){
4583             Roo.log("NavItem - prevent Default?");
4584             e.preventDefault();
4585         }
4586         
4587         if (this.disabled) {
4588             return;
4589         }
4590         
4591         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4592         if (tg && tg.transition) {
4593             Roo.log("waiting for the transitionend");
4594             return;
4595         }
4596         
4597         
4598         
4599         //Roo.log("fire event clicked");
4600         if(this.fireEvent('click', this, e) === false){
4601             return;
4602         };
4603         
4604         if(this.tagtype == 'span'){
4605             return;
4606         }
4607         
4608         //Roo.log(this.href);
4609         var ael = this.el.select('a',true).first();
4610         //Roo.log(ael);
4611         
4612         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4613             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4614             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4615                 return; // ignore... - it's a 'hash' to another page.
4616             }
4617             Roo.log("NavItem - prevent Default?");
4618             e.preventDefault();
4619             this.scrollToElement(e);
4620         }
4621         
4622         
4623         var p =  this.parent();
4624    
4625         if (['tabs','pills'].indexOf(p.type)!==-1) {
4626             if (typeof(p.setActiveItem) !== 'undefined') {
4627                 p.setActiveItem(this);
4628             }
4629         }
4630         
4631         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4632         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4633             // remove the collapsed menu expand...
4634             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4635         }
4636     },
4637     
4638     isActive: function () {
4639         return this.active
4640     },
4641     setActive : function(state, fire, is_was_active)
4642     {
4643         if (this.active && !state && this.navId) {
4644             this.was_active = true;
4645             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4646             if (nv) {
4647                 nv.clearWasActive(this);
4648             }
4649             
4650         }
4651         this.active = state;
4652         
4653         if (!state ) {
4654             this.el.removeClass('active');
4655         } else if (!this.el.hasClass('active')) {
4656             this.el.addClass('active');
4657         }
4658         if (fire) {
4659             this.fireEvent('changed', this, state);
4660         }
4661         
4662         // show a panel if it's registered and related..
4663         
4664         if (!this.navId || !this.tabId || !state || is_was_active) {
4665             return;
4666         }
4667         
4668         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4669         if (!tg) {
4670             return;
4671         }
4672         var pan = tg.getPanelByName(this.tabId);
4673         if (!pan) {
4674             return;
4675         }
4676         // if we can not flip to new panel - go back to old nav highlight..
4677         if (false == tg.showPanel(pan)) {
4678             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4679             if (nv) {
4680                 var onav = nv.getWasActive();
4681                 if (onav) {
4682                     onav.setActive(true, false, true);
4683                 }
4684             }
4685             
4686         }
4687         
4688         
4689         
4690     },
4691      // this should not be here...
4692     setDisabled : function(state)
4693     {
4694         this.disabled = state;
4695         if (!state ) {
4696             this.el.removeClass('disabled');
4697         } else if (!this.el.hasClass('disabled')) {
4698             this.el.addClass('disabled');
4699         }
4700         
4701     },
4702     
4703     /**
4704      * Fetch the element to display the tooltip on.
4705      * @return {Roo.Element} defaults to this.el
4706      */
4707     tooltipEl : function()
4708     {
4709         return this.el.select('' + this.tagtype + '', true).first();
4710     },
4711     
4712     scrollToElement : function(e)
4713     {
4714         var c = document.body;
4715         
4716         /*
4717          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4718          */
4719         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4720             c = document.documentElement;
4721         }
4722         
4723         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4724         
4725         if(!target){
4726             return;
4727         }
4728
4729         var o = target.calcOffsetsTo(c);
4730         
4731         var options = {
4732             target : target,
4733             value : o[1]
4734         };
4735         
4736         this.fireEvent('scrollto', this, options, e);
4737         
4738         Roo.get(c).scrollTo('top', options.value, true);
4739         
4740         return;
4741     }
4742 });
4743  
4744
4745  /*
4746  * - LGPL
4747  *
4748  * sidebar item
4749  *
4750  *  li
4751  *    <span> icon </span>
4752  *    <span> text </span>
4753  *    <span>badge </span>
4754  */
4755
4756 /**
4757  * @class Roo.bootstrap.NavSidebarItem
4758  * @extends Roo.bootstrap.NavItem
4759  * Bootstrap Navbar.NavSidebarItem class
4760  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4761  * {Boolean} open is the menu open
4762  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4763  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4764  * {String} buttonSize (sm|md|lg)the extra classes for the button
4765  * {Boolean} showArrow show arrow next to the text (default true)
4766  * @constructor
4767  * Create a new Navbar Button
4768  * @param {Object} config The config object
4769  */
4770 Roo.bootstrap.NavSidebarItem = function(config){
4771     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4772     this.addEvents({
4773         // raw events
4774         /**
4775          * @event click
4776          * The raw click event for the entire grid.
4777          * @param {Roo.EventObject} e
4778          */
4779         "click" : true,
4780          /**
4781             * @event changed
4782             * Fires when the active item active state changes
4783             * @param {Roo.bootstrap.NavSidebarItem} this
4784             * @param {boolean} state the new state
4785              
4786          */
4787         'changed': true
4788     });
4789    
4790 };
4791
4792 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4793     
4794     badgeWeight : 'default',
4795     
4796     open: false,
4797     
4798     buttonView : false,
4799     
4800     buttonWeight : 'default',
4801     
4802     buttonSize : 'md',
4803     
4804     showArrow : true,
4805     
4806     getAutoCreate : function(){
4807         
4808         
4809         var a = {
4810                 tag: 'a',
4811                 href : this.href || '#',
4812                 cls: '',
4813                 html : '',
4814                 cn : []
4815         };
4816         
4817         if(this.buttonView){
4818             a = {
4819                 tag: 'button',
4820                 href : this.href || '#',
4821                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4822                 html : this.html,
4823                 cn : []
4824             };
4825         }
4826         
4827         var cfg = {
4828             tag: 'li',
4829             cls: '',
4830             cn: [ a ]
4831         };
4832         
4833         if (this.active) {
4834             cfg.cls += ' active';
4835         }
4836         
4837         if (this.disabled) {
4838             cfg.cls += ' disabled';
4839         }
4840         if (this.open) {
4841             cfg.cls += ' open x-open';
4842         }
4843         // left icon..
4844         if (this.glyphicon || this.icon) {
4845             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4846             a.cn.push({ tag : 'i', cls : c }) ;
4847         }
4848         
4849         if(!this.buttonView){
4850             var span = {
4851                 tag: 'span',
4852                 html : this.html || ''
4853             };
4854
4855             a.cn.push(span);
4856             
4857         }
4858         
4859         if (this.badge !== '') {
4860             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4861         }
4862         
4863         if (this.menu) {
4864             
4865             if(this.showArrow){
4866                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4867             }
4868             
4869             a.cls += ' dropdown-toggle treeview' ;
4870         }
4871         
4872         return cfg;
4873     },
4874     
4875     initEvents : function()
4876     { 
4877         if (typeof (this.menu) != 'undefined') {
4878             this.menu.parentType = this.xtype;
4879             this.menu.triggerEl = this.el;
4880             this.menu = this.addxtype(Roo.apply({}, this.menu));
4881         }
4882         
4883         this.el.on('click', this.onClick, this);
4884         
4885         if(this.badge !== ''){
4886             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4887         }
4888         
4889     },
4890     
4891     onClick : function(e)
4892     {
4893         if(this.disabled){
4894             e.preventDefault();
4895             return;
4896         }
4897         
4898         if(this.preventDefault){
4899             e.preventDefault();
4900         }
4901         
4902         this.fireEvent('click', this);
4903     },
4904     
4905     disable : function()
4906     {
4907         this.setDisabled(true);
4908     },
4909     
4910     enable : function()
4911     {
4912         this.setDisabled(false);
4913     },
4914     
4915     setDisabled : function(state)
4916     {
4917         if(this.disabled == state){
4918             return;
4919         }
4920         
4921         this.disabled = state;
4922         
4923         if (state) {
4924             this.el.addClass('disabled');
4925             return;
4926         }
4927         
4928         this.el.removeClass('disabled');
4929         
4930         return;
4931     },
4932     
4933     setActive : function(state)
4934     {
4935         if(this.active == state){
4936             return;
4937         }
4938         
4939         this.active = state;
4940         
4941         if (state) {
4942             this.el.addClass('active');
4943             return;
4944         }
4945         
4946         this.el.removeClass('active');
4947         
4948         return;
4949     },
4950     
4951     isActive: function () 
4952     {
4953         return this.active;
4954     },
4955     
4956     setBadge : function(str)
4957     {
4958         if(!this.badgeEl){
4959             return;
4960         }
4961         
4962         this.badgeEl.dom.innerHTML = str;
4963     }
4964     
4965    
4966      
4967  
4968 });
4969  
4970
4971  /*
4972  * - LGPL
4973  *
4974  * row
4975  * 
4976  */
4977
4978 /**
4979  * @class Roo.bootstrap.Row
4980  * @extends Roo.bootstrap.Component
4981  * Bootstrap Row class (contains columns...)
4982  * 
4983  * @constructor
4984  * Create a new Row
4985  * @param {Object} config The config object
4986  */
4987
4988 Roo.bootstrap.Row = function(config){
4989     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4990 };
4991
4992 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4993     
4994     getAutoCreate : function(){
4995        return {
4996             cls: 'row clearfix'
4997        };
4998     }
4999     
5000     
5001 });
5002
5003  
5004
5005  /*
5006  * - LGPL
5007  *
5008  * element
5009  * 
5010  */
5011
5012 /**
5013  * @class Roo.bootstrap.Element
5014  * @extends Roo.bootstrap.Component
5015  * Bootstrap Element class
5016  * @cfg {String} html contents of the element
5017  * @cfg {String} tag tag of the element
5018  * @cfg {String} cls class of the element
5019  * @cfg {Boolean} preventDefault (true|false) default false
5020  * @cfg {Boolean} clickable (true|false) default false
5021  * 
5022  * @constructor
5023  * Create a new Element
5024  * @param {Object} config The config object
5025  */
5026
5027 Roo.bootstrap.Element = function(config){
5028     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5029     
5030     this.addEvents({
5031         // raw events
5032         /**
5033          * @event click
5034          * When a element is chick
5035          * @param {Roo.bootstrap.Element} this
5036          * @param {Roo.EventObject} e
5037          */
5038         "click" : true
5039     });
5040 };
5041
5042 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5043     
5044     tag: 'div',
5045     cls: '',
5046     html: '',
5047     preventDefault: false, 
5048     clickable: false,
5049     
5050     getAutoCreate : function(){
5051         
5052         var cfg = {
5053             tag: this.tag,
5054             // cls: this.cls, double assign in parent class Component.js :: onRender
5055             html: this.html
5056         };
5057         
5058         return cfg;
5059     },
5060     
5061     initEvents: function() 
5062     {
5063         Roo.bootstrap.Element.superclass.initEvents.call(this);
5064         
5065         if(this.clickable){
5066             this.el.on('click', this.onClick, this);
5067         }
5068         
5069     },
5070     
5071     onClick : function(e)
5072     {
5073         if(this.preventDefault){
5074             e.preventDefault();
5075         }
5076         
5077         this.fireEvent('click', this, e);
5078     },
5079     
5080     getValue : function()
5081     {
5082         return this.el.dom.innerHTML;
5083     },
5084     
5085     setValue : function(value)
5086     {
5087         this.el.dom.innerHTML = value;
5088     }
5089    
5090 });
5091
5092  
5093
5094  /*
5095  * - LGPL
5096  *
5097  * pagination
5098  * 
5099  */
5100
5101 /**
5102  * @class Roo.bootstrap.Pagination
5103  * @extends Roo.bootstrap.Component
5104  * Bootstrap Pagination class
5105  * @cfg {String} size xs | sm | md | lg
5106  * @cfg {Boolean} inverse false | true
5107  * 
5108  * @constructor
5109  * Create a new Pagination
5110  * @param {Object} config The config object
5111  */
5112
5113 Roo.bootstrap.Pagination = function(config){
5114     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5115 };
5116
5117 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5118     
5119     cls: false,
5120     size: false,
5121     inverse: false,
5122     
5123     getAutoCreate : function(){
5124         var cfg = {
5125             tag: 'ul',
5126                 cls: 'pagination'
5127         };
5128         if (this.inverse) {
5129             cfg.cls += ' inverse';
5130         }
5131         if (this.html) {
5132             cfg.html=this.html;
5133         }
5134         if (this.cls) {
5135             cfg.cls += " " + this.cls;
5136         }
5137         return cfg;
5138     }
5139    
5140 });
5141
5142  
5143
5144  /*
5145  * - LGPL
5146  *
5147  * Pagination item
5148  * 
5149  */
5150
5151
5152 /**
5153  * @class Roo.bootstrap.PaginationItem
5154  * @extends Roo.bootstrap.Component
5155  * Bootstrap PaginationItem class
5156  * @cfg {String} html text
5157  * @cfg {String} href the link
5158  * @cfg {Boolean} preventDefault (true | false) default true
5159  * @cfg {Boolean} active (true | false) default false
5160  * @cfg {Boolean} disabled default false
5161  * 
5162  * 
5163  * @constructor
5164  * Create a new PaginationItem
5165  * @param {Object} config The config object
5166  */
5167
5168
5169 Roo.bootstrap.PaginationItem = function(config){
5170     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5171     this.addEvents({
5172         // raw events
5173         /**
5174          * @event click
5175          * The raw click event for the entire grid.
5176          * @param {Roo.EventObject} e
5177          */
5178         "click" : true
5179     });
5180 };
5181
5182 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5183     
5184     href : false,
5185     html : false,
5186     preventDefault: true,
5187     active : false,
5188     cls : false,
5189     disabled: false,
5190     
5191     getAutoCreate : function(){
5192         var cfg= {
5193             tag: 'li',
5194             cn: [
5195                 {
5196                     tag : 'a',
5197                     href : this.href ? this.href : '#',
5198                     html : this.html ? this.html : ''
5199                 }
5200             ]
5201         };
5202         
5203         if(this.cls){
5204             cfg.cls = this.cls;
5205         }
5206         
5207         if(this.disabled){
5208             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5209         }
5210         
5211         if(this.active){
5212             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5213         }
5214         
5215         return cfg;
5216     },
5217     
5218     initEvents: function() {
5219         
5220         this.el.on('click', this.onClick, this);
5221         
5222     },
5223     onClick : function(e)
5224     {
5225         Roo.log('PaginationItem on click ');
5226         if(this.preventDefault){
5227             e.preventDefault();
5228         }
5229         
5230         if(this.disabled){
5231             return;
5232         }
5233         
5234         this.fireEvent('click', this, e);
5235     }
5236    
5237 });
5238
5239  
5240
5241  /*
5242  * - LGPL
5243  *
5244  * slider
5245  * 
5246  */
5247
5248
5249 /**
5250  * @class Roo.bootstrap.Slider
5251  * @extends Roo.bootstrap.Component
5252  * Bootstrap Slider class
5253  *    
5254  * @constructor
5255  * Create a new Slider
5256  * @param {Object} config The config object
5257  */
5258
5259 Roo.bootstrap.Slider = function(config){
5260     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5261 };
5262
5263 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5264     
5265     getAutoCreate : function(){
5266         
5267         var cfg = {
5268             tag: 'div',
5269             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5270             cn: [
5271                 {
5272                     tag: 'a',
5273                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5274                 }
5275             ]
5276         };
5277         
5278         return cfg;
5279     }
5280    
5281 });
5282
5283  /*
5284  * Based on:
5285  * Ext JS Library 1.1.1
5286  * Copyright(c) 2006-2007, Ext JS, LLC.
5287  *
5288  * Originally Released Under LGPL - original licence link has changed is not relivant.
5289  *
5290  * Fork - LGPL
5291  * <script type="text/javascript">
5292  */
5293  
5294
5295 /**
5296  * @class Roo.grid.ColumnModel
5297  * @extends Roo.util.Observable
5298  * This is the default implementation of a ColumnModel used by the Grid. It defines
5299  * the columns in the grid.
5300  * <br>Usage:<br>
5301  <pre><code>
5302  var colModel = new Roo.grid.ColumnModel([
5303         {header: "Ticker", width: 60, sortable: true, locked: true},
5304         {header: "Company Name", width: 150, sortable: true},
5305         {header: "Market Cap.", width: 100, sortable: true},
5306         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5307         {header: "Employees", width: 100, sortable: true, resizable: false}
5308  ]);
5309  </code></pre>
5310  * <p>
5311  
5312  * The config options listed for this class are options which may appear in each
5313  * individual column definition.
5314  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5315  * @constructor
5316  * @param {Object} config An Array of column config objects. See this class's
5317  * config objects for details.
5318 */
5319 Roo.grid.ColumnModel = function(config){
5320         /**
5321      * The config passed into the constructor
5322      */
5323     this.config = config;
5324     this.lookup = {};
5325
5326     // if no id, create one
5327     // if the column does not have a dataIndex mapping,
5328     // map it to the order it is in the config
5329     for(var i = 0, len = config.length; i < len; i++){
5330         var c = config[i];
5331         if(typeof c.dataIndex == "undefined"){
5332             c.dataIndex = i;
5333         }
5334         if(typeof c.renderer == "string"){
5335             c.renderer = Roo.util.Format[c.renderer];
5336         }
5337         if(typeof c.id == "undefined"){
5338             c.id = Roo.id();
5339         }
5340         if(c.editor && c.editor.xtype){
5341             c.editor  = Roo.factory(c.editor, Roo.grid);
5342         }
5343         if(c.editor && c.editor.isFormField){
5344             c.editor = new Roo.grid.GridEditor(c.editor);
5345         }
5346         this.lookup[c.id] = c;
5347     }
5348
5349     /**
5350      * The width of columns which have no width specified (defaults to 100)
5351      * @type Number
5352      */
5353     this.defaultWidth = 100;
5354
5355     /**
5356      * Default sortable of columns which have no sortable specified (defaults to false)
5357      * @type Boolean
5358      */
5359     this.defaultSortable = false;
5360
5361     this.addEvents({
5362         /**
5363              * @event widthchange
5364              * Fires when the width of a column changes.
5365              * @param {ColumnModel} this
5366              * @param {Number} columnIndex The column index
5367              * @param {Number} newWidth The new width
5368              */
5369             "widthchange": true,
5370         /**
5371              * @event headerchange
5372              * Fires when the text of a header changes.
5373              * @param {ColumnModel} this
5374              * @param {Number} columnIndex The column index
5375              * @param {Number} newText The new header text
5376              */
5377             "headerchange": true,
5378         /**
5379              * @event hiddenchange
5380              * Fires when a column is hidden or "unhidden".
5381              * @param {ColumnModel} this
5382              * @param {Number} columnIndex The column index
5383              * @param {Boolean} hidden true if hidden, false otherwise
5384              */
5385             "hiddenchange": true,
5386             /**
5387          * @event columnmoved
5388          * Fires when a column is moved.
5389          * @param {ColumnModel} this
5390          * @param {Number} oldIndex
5391          * @param {Number} newIndex
5392          */
5393         "columnmoved" : true,
5394         /**
5395          * @event columlockchange
5396          * Fires when a column's locked state is changed
5397          * @param {ColumnModel} this
5398          * @param {Number} colIndex
5399          * @param {Boolean} locked true if locked
5400          */
5401         "columnlockchange" : true
5402     });
5403     Roo.grid.ColumnModel.superclass.constructor.call(this);
5404 };
5405 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5406     /**
5407      * @cfg {String} header The header text to display in the Grid view.
5408      */
5409     /**
5410      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5411      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5412      * specified, the column's index is used as an index into the Record's data Array.
5413      */
5414     /**
5415      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5416      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5417      */
5418     /**
5419      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5420      * Defaults to the value of the {@link #defaultSortable} property.
5421      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5422      */
5423     /**
5424      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5425      */
5426     /**
5427      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5428      */
5429     /**
5430      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5431      */
5432     /**
5433      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5434      */
5435     /**
5436      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5437      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5438      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5439      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5440      */
5441        /**
5442      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5443      */
5444     /**
5445      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5446      */
5447     /**
5448      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5449      */
5450     /**
5451      * @cfg {String} cursor (Optional)
5452      */
5453     /**
5454      * @cfg {String} tooltip (Optional)
5455      */
5456     /**
5457      * @cfg {Number} xs (Optional)
5458      */
5459     /**
5460      * @cfg {Number} sm (Optional)
5461      */
5462     /**
5463      * @cfg {Number} md (Optional)
5464      */
5465     /**
5466      * @cfg {Number} lg (Optional)
5467      */
5468     /**
5469      * Returns the id of the column at the specified index.
5470      * @param {Number} index The column index
5471      * @return {String} the id
5472      */
5473     getColumnId : function(index){
5474         return this.config[index].id;
5475     },
5476
5477     /**
5478      * Returns the column for a specified id.
5479      * @param {String} id The column id
5480      * @return {Object} the column
5481      */
5482     getColumnById : function(id){
5483         return this.lookup[id];
5484     },
5485
5486     
5487     /**
5488      * Returns the column for a specified dataIndex.
5489      * @param {String} dataIndex The column dataIndex
5490      * @return {Object|Boolean} the column or false if not found
5491      */
5492     getColumnByDataIndex: function(dataIndex){
5493         var index = this.findColumnIndex(dataIndex);
5494         return index > -1 ? this.config[index] : false;
5495     },
5496     
5497     /**
5498      * Returns the index for a specified column id.
5499      * @param {String} id The column id
5500      * @return {Number} the index, or -1 if not found
5501      */
5502     getIndexById : function(id){
5503         for(var i = 0, len = this.config.length; i < len; i++){
5504             if(this.config[i].id == id){
5505                 return i;
5506             }
5507         }
5508         return -1;
5509     },
5510     
5511     /**
5512      * Returns the index for a specified column dataIndex.
5513      * @param {String} dataIndex The column dataIndex
5514      * @return {Number} the index, or -1 if not found
5515      */
5516     
5517     findColumnIndex : function(dataIndex){
5518         for(var i = 0, len = this.config.length; i < len; i++){
5519             if(this.config[i].dataIndex == dataIndex){
5520                 return i;
5521             }
5522         }
5523         return -1;
5524     },
5525     
5526     
5527     moveColumn : function(oldIndex, newIndex){
5528         var c = this.config[oldIndex];
5529         this.config.splice(oldIndex, 1);
5530         this.config.splice(newIndex, 0, c);
5531         this.dataMap = null;
5532         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5533     },
5534
5535     isLocked : function(colIndex){
5536         return this.config[colIndex].locked === true;
5537     },
5538
5539     setLocked : function(colIndex, value, suppressEvent){
5540         if(this.isLocked(colIndex) == value){
5541             return;
5542         }
5543         this.config[colIndex].locked = value;
5544         if(!suppressEvent){
5545             this.fireEvent("columnlockchange", this, colIndex, value);
5546         }
5547     },
5548
5549     getTotalLockedWidth : function(){
5550         var totalWidth = 0;
5551         for(var i = 0; i < this.config.length; i++){
5552             if(this.isLocked(i) && !this.isHidden(i)){
5553                 this.totalWidth += this.getColumnWidth(i);
5554             }
5555         }
5556         return totalWidth;
5557     },
5558
5559     getLockedCount : function(){
5560         for(var i = 0, len = this.config.length; i < len; i++){
5561             if(!this.isLocked(i)){
5562                 return i;
5563             }
5564         }
5565         
5566         return this.config.length;
5567     },
5568
5569     /**
5570      * Returns the number of columns.
5571      * @return {Number}
5572      */
5573     getColumnCount : function(visibleOnly){
5574         if(visibleOnly === true){
5575             var c = 0;
5576             for(var i = 0, len = this.config.length; i < len; i++){
5577                 if(!this.isHidden(i)){
5578                     c++;
5579                 }
5580             }
5581             return c;
5582         }
5583         return this.config.length;
5584     },
5585
5586     /**
5587      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5588      * @param {Function} fn
5589      * @param {Object} scope (optional)
5590      * @return {Array} result
5591      */
5592     getColumnsBy : function(fn, scope){
5593         var r = [];
5594         for(var i = 0, len = this.config.length; i < len; i++){
5595             var c = this.config[i];
5596             if(fn.call(scope||this, c, i) === true){
5597                 r[r.length] = c;
5598             }
5599         }
5600         return r;
5601     },
5602
5603     /**
5604      * Returns true if the specified column is sortable.
5605      * @param {Number} col The column index
5606      * @return {Boolean}
5607      */
5608     isSortable : function(col){
5609         if(typeof this.config[col].sortable == "undefined"){
5610             return this.defaultSortable;
5611         }
5612         return this.config[col].sortable;
5613     },
5614
5615     /**
5616      * Returns the rendering (formatting) function defined for the column.
5617      * @param {Number} col The column index.
5618      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5619      */
5620     getRenderer : function(col){
5621         if(!this.config[col].renderer){
5622             return Roo.grid.ColumnModel.defaultRenderer;
5623         }
5624         return this.config[col].renderer;
5625     },
5626
5627     /**
5628      * Sets the rendering (formatting) function for a column.
5629      * @param {Number} col The column index
5630      * @param {Function} fn The function to use to process the cell's raw data
5631      * to return HTML markup for the grid view. The render function is called with
5632      * the following parameters:<ul>
5633      * <li>Data value.</li>
5634      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5635      * <li>css A CSS style string to apply to the table cell.</li>
5636      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5637      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5638      * <li>Row index</li>
5639      * <li>Column index</li>
5640      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5641      */
5642     setRenderer : function(col, fn){
5643         this.config[col].renderer = fn;
5644     },
5645
5646     /**
5647      * Returns the width for the specified column.
5648      * @param {Number} col The column index
5649      * @return {Number}
5650      */
5651     getColumnWidth : function(col){
5652         return this.config[col].width * 1 || this.defaultWidth;
5653     },
5654
5655     /**
5656      * Sets the width for a column.
5657      * @param {Number} col The column index
5658      * @param {Number} width The new width
5659      */
5660     setColumnWidth : function(col, width, suppressEvent){
5661         this.config[col].width = width;
5662         this.totalWidth = null;
5663         if(!suppressEvent){
5664              this.fireEvent("widthchange", this, col, width);
5665         }
5666     },
5667
5668     /**
5669      * Returns the total width of all columns.
5670      * @param {Boolean} includeHidden True to include hidden column widths
5671      * @return {Number}
5672      */
5673     getTotalWidth : function(includeHidden){
5674         if(!this.totalWidth){
5675             this.totalWidth = 0;
5676             for(var i = 0, len = this.config.length; i < len; i++){
5677                 if(includeHidden || !this.isHidden(i)){
5678                     this.totalWidth += this.getColumnWidth(i);
5679                 }
5680             }
5681         }
5682         return this.totalWidth;
5683     },
5684
5685     /**
5686      * Returns the header for the specified column.
5687      * @param {Number} col The column index
5688      * @return {String}
5689      */
5690     getColumnHeader : function(col){
5691         return this.config[col].header;
5692     },
5693
5694     /**
5695      * Sets the header for a column.
5696      * @param {Number} col The column index
5697      * @param {String} header The new header
5698      */
5699     setColumnHeader : function(col, header){
5700         this.config[col].header = header;
5701         this.fireEvent("headerchange", this, col, header);
5702     },
5703
5704     /**
5705      * Returns the tooltip for the specified column.
5706      * @param {Number} col The column index
5707      * @return {String}
5708      */
5709     getColumnTooltip : function(col){
5710             return this.config[col].tooltip;
5711     },
5712     /**
5713      * Sets the tooltip for a column.
5714      * @param {Number} col The column index
5715      * @param {String} tooltip The new tooltip
5716      */
5717     setColumnTooltip : function(col, tooltip){
5718             this.config[col].tooltip = tooltip;
5719     },
5720
5721     /**
5722      * Returns the dataIndex for the specified column.
5723      * @param {Number} col The column index
5724      * @return {Number}
5725      */
5726     getDataIndex : function(col){
5727         return this.config[col].dataIndex;
5728     },
5729
5730     /**
5731      * Sets the dataIndex for a column.
5732      * @param {Number} col The column index
5733      * @param {Number} dataIndex The new dataIndex
5734      */
5735     setDataIndex : function(col, dataIndex){
5736         this.config[col].dataIndex = dataIndex;
5737     },
5738
5739     
5740     
5741     /**
5742      * Returns true if the cell is editable.
5743      * @param {Number} colIndex The column index
5744      * @param {Number} rowIndex The row index - this is nto actually used..?
5745      * @return {Boolean}
5746      */
5747     isCellEditable : function(colIndex, rowIndex){
5748         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5749     },
5750
5751     /**
5752      * Returns the editor defined for the cell/column.
5753      * return false or null to disable editing.
5754      * @param {Number} colIndex The column index
5755      * @param {Number} rowIndex The row index
5756      * @return {Object}
5757      */
5758     getCellEditor : function(colIndex, rowIndex){
5759         return this.config[colIndex].editor;
5760     },
5761
5762     /**
5763      * Sets if a column is editable.
5764      * @param {Number} col The column index
5765      * @param {Boolean} editable True if the column is editable
5766      */
5767     setEditable : function(col, editable){
5768         this.config[col].editable = editable;
5769     },
5770
5771
5772     /**
5773      * Returns true if the column is hidden.
5774      * @param {Number} colIndex The column index
5775      * @return {Boolean}
5776      */
5777     isHidden : function(colIndex){
5778         return this.config[colIndex].hidden;
5779     },
5780
5781
5782     /**
5783      * Returns true if the column width cannot be changed
5784      */
5785     isFixed : function(colIndex){
5786         return this.config[colIndex].fixed;
5787     },
5788
5789     /**
5790      * Returns true if the column can be resized
5791      * @return {Boolean}
5792      */
5793     isResizable : function(colIndex){
5794         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5795     },
5796     /**
5797      * Sets if a column is hidden.
5798      * @param {Number} colIndex The column index
5799      * @param {Boolean} hidden True if the column is hidden
5800      */
5801     setHidden : function(colIndex, hidden){
5802         this.config[colIndex].hidden = hidden;
5803         this.totalWidth = null;
5804         this.fireEvent("hiddenchange", this, colIndex, hidden);
5805     },
5806
5807     /**
5808      * Sets the editor for a column.
5809      * @param {Number} col The column index
5810      * @param {Object} editor The editor object
5811      */
5812     setEditor : function(col, editor){
5813         this.config[col].editor = editor;
5814     }
5815 });
5816
5817 Roo.grid.ColumnModel.defaultRenderer = function(value)
5818 {
5819     if(typeof value == "object") {
5820         return value;
5821     }
5822         if(typeof value == "string" && value.length < 1){
5823             return "&#160;";
5824         }
5825     
5826         return String.format("{0}", value);
5827 };
5828
5829 // Alias for backwards compatibility
5830 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5831 /*
5832  * Based on:
5833  * Ext JS Library 1.1.1
5834  * Copyright(c) 2006-2007, Ext JS, LLC.
5835  *
5836  * Originally Released Under LGPL - original licence link has changed is not relivant.
5837  *
5838  * Fork - LGPL
5839  * <script type="text/javascript">
5840  */
5841  
5842 /**
5843  * @class Roo.LoadMask
5844  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5845  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5846  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5847  * element's UpdateManager load indicator and will be destroyed after the initial load.
5848  * @constructor
5849  * Create a new LoadMask
5850  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5851  * @param {Object} config The config object
5852  */
5853 Roo.LoadMask = function(el, config){
5854     this.el = Roo.get(el);
5855     Roo.apply(this, config);
5856     if(this.store){
5857         this.store.on('beforeload', this.onBeforeLoad, this);
5858         this.store.on('load', this.onLoad, this);
5859         this.store.on('loadexception', this.onLoadException, this);
5860         this.removeMask = false;
5861     }else{
5862         var um = this.el.getUpdateManager();
5863         um.showLoadIndicator = false; // disable the default indicator
5864         um.on('beforeupdate', this.onBeforeLoad, this);
5865         um.on('update', this.onLoad, this);
5866         um.on('failure', this.onLoad, this);
5867         this.removeMask = true;
5868     }
5869 };
5870
5871 Roo.LoadMask.prototype = {
5872     /**
5873      * @cfg {Boolean} removeMask
5874      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5875      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5876      */
5877     /**
5878      * @cfg {String} msg
5879      * The text to display in a centered loading message box (defaults to 'Loading...')
5880      */
5881     msg : 'Loading...',
5882     /**
5883      * @cfg {String} msgCls
5884      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5885      */
5886     msgCls : 'x-mask-loading',
5887
5888     /**
5889      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5890      * @type Boolean
5891      */
5892     disabled: false,
5893
5894     /**
5895      * Disables the mask to prevent it from being displayed
5896      */
5897     disable : function(){
5898        this.disabled = true;
5899     },
5900
5901     /**
5902      * Enables the mask so that it can be displayed
5903      */
5904     enable : function(){
5905         this.disabled = false;
5906     },
5907     
5908     onLoadException : function()
5909     {
5910         Roo.log(arguments);
5911         
5912         if (typeof(arguments[3]) != 'undefined') {
5913             Roo.MessageBox.alert("Error loading",arguments[3]);
5914         } 
5915         /*
5916         try {
5917             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5918                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5919             }   
5920         } catch(e) {
5921             
5922         }
5923         */
5924     
5925         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5926     },
5927     // private
5928     onLoad : function()
5929     {
5930         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5931     },
5932
5933     // private
5934     onBeforeLoad : function(){
5935         if(!this.disabled){
5936             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5937         }
5938     },
5939
5940     // private
5941     destroy : function(){
5942         if(this.store){
5943             this.store.un('beforeload', this.onBeforeLoad, this);
5944             this.store.un('load', this.onLoad, this);
5945             this.store.un('loadexception', this.onLoadException, this);
5946         }else{
5947             var um = this.el.getUpdateManager();
5948             um.un('beforeupdate', this.onBeforeLoad, this);
5949             um.un('update', this.onLoad, this);
5950             um.un('failure', this.onLoad, this);
5951         }
5952     }
5953 };/*
5954  * - LGPL
5955  *
5956  * table
5957  * 
5958  */
5959
5960 /**
5961  * @class Roo.bootstrap.Table
5962  * @extends Roo.bootstrap.Component
5963  * Bootstrap Table class
5964  * @cfg {String} cls table class
5965  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5966  * @cfg {String} bgcolor Specifies the background color for a table
5967  * @cfg {Number} border Specifies whether the table cells should have borders or not
5968  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5969  * @cfg {Number} cellspacing Specifies the space between cells
5970  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5971  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5972  * @cfg {String} sortable Specifies that the table should be sortable
5973  * @cfg {String} summary Specifies a summary of the content of a table
5974  * @cfg {Number} width Specifies the width of a table
5975  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5976  * 
5977  * @cfg {boolean} striped Should the rows be alternative striped
5978  * @cfg {boolean} bordered Add borders to the table
5979  * @cfg {boolean} hover Add hover highlighting
5980  * @cfg {boolean} condensed Format condensed
5981  * @cfg {boolean} responsive Format condensed
5982  * @cfg {Boolean} loadMask (true|false) default false
5983  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5984  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5985  * @cfg {Boolean} rowSelection (true|false) default false
5986  * @cfg {Boolean} cellSelection (true|false) default false
5987  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5988  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5989  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5990  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5991  
5992  * 
5993  * @constructor
5994  * Create a new Table
5995  * @param {Object} config The config object
5996  */
5997
5998 Roo.bootstrap.Table = function(config){
5999     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6000     
6001   
6002     
6003     // BC...
6004     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6005     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6006     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6007     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6008     
6009     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6010     if (this.sm) {
6011         this.sm.grid = this;
6012         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6013         this.sm = this.selModel;
6014         this.sm.xmodule = this.xmodule || false;
6015     }
6016     
6017     if (this.cm && typeof(this.cm.config) == 'undefined') {
6018         this.colModel = new Roo.grid.ColumnModel(this.cm);
6019         this.cm = this.colModel;
6020         this.cm.xmodule = this.xmodule || false;
6021     }
6022     if (this.store) {
6023         this.store= Roo.factory(this.store, Roo.data);
6024         this.ds = this.store;
6025         this.ds.xmodule = this.xmodule || false;
6026          
6027     }
6028     if (this.footer && this.store) {
6029         this.footer.dataSource = this.ds;
6030         this.footer = Roo.factory(this.footer);
6031     }
6032     
6033     /** @private */
6034     this.addEvents({
6035         /**
6036          * @event cellclick
6037          * Fires when a cell is clicked
6038          * @param {Roo.bootstrap.Table} this
6039          * @param {Roo.Element} el
6040          * @param {Number} rowIndex
6041          * @param {Number} columnIndex
6042          * @param {Roo.EventObject} e
6043          */
6044         "cellclick" : true,
6045         /**
6046          * @event celldblclick
6047          * Fires when a cell is double clicked
6048          * @param {Roo.bootstrap.Table} this
6049          * @param {Roo.Element} el
6050          * @param {Number} rowIndex
6051          * @param {Number} columnIndex
6052          * @param {Roo.EventObject} e
6053          */
6054         "celldblclick" : true,
6055         /**
6056          * @event rowclick
6057          * Fires when a row is clicked
6058          * @param {Roo.bootstrap.Table} this
6059          * @param {Roo.Element} el
6060          * @param {Number} rowIndex
6061          * @param {Roo.EventObject} e
6062          */
6063         "rowclick" : true,
6064         /**
6065          * @event rowdblclick
6066          * Fires when a row is double clicked
6067          * @param {Roo.bootstrap.Table} this
6068          * @param {Roo.Element} el
6069          * @param {Number} rowIndex
6070          * @param {Roo.EventObject} e
6071          */
6072         "rowdblclick" : true,
6073         /**
6074          * @event mouseover
6075          * Fires when a mouseover occur
6076          * @param {Roo.bootstrap.Table} this
6077          * @param {Roo.Element} el
6078          * @param {Number} rowIndex
6079          * @param {Number} columnIndex
6080          * @param {Roo.EventObject} e
6081          */
6082         "mouseover" : true,
6083         /**
6084          * @event mouseout
6085          * Fires when a mouseout occur
6086          * @param {Roo.bootstrap.Table} this
6087          * @param {Roo.Element} el
6088          * @param {Number} rowIndex
6089          * @param {Number} columnIndex
6090          * @param {Roo.EventObject} e
6091          */
6092         "mouseout" : true,
6093         /**
6094          * @event rowclass
6095          * Fires when a row is rendered, so you can change add a style to it.
6096          * @param {Roo.bootstrap.Table} this
6097          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6098          */
6099         'rowclass' : true,
6100           /**
6101          * @event rowsrendered
6102          * Fires when all the  rows have been rendered
6103          * @param {Roo.bootstrap.Table} this
6104          */
6105         'rowsrendered' : true,
6106         /**
6107          * @event contextmenu
6108          * The raw contextmenu event for the entire grid.
6109          * @param {Roo.EventObject} e
6110          */
6111         "contextmenu" : true,
6112         /**
6113          * @event rowcontextmenu
6114          * Fires when a row is right clicked
6115          * @param {Roo.bootstrap.Table} this
6116          * @param {Number} rowIndex
6117          * @param {Roo.EventObject} e
6118          */
6119         "rowcontextmenu" : true,
6120         /**
6121          * @event cellcontextmenu
6122          * Fires when a cell is right clicked
6123          * @param {Roo.bootstrap.Table} this
6124          * @param {Number} rowIndex
6125          * @param {Number} cellIndex
6126          * @param {Roo.EventObject} e
6127          */
6128          "cellcontextmenu" : true,
6129          /**
6130          * @event headercontextmenu
6131          * Fires when a header is right clicked
6132          * @param {Roo.bootstrap.Table} this
6133          * @param {Number} columnIndex
6134          * @param {Roo.EventObject} e
6135          */
6136         "headercontextmenu" : true
6137     });
6138 };
6139
6140 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6141     
6142     cls: false,
6143     align: false,
6144     bgcolor: false,
6145     border: false,
6146     cellpadding: false,
6147     cellspacing: false,
6148     frame: false,
6149     rules: false,
6150     sortable: false,
6151     summary: false,
6152     width: false,
6153     striped : false,
6154     scrollBody : false,
6155     bordered: false,
6156     hover:  false,
6157     condensed : false,
6158     responsive : false,
6159     sm : false,
6160     cm : false,
6161     store : false,
6162     loadMask : false,
6163     footerShow : true,
6164     headerShow : true,
6165   
6166     rowSelection : false,
6167     cellSelection : false,
6168     layout : false,
6169     
6170     // Roo.Element - the tbody
6171     mainBody: false,
6172     // Roo.Element - thead element
6173     mainHead: false,
6174     
6175     container: false, // used by gridpanel...
6176     
6177     lazyLoad : false,
6178     
6179     CSS : Roo.util.CSS,
6180     
6181     auto_hide_footer : false,
6182     
6183     getAutoCreate : function()
6184     {
6185         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6186         
6187         cfg = {
6188             tag: 'table',
6189             cls : 'table',
6190             cn : []
6191         };
6192         if (this.scrollBody) {
6193             cfg.cls += ' table-body-fixed';
6194         }    
6195         if (this.striped) {
6196             cfg.cls += ' table-striped';
6197         }
6198         
6199         if (this.hover) {
6200             cfg.cls += ' table-hover';
6201         }
6202         if (this.bordered) {
6203             cfg.cls += ' table-bordered';
6204         }
6205         if (this.condensed) {
6206             cfg.cls += ' table-condensed';
6207         }
6208         if (this.responsive) {
6209             cfg.cls += ' table-responsive';
6210         }
6211         
6212         if (this.cls) {
6213             cfg.cls+=  ' ' +this.cls;
6214         }
6215         
6216         // this lot should be simplifed...
6217         var _t = this;
6218         var cp = [
6219             'align',
6220             'bgcolor',
6221             'border',
6222             'cellpadding',
6223             'cellspacing',
6224             'frame',
6225             'rules',
6226             'sortable',
6227             'summary',
6228             'width'
6229         ].forEach(function(k) {
6230             if (_t[k]) {
6231                 cfg[k] = _t[k];
6232             }
6233         });
6234         
6235         
6236         if (this.layout) {
6237             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6238         }
6239         
6240         if(this.store || this.cm){
6241             if(this.headerShow){
6242                 cfg.cn.push(this.renderHeader());
6243             }
6244             
6245             cfg.cn.push(this.renderBody());
6246             
6247             if(this.footerShow){
6248                 cfg.cn.push(this.renderFooter());
6249             }
6250             // where does this come from?
6251             //cfg.cls+=  ' TableGrid';
6252         }
6253         
6254         return { cn : [ cfg ] };
6255     },
6256     
6257     initEvents : function()
6258     {   
6259         if(!this.store || !this.cm){
6260             return;
6261         }
6262         if (this.selModel) {
6263             this.selModel.initEvents();
6264         }
6265         
6266         
6267         //Roo.log('initEvents with ds!!!!');
6268         
6269         this.mainBody = this.el.select('tbody', true).first();
6270         this.mainHead = this.el.select('thead', true).first();
6271         this.mainFoot = this.el.select('tfoot', true).first();
6272         
6273         
6274         
6275         var _this = this;
6276         
6277         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6278             e.on('click', _this.sort, _this);
6279         });
6280         
6281         this.mainBody.on("click", this.onClick, this);
6282         this.mainBody.on("dblclick", this.onDblClick, this);
6283         
6284         // why is this done????? = it breaks dialogs??
6285         //this.parent().el.setStyle('position', 'relative');
6286         
6287         
6288         if (this.footer) {
6289             this.footer.parentId = this.id;
6290             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6291             
6292             if(this.lazyLoad){
6293                 this.el.select('tfoot tr td').first().addClass('hide');
6294             }
6295         } 
6296         
6297         if(this.loadMask) {
6298             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6299         }
6300         
6301         this.store.on('load', this.onLoad, this);
6302         this.store.on('beforeload', this.onBeforeLoad, this);
6303         this.store.on('update', this.onUpdate, this);
6304         this.store.on('add', this.onAdd, this);
6305         this.store.on("clear", this.clear, this);
6306         
6307         this.el.on("contextmenu", this.onContextMenu, this);
6308         
6309         this.mainBody.on('scroll', this.onBodyScroll, this);
6310         
6311         this.cm.on("headerchange", this.onHeaderChange, this);
6312         
6313         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6314         
6315     },
6316     
6317     onContextMenu : function(e, t)
6318     {
6319         this.processEvent("contextmenu", e);
6320     },
6321     
6322     processEvent : function(name, e)
6323     {
6324         if (name != 'touchstart' ) {
6325             this.fireEvent(name, e);    
6326         }
6327         
6328         var t = e.getTarget();
6329         
6330         var cell = Roo.get(t);
6331         
6332         if(!cell){
6333             return;
6334         }
6335         
6336         if(cell.findParent('tfoot', false, true)){
6337             return;
6338         }
6339         
6340         if(cell.findParent('thead', false, true)){
6341             
6342             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6343                 cell = Roo.get(t).findParent('th', false, true);
6344                 if (!cell) {
6345                     Roo.log("failed to find th in thead?");
6346                     Roo.log(e.getTarget());
6347                     return;
6348                 }
6349             }
6350             
6351             var cellIndex = cell.dom.cellIndex;
6352             
6353             var ename = name == 'touchstart' ? 'click' : name;
6354             this.fireEvent("header" + ename, this, cellIndex, e);
6355             
6356             return;
6357         }
6358         
6359         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6360             cell = Roo.get(t).findParent('td', false, true);
6361             if (!cell) {
6362                 Roo.log("failed to find th in tbody?");
6363                 Roo.log(e.getTarget());
6364                 return;
6365             }
6366         }
6367         
6368         var row = cell.findParent('tr', false, true);
6369         var cellIndex = cell.dom.cellIndex;
6370         var rowIndex = row.dom.rowIndex - 1;
6371         
6372         if(row !== false){
6373             
6374             this.fireEvent("row" + name, this, rowIndex, e);
6375             
6376             if(cell !== false){
6377             
6378                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6379             }
6380         }
6381         
6382     },
6383     
6384     onMouseover : function(e, el)
6385     {
6386         var cell = Roo.get(el);
6387         
6388         if(!cell){
6389             return;
6390         }
6391         
6392         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6393             cell = cell.findParent('td', false, true);
6394         }
6395         
6396         var row = cell.findParent('tr', false, true);
6397         var cellIndex = cell.dom.cellIndex;
6398         var rowIndex = row.dom.rowIndex - 1; // start from 0
6399         
6400         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6401         
6402     },
6403     
6404     onMouseout : function(e, el)
6405     {
6406         var cell = Roo.get(el);
6407         
6408         if(!cell){
6409             return;
6410         }
6411         
6412         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6413             cell = cell.findParent('td', false, true);
6414         }
6415         
6416         var row = cell.findParent('tr', false, true);
6417         var cellIndex = cell.dom.cellIndex;
6418         var rowIndex = row.dom.rowIndex - 1; // start from 0
6419         
6420         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6421         
6422     },
6423     
6424     onClick : function(e, el)
6425     {
6426         var cell = Roo.get(el);
6427         
6428         if(!cell || (!this.cellSelection && !this.rowSelection)){
6429             return;
6430         }
6431         
6432         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6433             cell = cell.findParent('td', false, true);
6434         }
6435         
6436         if(!cell || typeof(cell) == 'undefined'){
6437             return;
6438         }
6439         
6440         var row = cell.findParent('tr', false, true);
6441         
6442         if(!row || typeof(row) == 'undefined'){
6443             return;
6444         }
6445         
6446         var cellIndex = cell.dom.cellIndex;
6447         var rowIndex = this.getRowIndex(row);
6448         
6449         // why??? - should these not be based on SelectionModel?
6450         if(this.cellSelection){
6451             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6452         }
6453         
6454         if(this.rowSelection){
6455             this.fireEvent('rowclick', this, row, rowIndex, e);
6456         }
6457         
6458         
6459     },
6460         
6461     onDblClick : function(e,el)
6462     {
6463         var cell = Roo.get(el);
6464         
6465         if(!cell || (!this.cellSelection && !this.rowSelection)){
6466             return;
6467         }
6468         
6469         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6470             cell = cell.findParent('td', false, true);
6471         }
6472         
6473         if(!cell || typeof(cell) == 'undefined'){
6474             return;
6475         }
6476         
6477         var row = cell.findParent('tr', false, true);
6478         
6479         if(!row || typeof(row) == 'undefined'){
6480             return;
6481         }
6482         
6483         var cellIndex = cell.dom.cellIndex;
6484         var rowIndex = this.getRowIndex(row);
6485         
6486         if(this.cellSelection){
6487             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6488         }
6489         
6490         if(this.rowSelection){
6491             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6492         }
6493     },
6494     
6495     sort : function(e,el)
6496     {
6497         var col = Roo.get(el);
6498         
6499         if(!col.hasClass('sortable')){
6500             return;
6501         }
6502         
6503         var sort = col.attr('sort');
6504         var dir = 'ASC';
6505         
6506         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6507             dir = 'DESC';
6508         }
6509         
6510         this.store.sortInfo = {field : sort, direction : dir};
6511         
6512         if (this.footer) {
6513             Roo.log("calling footer first");
6514             this.footer.onClick('first');
6515         } else {
6516         
6517             this.store.load({ params : { start : 0 } });
6518         }
6519     },
6520     
6521     renderHeader : function()
6522     {
6523         var header = {
6524             tag: 'thead',
6525             cn : []
6526         };
6527         
6528         var cm = this.cm;
6529         this.totalWidth = 0;
6530         
6531         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6532             
6533             var config = cm.config[i];
6534             
6535             var c = {
6536                 tag: 'th',
6537                 cls : 'x-hcol-' + i,
6538                 style : '',
6539                 html: cm.getColumnHeader(i)
6540             };
6541             
6542             var hh = '';
6543             
6544             if(typeof(config.sortable) != 'undefined' && config.sortable){
6545                 c.cls = 'sortable';
6546                 c.html = '<i class="glyphicon"></i>' + c.html;
6547             }
6548             
6549             if(typeof(config.lgHeader) != 'undefined'){
6550                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6551             }
6552             
6553             if(typeof(config.mdHeader) != 'undefined'){
6554                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6555             }
6556             
6557             if(typeof(config.smHeader) != 'undefined'){
6558                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6559             }
6560             
6561             if(typeof(config.xsHeader) != 'undefined'){
6562                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6563             }
6564             
6565             if(hh.length){
6566                 c.html = hh;
6567             }
6568             
6569             if(typeof(config.tooltip) != 'undefined'){
6570                 c.tooltip = config.tooltip;
6571             }
6572             
6573             if(typeof(config.colspan) != 'undefined'){
6574                 c.colspan = config.colspan;
6575             }
6576             
6577             if(typeof(config.hidden) != 'undefined' && config.hidden){
6578                 c.style += ' display:none;';
6579             }
6580             
6581             if(typeof(config.dataIndex) != 'undefined'){
6582                 c.sort = config.dataIndex;
6583             }
6584             
6585            
6586             
6587             if(typeof(config.align) != 'undefined' && config.align.length){
6588                 c.style += ' text-align:' + config.align + ';';
6589             }
6590             
6591             if(typeof(config.width) != 'undefined'){
6592                 c.style += ' width:' + config.width + 'px;';
6593                 this.totalWidth += config.width;
6594             } else {
6595                 this.totalWidth += 100; // assume minimum of 100 per column?
6596             }
6597             
6598             if(typeof(config.cls) != 'undefined'){
6599                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6600             }
6601             
6602             ['xs','sm','md','lg'].map(function(size){
6603                 
6604                 if(typeof(config[size]) == 'undefined'){
6605                     return;
6606                 }
6607                 
6608                 if (!config[size]) { // 0 = hidden
6609                     c.cls += ' hidden-' + size;
6610                     return;
6611                 }
6612                 
6613                 c.cls += ' col-' + size + '-' + config[size];
6614
6615             });
6616             
6617             header.cn.push(c)
6618         }
6619         
6620         return header;
6621     },
6622     
6623     renderBody : function()
6624     {
6625         var body = {
6626             tag: 'tbody',
6627             cn : [
6628                 {
6629                     tag: 'tr',
6630                     cn : [
6631                         {
6632                             tag : 'td',
6633                             colspan :  this.cm.getColumnCount()
6634                         }
6635                     ]
6636                 }
6637             ]
6638         };
6639         
6640         return body;
6641     },
6642     
6643     renderFooter : function()
6644     {
6645         var footer = {
6646             tag: 'tfoot',
6647             cn : [
6648                 {
6649                     tag: 'tr',
6650                     cn : [
6651                         {
6652                             tag : 'td',
6653                             colspan :  this.cm.getColumnCount()
6654                         }
6655                     ]
6656                 }
6657             ]
6658         };
6659         
6660         return footer;
6661     },
6662     
6663     
6664     
6665     onLoad : function()
6666     {
6667 //        Roo.log('ds onload');
6668         this.clear();
6669         
6670         var _this = this;
6671         var cm = this.cm;
6672         var ds = this.store;
6673         
6674         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6675             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6676             if (_this.store.sortInfo) {
6677                     
6678                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6679                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6680                 }
6681                 
6682                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6683                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6684                 }
6685             }
6686         });
6687         
6688         var tbody =  this.mainBody;
6689               
6690         if(ds.getCount() > 0){
6691             ds.data.each(function(d,rowIndex){
6692                 var row =  this.renderRow(cm, ds, rowIndex);
6693                 
6694                 tbody.createChild(row);
6695                 
6696                 var _this = this;
6697                 
6698                 if(row.cellObjects.length){
6699                     Roo.each(row.cellObjects, function(r){
6700                         _this.renderCellObject(r);
6701                     })
6702                 }
6703                 
6704             }, this);
6705         }
6706         
6707         var tfoot = this.el.select('tfoot', true).first();
6708         
6709         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6710             
6711             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6712             
6713             var total = this.ds.getTotalCount();
6714             
6715             if(this.footer.pageSize < total){
6716                 this.mainFoot.show();
6717             }
6718         }
6719         
6720         Roo.each(this.el.select('tbody td', true).elements, function(e){
6721             e.on('mouseover', _this.onMouseover, _this);
6722         });
6723         
6724         Roo.each(this.el.select('tbody td', true).elements, function(e){
6725             e.on('mouseout', _this.onMouseout, _this);
6726         });
6727         this.fireEvent('rowsrendered', this);
6728         
6729         this.autoSize();
6730     },
6731     
6732     
6733     onUpdate : function(ds,record)
6734     {
6735         this.refreshRow(record);
6736         this.autoSize();
6737     },
6738     
6739     onRemove : function(ds, record, index, isUpdate){
6740         if(isUpdate !== true){
6741             this.fireEvent("beforerowremoved", this, index, record);
6742         }
6743         var bt = this.mainBody.dom;
6744         
6745         var rows = this.el.select('tbody > tr', true).elements;
6746         
6747         if(typeof(rows[index]) != 'undefined'){
6748             bt.removeChild(rows[index].dom);
6749         }
6750         
6751 //        if(bt.rows[index]){
6752 //            bt.removeChild(bt.rows[index]);
6753 //        }
6754         
6755         if(isUpdate !== true){
6756             //this.stripeRows(index);
6757             //this.syncRowHeights(index, index);
6758             //this.layout();
6759             this.fireEvent("rowremoved", this, index, record);
6760         }
6761     },
6762     
6763     onAdd : function(ds, records, rowIndex)
6764     {
6765         //Roo.log('on Add called');
6766         // - note this does not handle multiple adding very well..
6767         var bt = this.mainBody.dom;
6768         for (var i =0 ; i < records.length;i++) {
6769             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6770             //Roo.log(records[i]);
6771             //Roo.log(this.store.getAt(rowIndex+i));
6772             this.insertRow(this.store, rowIndex + i, false);
6773             return;
6774         }
6775         
6776     },
6777     
6778     
6779     refreshRow : function(record){
6780         var ds = this.store, index;
6781         if(typeof record == 'number'){
6782             index = record;
6783             record = ds.getAt(index);
6784         }else{
6785             index = ds.indexOf(record);
6786         }
6787         this.insertRow(ds, index, true);
6788         this.autoSize();
6789         this.onRemove(ds, record, index+1, true);
6790         this.autoSize();
6791         //this.syncRowHeights(index, index);
6792         //this.layout();
6793         this.fireEvent("rowupdated", this, index, record);
6794     },
6795     
6796     insertRow : function(dm, rowIndex, isUpdate){
6797         
6798         if(!isUpdate){
6799             this.fireEvent("beforerowsinserted", this, rowIndex);
6800         }
6801             //var s = this.getScrollState();
6802         var row = this.renderRow(this.cm, this.store, rowIndex);
6803         // insert before rowIndex..
6804         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6805         
6806         var _this = this;
6807                 
6808         if(row.cellObjects.length){
6809             Roo.each(row.cellObjects, function(r){
6810                 _this.renderCellObject(r);
6811             })
6812         }
6813             
6814         if(!isUpdate){
6815             this.fireEvent("rowsinserted", this, rowIndex);
6816             //this.syncRowHeights(firstRow, lastRow);
6817             //this.stripeRows(firstRow);
6818             //this.layout();
6819         }
6820         
6821     },
6822     
6823     
6824     getRowDom : function(rowIndex)
6825     {
6826         var rows = this.el.select('tbody > tr', true).elements;
6827         
6828         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6829         
6830     },
6831     // returns the object tree for a tr..
6832   
6833     
6834     renderRow : function(cm, ds, rowIndex) 
6835     {
6836         var d = ds.getAt(rowIndex);
6837         
6838         var row = {
6839             tag : 'tr',
6840             cls : 'x-row-' + rowIndex,
6841             cn : []
6842         };
6843             
6844         var cellObjects = [];
6845         
6846         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6847             var config = cm.config[i];
6848             
6849             var renderer = cm.getRenderer(i);
6850             var value = '';
6851             var id = false;
6852             
6853             if(typeof(renderer) !== 'undefined'){
6854                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6855             }
6856             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6857             // and are rendered into the cells after the row is rendered - using the id for the element.
6858             
6859             if(typeof(value) === 'object'){
6860                 id = Roo.id();
6861                 cellObjects.push({
6862                     container : id,
6863                     cfg : value 
6864                 })
6865             }
6866             
6867             var rowcfg = {
6868                 record: d,
6869                 rowIndex : rowIndex,
6870                 colIndex : i,
6871                 rowClass : ''
6872             };
6873
6874             this.fireEvent('rowclass', this, rowcfg);
6875             
6876             var td = {
6877                 tag: 'td',
6878                 cls : rowcfg.rowClass + ' x-col-' + i,
6879                 style: '',
6880                 html: (typeof(value) === 'object') ? '' : value
6881             };
6882             
6883             if (id) {
6884                 td.id = id;
6885             }
6886             
6887             if(typeof(config.colspan) != 'undefined'){
6888                 td.colspan = config.colspan;
6889             }
6890             
6891             if(typeof(config.hidden) != 'undefined' && config.hidden){
6892                 td.style += ' display:none;';
6893             }
6894             
6895             if(typeof(config.align) != 'undefined' && config.align.length){
6896                 td.style += ' text-align:' + config.align + ';';
6897             }
6898             if(typeof(config.valign) != 'undefined' && config.valign.length){
6899                 td.style += ' vertical-align:' + config.valign + ';';
6900             }
6901             
6902             if(typeof(config.width) != 'undefined'){
6903                 td.style += ' width:' +  config.width + 'px;';
6904             }
6905             
6906             if(typeof(config.cursor) != 'undefined'){
6907                 td.style += ' cursor:' +  config.cursor + ';';
6908             }
6909             
6910             if(typeof(config.cls) != 'undefined'){
6911                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6912             }
6913             
6914             ['xs','sm','md','lg'].map(function(size){
6915                 
6916                 if(typeof(config[size]) == 'undefined'){
6917                     return;
6918                 }
6919                 
6920                 if (!config[size]) { // 0 = hidden
6921                     td.cls += ' hidden-' + size;
6922                     return;
6923                 }
6924                 
6925                 td.cls += ' col-' + size + '-' + config[size];
6926
6927             });
6928             
6929             row.cn.push(td);
6930            
6931         }
6932         
6933         row.cellObjects = cellObjects;
6934         
6935         return row;
6936           
6937     },
6938     
6939     
6940     
6941     onBeforeLoad : function()
6942     {
6943         
6944     },
6945      /**
6946      * Remove all rows
6947      */
6948     clear : function()
6949     {
6950         this.el.select('tbody', true).first().dom.innerHTML = '';
6951     },
6952     /**
6953      * Show or hide a row.
6954      * @param {Number} rowIndex to show or hide
6955      * @param {Boolean} state hide
6956      */
6957     setRowVisibility : function(rowIndex, state)
6958     {
6959         var bt = this.mainBody.dom;
6960         
6961         var rows = this.el.select('tbody > tr', true).elements;
6962         
6963         if(typeof(rows[rowIndex]) == 'undefined'){
6964             return;
6965         }
6966         rows[rowIndex].dom.style.display = state ? '' : 'none';
6967     },
6968     
6969     
6970     getSelectionModel : function(){
6971         if(!this.selModel){
6972             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6973         }
6974         return this.selModel;
6975     },
6976     /*
6977      * Render the Roo.bootstrap object from renderder
6978      */
6979     renderCellObject : function(r)
6980     {
6981         var _this = this;
6982         
6983         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6984         
6985         var t = r.cfg.render(r.container);
6986         
6987         if(r.cfg.cn){
6988             Roo.each(r.cfg.cn, function(c){
6989                 var child = {
6990                     container: t.getChildContainer(),
6991                     cfg: c
6992                 };
6993                 _this.renderCellObject(child);
6994             })
6995         }
6996     },
6997     
6998     getRowIndex : function(row)
6999     {
7000         var rowIndex = -1;
7001         
7002         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7003             if(el != row){
7004                 return;
7005             }
7006             
7007             rowIndex = index;
7008         });
7009         
7010         return rowIndex;
7011     },
7012      /**
7013      * Returns the grid's underlying element = used by panel.Grid
7014      * @return {Element} The element
7015      */
7016     getGridEl : function(){
7017         return this.el;
7018     },
7019      /**
7020      * Forces a resize - used by panel.Grid
7021      * @return {Element} The element
7022      */
7023     autoSize : function()
7024     {
7025         //var ctr = Roo.get(this.container.dom.parentElement);
7026         var ctr = Roo.get(this.el.dom);
7027         
7028         var thd = this.getGridEl().select('thead',true).first();
7029         var tbd = this.getGridEl().select('tbody', true).first();
7030         var tfd = this.getGridEl().select('tfoot', true).first();
7031         
7032         var cw = ctr.getWidth();
7033         
7034         if (tbd) {
7035             
7036             tbd.setSize(ctr.getWidth(),
7037                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7038             );
7039             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7040             cw -= barsize;
7041         }
7042         cw = Math.max(cw, this.totalWidth);
7043         this.getGridEl().select('tr',true).setWidth(cw);
7044         // resize 'expandable coloumn?
7045         
7046         return; // we doe not have a view in this design..
7047         
7048     },
7049     onBodyScroll: function()
7050     {
7051         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7052         if(this.mainHead){
7053             this.mainHead.setStyle({
7054                 'position' : 'relative',
7055                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7056             });
7057         }
7058         
7059         if(this.lazyLoad){
7060             
7061             var scrollHeight = this.mainBody.dom.scrollHeight;
7062             
7063             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7064             
7065             var height = this.mainBody.getHeight();
7066             
7067             if(scrollHeight - height == scrollTop) {
7068                 
7069                 var total = this.ds.getTotalCount();
7070                 
7071                 if(this.footer.cursor + this.footer.pageSize < total){
7072                     
7073                     this.footer.ds.load({
7074                         params : {
7075                             start : this.footer.cursor + this.footer.pageSize,
7076                             limit : this.footer.pageSize
7077                         },
7078                         add : true
7079                     });
7080                 }
7081             }
7082             
7083         }
7084     },
7085     
7086     onHeaderChange : function()
7087     {
7088         var header = this.renderHeader();
7089         var table = this.el.select('table', true).first();
7090         
7091         this.mainHead.remove();
7092         this.mainHead = table.createChild(header, this.mainBody, false);
7093     },
7094     
7095     onHiddenChange : function(colModel, colIndex, hidden)
7096     {
7097         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7098         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7099         
7100         this.CSS.updateRule(thSelector, "display", "");
7101         this.CSS.updateRule(tdSelector, "display", "");
7102         
7103         if(hidden){
7104             this.CSS.updateRule(thSelector, "display", "none");
7105             this.CSS.updateRule(tdSelector, "display", "none");
7106         }
7107         
7108         this.onHeaderChange();
7109         this.onLoad();
7110         
7111     }
7112     
7113 });
7114
7115  
7116
7117  /*
7118  * - LGPL
7119  *
7120  * table cell
7121  * 
7122  */
7123
7124 /**
7125  * @class Roo.bootstrap.TableCell
7126  * @extends Roo.bootstrap.Component
7127  * Bootstrap TableCell class
7128  * @cfg {String} html cell contain text
7129  * @cfg {String} cls cell class
7130  * @cfg {String} tag cell tag (td|th) default td
7131  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7132  * @cfg {String} align Aligns the content in a cell
7133  * @cfg {String} axis Categorizes cells
7134  * @cfg {String} bgcolor Specifies the background color of a cell
7135  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7136  * @cfg {Number} colspan Specifies the number of columns a cell should span
7137  * @cfg {String} headers Specifies one or more header cells a cell is related to
7138  * @cfg {Number} height Sets the height of a cell
7139  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7140  * @cfg {Number} rowspan Sets the number of rows a cell should span
7141  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7142  * @cfg {String} valign Vertical aligns the content in a cell
7143  * @cfg {Number} width Specifies the width of a cell
7144  * 
7145  * @constructor
7146  * Create a new TableCell
7147  * @param {Object} config The config object
7148  */
7149
7150 Roo.bootstrap.TableCell = function(config){
7151     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7152 };
7153
7154 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7155     
7156     html: false,
7157     cls: false,
7158     tag: false,
7159     abbr: false,
7160     align: false,
7161     axis: false,
7162     bgcolor: false,
7163     charoff: false,
7164     colspan: false,
7165     headers: false,
7166     height: false,
7167     nowrap: false,
7168     rowspan: false,
7169     scope: false,
7170     valign: false,
7171     width: false,
7172     
7173     
7174     getAutoCreate : function(){
7175         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7176         
7177         cfg = {
7178             tag: 'td'
7179         };
7180         
7181         if(this.tag){
7182             cfg.tag = this.tag;
7183         }
7184         
7185         if (this.html) {
7186             cfg.html=this.html
7187         }
7188         if (this.cls) {
7189             cfg.cls=this.cls
7190         }
7191         if (this.abbr) {
7192             cfg.abbr=this.abbr
7193         }
7194         if (this.align) {
7195             cfg.align=this.align
7196         }
7197         if (this.axis) {
7198             cfg.axis=this.axis
7199         }
7200         if (this.bgcolor) {
7201             cfg.bgcolor=this.bgcolor
7202         }
7203         if (this.charoff) {
7204             cfg.charoff=this.charoff
7205         }
7206         if (this.colspan) {
7207             cfg.colspan=this.colspan
7208         }
7209         if (this.headers) {
7210             cfg.headers=this.headers
7211         }
7212         if (this.height) {
7213             cfg.height=this.height
7214         }
7215         if (this.nowrap) {
7216             cfg.nowrap=this.nowrap
7217         }
7218         if (this.rowspan) {
7219             cfg.rowspan=this.rowspan
7220         }
7221         if (this.scope) {
7222             cfg.scope=this.scope
7223         }
7224         if (this.valign) {
7225             cfg.valign=this.valign
7226         }
7227         if (this.width) {
7228             cfg.width=this.width
7229         }
7230         
7231         
7232         return cfg;
7233     }
7234    
7235 });
7236
7237  
7238
7239  /*
7240  * - LGPL
7241  *
7242  * table row
7243  * 
7244  */
7245
7246 /**
7247  * @class Roo.bootstrap.TableRow
7248  * @extends Roo.bootstrap.Component
7249  * Bootstrap TableRow class
7250  * @cfg {String} cls row class
7251  * @cfg {String} align Aligns the content in a table row
7252  * @cfg {String} bgcolor Specifies a background color for a table row
7253  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7254  * @cfg {String} valign Vertical aligns the content in a table row
7255  * 
7256  * @constructor
7257  * Create a new TableRow
7258  * @param {Object} config The config object
7259  */
7260
7261 Roo.bootstrap.TableRow = function(config){
7262     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7263 };
7264
7265 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7266     
7267     cls: false,
7268     align: false,
7269     bgcolor: false,
7270     charoff: false,
7271     valign: false,
7272     
7273     getAutoCreate : function(){
7274         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7275         
7276         cfg = {
7277             tag: 'tr'
7278         };
7279             
7280         if(this.cls){
7281             cfg.cls = this.cls;
7282         }
7283         if(this.align){
7284             cfg.align = this.align;
7285         }
7286         if(this.bgcolor){
7287             cfg.bgcolor = this.bgcolor;
7288         }
7289         if(this.charoff){
7290             cfg.charoff = this.charoff;
7291         }
7292         if(this.valign){
7293             cfg.valign = this.valign;
7294         }
7295         
7296         return cfg;
7297     }
7298    
7299 });
7300
7301  
7302
7303  /*
7304  * - LGPL
7305  *
7306  * table body
7307  * 
7308  */
7309
7310 /**
7311  * @class Roo.bootstrap.TableBody
7312  * @extends Roo.bootstrap.Component
7313  * Bootstrap TableBody class
7314  * @cfg {String} cls element class
7315  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7316  * @cfg {String} align Aligns the content inside the element
7317  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7318  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7319  * 
7320  * @constructor
7321  * Create a new TableBody
7322  * @param {Object} config The config object
7323  */
7324
7325 Roo.bootstrap.TableBody = function(config){
7326     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7327 };
7328
7329 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7330     
7331     cls: false,
7332     tag: false,
7333     align: false,
7334     charoff: false,
7335     valign: false,
7336     
7337     getAutoCreate : function(){
7338         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7339         
7340         cfg = {
7341             tag: 'tbody'
7342         };
7343             
7344         if (this.cls) {
7345             cfg.cls=this.cls
7346         }
7347         if(this.tag){
7348             cfg.tag = this.tag;
7349         }
7350         
7351         if(this.align){
7352             cfg.align = this.align;
7353         }
7354         if(this.charoff){
7355             cfg.charoff = this.charoff;
7356         }
7357         if(this.valign){
7358             cfg.valign = this.valign;
7359         }
7360         
7361         return cfg;
7362     }
7363     
7364     
7365 //    initEvents : function()
7366 //    {
7367 //        
7368 //        if(!this.store){
7369 //            return;
7370 //        }
7371 //        
7372 //        this.store = Roo.factory(this.store, Roo.data);
7373 //        this.store.on('load', this.onLoad, this);
7374 //        
7375 //        this.store.load();
7376 //        
7377 //    },
7378 //    
7379 //    onLoad: function () 
7380 //    {   
7381 //        this.fireEvent('load', this);
7382 //    }
7383 //    
7384 //   
7385 });
7386
7387  
7388
7389  /*
7390  * Based on:
7391  * Ext JS Library 1.1.1
7392  * Copyright(c) 2006-2007, Ext JS, LLC.
7393  *
7394  * Originally Released Under LGPL - original licence link has changed is not relivant.
7395  *
7396  * Fork - LGPL
7397  * <script type="text/javascript">
7398  */
7399
7400 // as we use this in bootstrap.
7401 Roo.namespace('Roo.form');
7402  /**
7403  * @class Roo.form.Action
7404  * Internal Class used to handle form actions
7405  * @constructor
7406  * @param {Roo.form.BasicForm} el The form element or its id
7407  * @param {Object} config Configuration options
7408  */
7409
7410  
7411  
7412 // define the action interface
7413 Roo.form.Action = function(form, options){
7414     this.form = form;
7415     this.options = options || {};
7416 };
7417 /**
7418  * Client Validation Failed
7419  * @const 
7420  */
7421 Roo.form.Action.CLIENT_INVALID = 'client';
7422 /**
7423  * Server Validation Failed
7424  * @const 
7425  */
7426 Roo.form.Action.SERVER_INVALID = 'server';
7427  /**
7428  * Connect to Server Failed
7429  * @const 
7430  */
7431 Roo.form.Action.CONNECT_FAILURE = 'connect';
7432 /**
7433  * Reading Data from Server Failed
7434  * @const 
7435  */
7436 Roo.form.Action.LOAD_FAILURE = 'load';
7437
7438 Roo.form.Action.prototype = {
7439     type : 'default',
7440     failureType : undefined,
7441     response : undefined,
7442     result : undefined,
7443
7444     // interface method
7445     run : function(options){
7446
7447     },
7448
7449     // interface method
7450     success : function(response){
7451
7452     },
7453
7454     // interface method
7455     handleResponse : function(response){
7456
7457     },
7458
7459     // default connection failure
7460     failure : function(response){
7461         
7462         this.response = response;
7463         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7464         this.form.afterAction(this, false);
7465     },
7466
7467     processResponse : function(response){
7468         this.response = response;
7469         if(!response.responseText){
7470             return true;
7471         }
7472         this.result = this.handleResponse(response);
7473         return this.result;
7474     },
7475
7476     // utility functions used internally
7477     getUrl : function(appendParams){
7478         var url = this.options.url || this.form.url || this.form.el.dom.action;
7479         if(appendParams){
7480             var p = this.getParams();
7481             if(p){
7482                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7483             }
7484         }
7485         return url;
7486     },
7487
7488     getMethod : function(){
7489         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7490     },
7491
7492     getParams : function(){
7493         var bp = this.form.baseParams;
7494         var p = this.options.params;
7495         if(p){
7496             if(typeof p == "object"){
7497                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7498             }else if(typeof p == 'string' && bp){
7499                 p += '&' + Roo.urlEncode(bp);
7500             }
7501         }else if(bp){
7502             p = Roo.urlEncode(bp);
7503         }
7504         return p;
7505     },
7506
7507     createCallback : function(){
7508         return {
7509             success: this.success,
7510             failure: this.failure,
7511             scope: this,
7512             timeout: (this.form.timeout*1000),
7513             upload: this.form.fileUpload ? this.success : undefined
7514         };
7515     }
7516 };
7517
7518 Roo.form.Action.Submit = function(form, options){
7519     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7520 };
7521
7522 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7523     type : 'submit',
7524
7525     haveProgress : false,
7526     uploadComplete : false,
7527     
7528     // uploadProgress indicator.
7529     uploadProgress : function()
7530     {
7531         if (!this.form.progressUrl) {
7532             return;
7533         }
7534         
7535         if (!this.haveProgress) {
7536             Roo.MessageBox.progress("Uploading", "Uploading");
7537         }
7538         if (this.uploadComplete) {
7539            Roo.MessageBox.hide();
7540            return;
7541         }
7542         
7543         this.haveProgress = true;
7544    
7545         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7546         
7547         var c = new Roo.data.Connection();
7548         c.request({
7549             url : this.form.progressUrl,
7550             params: {
7551                 id : uid
7552             },
7553             method: 'GET',
7554             success : function(req){
7555                //console.log(data);
7556                 var rdata = false;
7557                 var edata;
7558                 try  {
7559                    rdata = Roo.decode(req.responseText)
7560                 } catch (e) {
7561                     Roo.log("Invalid data from server..");
7562                     Roo.log(edata);
7563                     return;
7564                 }
7565                 if (!rdata || !rdata.success) {
7566                     Roo.log(rdata);
7567                     Roo.MessageBox.alert(Roo.encode(rdata));
7568                     return;
7569                 }
7570                 var data = rdata.data;
7571                 
7572                 if (this.uploadComplete) {
7573                    Roo.MessageBox.hide();
7574                    return;
7575                 }
7576                    
7577                 if (data){
7578                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7579                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7580                     );
7581                 }
7582                 this.uploadProgress.defer(2000,this);
7583             },
7584        
7585             failure: function(data) {
7586                 Roo.log('progress url failed ');
7587                 Roo.log(data);
7588             },
7589             scope : this
7590         });
7591            
7592     },
7593     
7594     
7595     run : function()
7596     {
7597         // run get Values on the form, so it syncs any secondary forms.
7598         this.form.getValues();
7599         
7600         var o = this.options;
7601         var method = this.getMethod();
7602         var isPost = method == 'POST';
7603         if(o.clientValidation === false || this.form.isValid()){
7604             
7605             if (this.form.progressUrl) {
7606                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7607                     (new Date() * 1) + '' + Math.random());
7608                     
7609             } 
7610             
7611             
7612             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7613                 form:this.form.el.dom,
7614                 url:this.getUrl(!isPost),
7615                 method: method,
7616                 params:isPost ? this.getParams() : null,
7617                 isUpload: this.form.fileUpload
7618             }));
7619             
7620             this.uploadProgress();
7621
7622         }else if (o.clientValidation !== false){ // client validation failed
7623             this.failureType = Roo.form.Action.CLIENT_INVALID;
7624             this.form.afterAction(this, false);
7625         }
7626     },
7627
7628     success : function(response)
7629     {
7630         this.uploadComplete= true;
7631         if (this.haveProgress) {
7632             Roo.MessageBox.hide();
7633         }
7634         
7635         
7636         var result = this.processResponse(response);
7637         if(result === true || result.success){
7638             this.form.afterAction(this, true);
7639             return;
7640         }
7641         if(result.errors){
7642             this.form.markInvalid(result.errors);
7643             this.failureType = Roo.form.Action.SERVER_INVALID;
7644         }
7645         this.form.afterAction(this, false);
7646     },
7647     failure : function(response)
7648     {
7649         this.uploadComplete= true;
7650         if (this.haveProgress) {
7651             Roo.MessageBox.hide();
7652         }
7653         
7654         this.response = response;
7655         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7656         this.form.afterAction(this, false);
7657     },
7658     
7659     handleResponse : function(response){
7660         if(this.form.errorReader){
7661             var rs = this.form.errorReader.read(response);
7662             var errors = [];
7663             if(rs.records){
7664                 for(var i = 0, len = rs.records.length; i < len; i++) {
7665                     var r = rs.records[i];
7666                     errors[i] = r.data;
7667                 }
7668             }
7669             if(errors.length < 1){
7670                 errors = null;
7671             }
7672             return {
7673                 success : rs.success,
7674                 errors : errors
7675             };
7676         }
7677         var ret = false;
7678         try {
7679             ret = Roo.decode(response.responseText);
7680         } catch (e) {
7681             ret = {
7682                 success: false,
7683                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7684                 errors : []
7685             };
7686         }
7687         return ret;
7688         
7689     }
7690 });
7691
7692
7693 Roo.form.Action.Load = function(form, options){
7694     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7695     this.reader = this.form.reader;
7696 };
7697
7698 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7699     type : 'load',
7700
7701     run : function(){
7702         
7703         Roo.Ajax.request(Roo.apply(
7704                 this.createCallback(), {
7705                     method:this.getMethod(),
7706                     url:this.getUrl(false),
7707                     params:this.getParams()
7708         }));
7709     },
7710
7711     success : function(response){
7712         
7713         var result = this.processResponse(response);
7714         if(result === true || !result.success || !result.data){
7715             this.failureType = Roo.form.Action.LOAD_FAILURE;
7716             this.form.afterAction(this, false);
7717             return;
7718         }
7719         this.form.clearInvalid();
7720         this.form.setValues(result.data);
7721         this.form.afterAction(this, true);
7722     },
7723
7724     handleResponse : function(response){
7725         if(this.form.reader){
7726             var rs = this.form.reader.read(response);
7727             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7728             return {
7729                 success : rs.success,
7730                 data : data
7731             };
7732         }
7733         return Roo.decode(response.responseText);
7734     }
7735 });
7736
7737 Roo.form.Action.ACTION_TYPES = {
7738     'load' : Roo.form.Action.Load,
7739     'submit' : Roo.form.Action.Submit
7740 };/*
7741  * - LGPL
7742  *
7743  * form
7744  *
7745  */
7746
7747 /**
7748  * @class Roo.bootstrap.Form
7749  * @extends Roo.bootstrap.Component
7750  * Bootstrap Form class
7751  * @cfg {String} method  GET | POST (default POST)
7752  * @cfg {String} labelAlign top | left (default top)
7753  * @cfg {String} align left  | right - for navbars
7754  * @cfg {Boolean} loadMask load mask when submit (default true)
7755
7756  *
7757  * @constructor
7758  * Create a new Form
7759  * @param {Object} config The config object
7760  */
7761
7762
7763 Roo.bootstrap.Form = function(config){
7764     
7765     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7766     
7767     Roo.bootstrap.Form.popover.apply();
7768     
7769     this.addEvents({
7770         /**
7771          * @event clientvalidation
7772          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7773          * @param {Form} this
7774          * @param {Boolean} valid true if the form has passed client-side validation
7775          */
7776         clientvalidation: true,
7777         /**
7778          * @event beforeaction
7779          * Fires before any action is performed. Return false to cancel the action.
7780          * @param {Form} this
7781          * @param {Action} action The action to be performed
7782          */
7783         beforeaction: true,
7784         /**
7785          * @event actionfailed
7786          * Fires when an action fails.
7787          * @param {Form} this
7788          * @param {Action} action The action that failed
7789          */
7790         actionfailed : true,
7791         /**
7792          * @event actioncomplete
7793          * Fires when an action is completed.
7794          * @param {Form} this
7795          * @param {Action} action The action that completed
7796          */
7797         actioncomplete : true
7798     });
7799 };
7800
7801 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7802
7803      /**
7804      * @cfg {String} method
7805      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7806      */
7807     method : 'POST',
7808     /**
7809      * @cfg {String} url
7810      * The URL to use for form actions if one isn't supplied in the action options.
7811      */
7812     /**
7813      * @cfg {Boolean} fileUpload
7814      * Set to true if this form is a file upload.
7815      */
7816
7817     /**
7818      * @cfg {Object} baseParams
7819      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7820      */
7821
7822     /**
7823      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7824      */
7825     timeout: 30,
7826     /**
7827      * @cfg {Sting} align (left|right) for navbar forms
7828      */
7829     align : 'left',
7830
7831     // private
7832     activeAction : null,
7833
7834     /**
7835      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7836      * element by passing it or its id or mask the form itself by passing in true.
7837      * @type Mixed
7838      */
7839     waitMsgTarget : false,
7840
7841     loadMask : true,
7842     
7843     /**
7844      * @cfg {Boolean} errorMask (true|false) default false
7845      */
7846     errorMask : false,
7847     
7848     /**
7849      * @cfg {Number} maskOffset Default 100
7850      */
7851     maskOffset : 100,
7852     
7853     /**
7854      * @cfg {Boolean} maskBody
7855      */
7856     maskBody : false,
7857
7858     getAutoCreate : function(){
7859
7860         var cfg = {
7861             tag: 'form',
7862             method : this.method || 'POST',
7863             id : this.id || Roo.id(),
7864             cls : ''
7865         };
7866         if (this.parent().xtype.match(/^Nav/)) {
7867             cfg.cls = 'navbar-form navbar-' + this.align;
7868
7869         }
7870
7871         if (this.labelAlign == 'left' ) {
7872             cfg.cls += ' form-horizontal';
7873         }
7874
7875
7876         return cfg;
7877     },
7878     initEvents : function()
7879     {
7880         this.el.on('submit', this.onSubmit, this);
7881         // this was added as random key presses on the form where triggering form submit.
7882         this.el.on('keypress', function(e) {
7883             if (e.getCharCode() != 13) {
7884                 return true;
7885             }
7886             // we might need to allow it for textareas.. and some other items.
7887             // check e.getTarget().
7888
7889             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7890                 return true;
7891             }
7892
7893             Roo.log("keypress blocked");
7894
7895             e.preventDefault();
7896             return false;
7897         });
7898         
7899     },
7900     // private
7901     onSubmit : function(e){
7902         e.stopEvent();
7903     },
7904
7905      /**
7906      * Returns true if client-side validation on the form is successful.
7907      * @return Boolean
7908      */
7909     isValid : function(){
7910         var items = this.getItems();
7911         var valid = true;
7912         var target = false;
7913         
7914         items.each(function(f){
7915             
7916             if(f.validate()){
7917                 return;
7918             }
7919             
7920             Roo.log('invalid field: ' + f.name);
7921             
7922             valid = false;
7923
7924             if(!target && f.el.isVisible(true)){
7925                 target = f;
7926             }
7927            
7928         });
7929         
7930         if(this.errorMask && !valid){
7931             Roo.bootstrap.Form.popover.mask(this, target);
7932         }
7933         
7934         return valid;
7935     },
7936     
7937     /**
7938      * Returns true if any fields in this form have changed since their original load.
7939      * @return Boolean
7940      */
7941     isDirty : function(){
7942         var dirty = false;
7943         var items = this.getItems();
7944         items.each(function(f){
7945            if(f.isDirty()){
7946                dirty = true;
7947                return false;
7948            }
7949            return true;
7950         });
7951         return dirty;
7952     },
7953      /**
7954      * Performs a predefined action (submit or load) or custom actions you define on this form.
7955      * @param {String} actionName The name of the action type
7956      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7957      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7958      * accept other config options):
7959      * <pre>
7960 Property          Type             Description
7961 ----------------  ---------------  ----------------------------------------------------------------------------------
7962 url               String           The url for the action (defaults to the form's url)
7963 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7964 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7965 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7966                                    validate the form on the client (defaults to false)
7967      * </pre>
7968      * @return {BasicForm} this
7969      */
7970     doAction : function(action, options){
7971         if(typeof action == 'string'){
7972             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7973         }
7974         if(this.fireEvent('beforeaction', this, action) !== false){
7975             this.beforeAction(action);
7976             action.run.defer(100, action);
7977         }
7978         return this;
7979     },
7980
7981     // private
7982     beforeAction : function(action){
7983         var o = action.options;
7984         
7985         if(this.loadMask){
7986             
7987             if(this.maskBody){
7988                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7989             } else {
7990                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7991             }
7992         }
7993         // not really supported yet.. ??
7994
7995         //if(this.waitMsgTarget === true){
7996         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7997         //}else if(this.waitMsgTarget){
7998         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7999         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8000         //}else {
8001         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8002        // }
8003
8004     },
8005
8006     // private
8007     afterAction : function(action, success){
8008         this.activeAction = null;
8009         var o = action.options;
8010
8011         if(this.loadMask){
8012             
8013             if(this.maskBody){
8014                 Roo.get(document.body).unmask();
8015             } else {
8016                 this.el.unmask();
8017             }
8018         }
8019         
8020         //if(this.waitMsgTarget === true){
8021 //            this.el.unmask();
8022         //}else if(this.waitMsgTarget){
8023         //    this.waitMsgTarget.unmask();
8024         //}else{
8025         //    Roo.MessageBox.updateProgress(1);
8026         //    Roo.MessageBox.hide();
8027        // }
8028         //
8029         if(success){
8030             if(o.reset){
8031                 this.reset();
8032             }
8033             Roo.callback(o.success, o.scope, [this, action]);
8034             this.fireEvent('actioncomplete', this, action);
8035
8036         }else{
8037
8038             // failure condition..
8039             // we have a scenario where updates need confirming.
8040             // eg. if a locking scenario exists..
8041             // we look for { errors : { needs_confirm : true }} in the response.
8042             if (
8043                 (typeof(action.result) != 'undefined')  &&
8044                 (typeof(action.result.errors) != 'undefined')  &&
8045                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8046            ){
8047                 var _t = this;
8048                 Roo.log("not supported yet");
8049                  /*
8050
8051                 Roo.MessageBox.confirm(
8052                     "Change requires confirmation",
8053                     action.result.errorMsg,
8054                     function(r) {
8055                         if (r != 'yes') {
8056                             return;
8057                         }
8058                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8059                     }
8060
8061                 );
8062                 */
8063
8064
8065                 return;
8066             }
8067
8068             Roo.callback(o.failure, o.scope, [this, action]);
8069             // show an error message if no failed handler is set..
8070             if (!this.hasListener('actionfailed')) {
8071                 Roo.log("need to add dialog support");
8072                 /*
8073                 Roo.MessageBox.alert("Error",
8074                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8075                         action.result.errorMsg :
8076                         "Saving Failed, please check your entries or try again"
8077                 );
8078                 */
8079             }
8080
8081             this.fireEvent('actionfailed', this, action);
8082         }
8083
8084     },
8085     /**
8086      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8087      * @param {String} id The value to search for
8088      * @return Field
8089      */
8090     findField : function(id){
8091         var items = this.getItems();
8092         var field = items.get(id);
8093         if(!field){
8094              items.each(function(f){
8095                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8096                     field = f;
8097                     return false;
8098                 }
8099                 return true;
8100             });
8101         }
8102         return field || null;
8103     },
8104      /**
8105      * Mark fields in this form invalid in bulk.
8106      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8107      * @return {BasicForm} this
8108      */
8109     markInvalid : function(errors){
8110         if(errors instanceof Array){
8111             for(var i = 0, len = errors.length; i < len; i++){
8112                 var fieldError = errors[i];
8113                 var f = this.findField(fieldError.id);
8114                 if(f){
8115                     f.markInvalid(fieldError.msg);
8116                 }
8117             }
8118         }else{
8119             var field, id;
8120             for(id in errors){
8121                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8122                     field.markInvalid(errors[id]);
8123                 }
8124             }
8125         }
8126         //Roo.each(this.childForms || [], function (f) {
8127         //    f.markInvalid(errors);
8128         //});
8129
8130         return this;
8131     },
8132
8133     /**
8134      * Set values for fields in this form in bulk.
8135      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8136      * @return {BasicForm} this
8137      */
8138     setValues : function(values){
8139         if(values instanceof Array){ // array of objects
8140             for(var i = 0, len = values.length; i < len; i++){
8141                 var v = values[i];
8142                 var f = this.findField(v.id);
8143                 if(f){
8144                     f.setValue(v.value);
8145                     if(this.trackResetOnLoad){
8146                         f.originalValue = f.getValue();
8147                     }
8148                 }
8149             }
8150         }else{ // object hash
8151             var field, id;
8152             for(id in values){
8153                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8154
8155                     if (field.setFromData &&
8156                         field.valueField &&
8157                         field.displayField &&
8158                         // combos' with local stores can
8159                         // be queried via setValue()
8160                         // to set their value..
8161                         (field.store && !field.store.isLocal)
8162                         ) {
8163                         // it's a combo
8164                         var sd = { };
8165                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8166                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8167                         field.setFromData(sd);
8168
8169                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8170                         
8171                         field.setFromData(values);
8172                         
8173                     } else {
8174                         field.setValue(values[id]);
8175                     }
8176
8177
8178                     if(this.trackResetOnLoad){
8179                         field.originalValue = field.getValue();
8180                     }
8181                 }
8182             }
8183         }
8184
8185         //Roo.each(this.childForms || [], function (f) {
8186         //    f.setValues(values);
8187         //});
8188
8189         return this;
8190     },
8191
8192     /**
8193      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8194      * they are returned as an array.
8195      * @param {Boolean} asString
8196      * @return {Object}
8197      */
8198     getValues : function(asString){
8199         //if (this.childForms) {
8200             // copy values from the child forms
8201         //    Roo.each(this.childForms, function (f) {
8202         //        this.setValues(f.getValues());
8203         //    }, this);
8204         //}
8205
8206
8207
8208         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8209         if(asString === true){
8210             return fs;
8211         }
8212         return Roo.urlDecode(fs);
8213     },
8214
8215     /**
8216      * Returns the fields in this form as an object with key/value pairs.
8217      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8218      * @return {Object}
8219      */
8220     getFieldValues : function(with_hidden)
8221     {
8222         var items = this.getItems();
8223         var ret = {};
8224         items.each(function(f){
8225             
8226             if (!f.getName()) {
8227                 return;
8228             }
8229             
8230             var v = f.getValue();
8231             
8232             if (f.inputType =='radio') {
8233                 if (typeof(ret[f.getName()]) == 'undefined') {
8234                     ret[f.getName()] = ''; // empty..
8235                 }
8236
8237                 if (!f.el.dom.checked) {
8238                     return;
8239
8240                 }
8241                 v = f.el.dom.value;
8242
8243             }
8244             
8245             if(f.xtype == 'MoneyField'){
8246                 ret[f.currencyName] = f.getCurrency();
8247             }
8248
8249             // not sure if this supported any more..
8250             if ((typeof(v) == 'object') && f.getRawValue) {
8251                 v = f.getRawValue() ; // dates..
8252             }
8253             // combo boxes where name != hiddenName...
8254             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8255                 ret[f.name] = f.getRawValue();
8256             }
8257             ret[f.getName()] = v;
8258         });
8259
8260         return ret;
8261     },
8262
8263     /**
8264      * Clears all invalid messages in this form.
8265      * @return {BasicForm} this
8266      */
8267     clearInvalid : function(){
8268         var items = this.getItems();
8269
8270         items.each(function(f){
8271            f.clearInvalid();
8272         });
8273
8274         return this;
8275     },
8276
8277     /**
8278      * Resets this form.
8279      * @return {BasicForm} this
8280      */
8281     reset : function(){
8282         var items = this.getItems();
8283         items.each(function(f){
8284             f.reset();
8285         });
8286
8287         Roo.each(this.childForms || [], function (f) {
8288             f.reset();
8289         });
8290
8291
8292         return this;
8293     },
8294     
8295     getItems : function()
8296     {
8297         var r=new Roo.util.MixedCollection(false, function(o){
8298             return o.id || (o.id = Roo.id());
8299         });
8300         var iter = function(el) {
8301             if (el.inputEl) {
8302                 r.add(el);
8303             }
8304             if (!el.items) {
8305                 return;
8306             }
8307             Roo.each(el.items,function(e) {
8308                 iter(e);
8309             });
8310         };
8311
8312         iter(this);
8313         return r;
8314     },
8315     
8316     hideFields : function(items)
8317     {
8318         Roo.each(items, function(i){
8319             
8320             var f = this.findField(i);
8321             
8322             if(!f){
8323                 return;
8324             }
8325             
8326             if(f.xtype == 'DateField'){
8327                 f.setVisible(false);
8328                 return;
8329             }
8330             
8331             f.hide();
8332             
8333         }, this);
8334     },
8335     
8336     showFields : function(items)
8337     {
8338         Roo.each(items, function(i){
8339             
8340             var f = this.findField(i);
8341             
8342             if(!f){
8343                 return;
8344             }
8345             
8346             if(f.xtype == 'DateField'){
8347                 f.setVisible(true);
8348                 return;
8349             }
8350             
8351             f.show();
8352             
8353         }, this);
8354     }
8355
8356 });
8357
8358 Roo.apply(Roo.bootstrap.Form, {
8359     
8360     popover : {
8361         
8362         padding : 5,
8363         
8364         isApplied : false,
8365         
8366         isMasked : false,
8367         
8368         form : false,
8369         
8370         target : false,
8371         
8372         toolTip : false,
8373         
8374         intervalID : false,
8375         
8376         maskEl : false,
8377         
8378         apply : function()
8379         {
8380             if(this.isApplied){
8381                 return;
8382             }
8383             
8384             this.maskEl = {
8385                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8386                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8387                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8388                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8389             };
8390             
8391             this.maskEl.top.enableDisplayMode("block");
8392             this.maskEl.left.enableDisplayMode("block");
8393             this.maskEl.bottom.enableDisplayMode("block");
8394             this.maskEl.right.enableDisplayMode("block");
8395             
8396             this.toolTip = new Roo.bootstrap.Tooltip({
8397                 cls : 'roo-form-error-popover',
8398                 alignment : {
8399                     'left' : ['r-l', [-2,0], 'right'],
8400                     'right' : ['l-r', [2,0], 'left'],
8401                     'bottom' : ['tl-bl', [0,2], 'top'],
8402                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8403                 }
8404             });
8405             
8406             this.toolTip.render(Roo.get(document.body));
8407
8408             this.toolTip.el.enableDisplayMode("block");
8409             
8410             Roo.get(document.body).on('click', function(){
8411                 this.unmask();
8412             }, this);
8413             
8414             Roo.get(document.body).on('touchstart', function(){
8415                 this.unmask();
8416             }, this);
8417             
8418             this.isApplied = true
8419         },
8420         
8421         mask : function(form, target)
8422         {
8423             this.form = form;
8424             
8425             this.target = target;
8426             
8427             if(!this.form.errorMask || !target.el){
8428                 return;
8429             }
8430             
8431             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8432             
8433             Roo.log(scrollable);
8434             
8435             var ot = this.target.el.calcOffsetsTo(scrollable);
8436             
8437             var scrollTo = ot[1] - this.form.maskOffset;
8438             
8439             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8440             
8441             scrollable.scrollTo('top', scrollTo);
8442             
8443             var box = this.target.el.getBox();
8444             Roo.log(box);
8445             var zIndex = Roo.bootstrap.Modal.zIndex++;
8446
8447             
8448             this.maskEl.top.setStyle('position', 'absolute');
8449             this.maskEl.top.setStyle('z-index', zIndex);
8450             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8451             this.maskEl.top.setLeft(0);
8452             this.maskEl.top.setTop(0);
8453             this.maskEl.top.show();
8454             
8455             this.maskEl.left.setStyle('position', 'absolute');
8456             this.maskEl.left.setStyle('z-index', zIndex);
8457             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8458             this.maskEl.left.setLeft(0);
8459             this.maskEl.left.setTop(box.y - this.padding);
8460             this.maskEl.left.show();
8461
8462             this.maskEl.bottom.setStyle('position', 'absolute');
8463             this.maskEl.bottom.setStyle('z-index', zIndex);
8464             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8465             this.maskEl.bottom.setLeft(0);
8466             this.maskEl.bottom.setTop(box.bottom + this.padding);
8467             this.maskEl.bottom.show();
8468
8469             this.maskEl.right.setStyle('position', 'absolute');
8470             this.maskEl.right.setStyle('z-index', zIndex);
8471             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8472             this.maskEl.right.setLeft(box.right + this.padding);
8473             this.maskEl.right.setTop(box.y - this.padding);
8474             this.maskEl.right.show();
8475
8476             this.toolTip.bindEl = this.target.el;
8477
8478             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8479
8480             var tip = this.target.blankText;
8481
8482             if(this.target.getValue() !== '' ) {
8483                 
8484                 if (this.target.invalidText.length) {
8485                     tip = this.target.invalidText;
8486                 } else if (this.target.regexText.length){
8487                     tip = this.target.regexText;
8488                 }
8489             }
8490
8491             this.toolTip.show(tip);
8492
8493             this.intervalID = window.setInterval(function() {
8494                 Roo.bootstrap.Form.popover.unmask();
8495             }, 10000);
8496
8497             window.onwheel = function(){ return false;};
8498             
8499             (function(){ this.isMasked = true; }).defer(500, this);
8500             
8501         },
8502         
8503         unmask : function()
8504         {
8505             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8506                 return;
8507             }
8508             
8509             this.maskEl.top.setStyle('position', 'absolute');
8510             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8511             this.maskEl.top.hide();
8512
8513             this.maskEl.left.setStyle('position', 'absolute');
8514             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8515             this.maskEl.left.hide();
8516
8517             this.maskEl.bottom.setStyle('position', 'absolute');
8518             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8519             this.maskEl.bottom.hide();
8520
8521             this.maskEl.right.setStyle('position', 'absolute');
8522             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8523             this.maskEl.right.hide();
8524             
8525             this.toolTip.hide();
8526             
8527             this.toolTip.el.hide();
8528             
8529             window.onwheel = function(){ return true;};
8530             
8531             if(this.intervalID){
8532                 window.clearInterval(this.intervalID);
8533                 this.intervalID = false;
8534             }
8535             
8536             this.isMasked = false;
8537             
8538         }
8539         
8540     }
8541     
8542 });
8543
8544 /*
8545  * Based on:
8546  * Ext JS Library 1.1.1
8547  * Copyright(c) 2006-2007, Ext JS, LLC.
8548  *
8549  * Originally Released Under LGPL - original licence link has changed is not relivant.
8550  *
8551  * Fork - LGPL
8552  * <script type="text/javascript">
8553  */
8554 /**
8555  * @class Roo.form.VTypes
8556  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8557  * @singleton
8558  */
8559 Roo.form.VTypes = function(){
8560     // closure these in so they are only created once.
8561     var alpha = /^[a-zA-Z_]+$/;
8562     var alphanum = /^[a-zA-Z0-9_]+$/;
8563     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8564     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8565
8566     // All these messages and functions are configurable
8567     return {
8568         /**
8569          * The function used to validate email addresses
8570          * @param {String} value The email address
8571          */
8572         'email' : function(v){
8573             return email.test(v);
8574         },
8575         /**
8576          * The error text to display when the email validation function returns false
8577          * @type String
8578          */
8579         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8580         /**
8581          * The keystroke filter mask to be applied on email input
8582          * @type RegExp
8583          */
8584         'emailMask' : /[a-z0-9_\.\-@]/i,
8585
8586         /**
8587          * The function used to validate URLs
8588          * @param {String} value The URL
8589          */
8590         'url' : function(v){
8591             return url.test(v);
8592         },
8593         /**
8594          * The error text to display when the url validation function returns false
8595          * @type String
8596          */
8597         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8598         
8599         /**
8600          * The function used to validate alpha values
8601          * @param {String} value The value
8602          */
8603         'alpha' : function(v){
8604             return alpha.test(v);
8605         },
8606         /**
8607          * The error text to display when the alpha validation function returns false
8608          * @type String
8609          */
8610         'alphaText' : 'This field should only contain letters and _',
8611         /**
8612          * The keystroke filter mask to be applied on alpha input
8613          * @type RegExp
8614          */
8615         'alphaMask' : /[a-z_]/i,
8616
8617         /**
8618          * The function used to validate alphanumeric values
8619          * @param {String} value The value
8620          */
8621         'alphanum' : function(v){
8622             return alphanum.test(v);
8623         },
8624         /**
8625          * The error text to display when the alphanumeric validation function returns false
8626          * @type String
8627          */
8628         'alphanumText' : 'This field should only contain letters, numbers and _',
8629         /**
8630          * The keystroke filter mask to be applied on alphanumeric input
8631          * @type RegExp
8632          */
8633         'alphanumMask' : /[a-z0-9_]/i
8634     };
8635 }();/*
8636  * - LGPL
8637  *
8638  * Input
8639  * 
8640  */
8641
8642 /**
8643  * @class Roo.bootstrap.Input
8644  * @extends Roo.bootstrap.Component
8645  * Bootstrap Input class
8646  * @cfg {Boolean} disabled is it disabled
8647  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8648  * @cfg {String} name name of the input
8649  * @cfg {string} fieldLabel - the label associated
8650  * @cfg {string} placeholder - placeholder to put in text.
8651  * @cfg {string}  before - input group add on before
8652  * @cfg {string} after - input group add on after
8653  * @cfg {string} size - (lg|sm) or leave empty..
8654  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8655  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8656  * @cfg {Number} md colspan out of 12 for computer-sized screens
8657  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8658  * @cfg {string} value default value of the input
8659  * @cfg {Number} labelWidth set the width of label 
8660  * @cfg {Number} labellg set the width of label (1-12)
8661  * @cfg {Number} labelmd set the width of label (1-12)
8662  * @cfg {Number} labelsm set the width of label (1-12)
8663  * @cfg {Number} labelxs set the width of label (1-12)
8664  * @cfg {String} labelAlign (top|left)
8665  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8666  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8667  * @cfg {String} indicatorpos (left|right) default left
8668  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8669  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8670
8671  * @cfg {String} align (left|center|right) Default left
8672  * @cfg {Boolean} forceFeedback (true|false) Default false
8673  * 
8674  * @constructor
8675  * Create a new Input
8676  * @param {Object} config The config object
8677  */
8678
8679 Roo.bootstrap.Input = function(config){
8680     
8681     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8682     
8683     this.addEvents({
8684         /**
8685          * @event focus
8686          * Fires when this field receives input focus.
8687          * @param {Roo.form.Field} this
8688          */
8689         focus : true,
8690         /**
8691          * @event blur
8692          * Fires when this field loses input focus.
8693          * @param {Roo.form.Field} this
8694          */
8695         blur : true,
8696         /**
8697          * @event specialkey
8698          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8699          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8700          * @param {Roo.form.Field} this
8701          * @param {Roo.EventObject} e The event object
8702          */
8703         specialkey : true,
8704         /**
8705          * @event change
8706          * Fires just before the field blurs if the field value has changed.
8707          * @param {Roo.form.Field} this
8708          * @param {Mixed} newValue The new value
8709          * @param {Mixed} oldValue The original value
8710          */
8711         change : true,
8712         /**
8713          * @event invalid
8714          * Fires after the field has been marked as invalid.
8715          * @param {Roo.form.Field} this
8716          * @param {String} msg The validation message
8717          */
8718         invalid : true,
8719         /**
8720          * @event valid
8721          * Fires after the field has been validated with no errors.
8722          * @param {Roo.form.Field} this
8723          */
8724         valid : true,
8725          /**
8726          * @event keyup
8727          * Fires after the key up
8728          * @param {Roo.form.Field} this
8729          * @param {Roo.EventObject}  e The event Object
8730          */
8731         keyup : true
8732     });
8733 };
8734
8735 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8736      /**
8737      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8738       automatic validation (defaults to "keyup").
8739      */
8740     validationEvent : "keyup",
8741      /**
8742      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8743      */
8744     validateOnBlur : true,
8745     /**
8746      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8747      */
8748     validationDelay : 250,
8749      /**
8750      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8751      */
8752     focusClass : "x-form-focus",  // not needed???
8753     
8754        
8755     /**
8756      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8757      */
8758     invalidClass : "has-warning",
8759     
8760     /**
8761      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8762      */
8763     validClass : "has-success",
8764     
8765     /**
8766      * @cfg {Boolean} hasFeedback (true|false) default true
8767      */
8768     hasFeedback : true,
8769     
8770     /**
8771      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8772      */
8773     invalidFeedbackClass : "glyphicon-warning-sign",
8774     
8775     /**
8776      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8777      */
8778     validFeedbackClass : "glyphicon-ok",
8779     
8780     /**
8781      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8782      */
8783     selectOnFocus : false,
8784     
8785      /**
8786      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8787      */
8788     maskRe : null,
8789        /**
8790      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8791      */
8792     vtype : null,
8793     
8794       /**
8795      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8796      */
8797     disableKeyFilter : false,
8798     
8799        /**
8800      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8801      */
8802     disabled : false,
8803      /**
8804      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8805      */
8806     allowBlank : true,
8807     /**
8808      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8809      */
8810     blankText : "Please complete this mandatory field",
8811     
8812      /**
8813      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8814      */
8815     minLength : 0,
8816     /**
8817      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8818      */
8819     maxLength : Number.MAX_VALUE,
8820     /**
8821      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8822      */
8823     minLengthText : "The minimum length for this field is {0}",
8824     /**
8825      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8826      */
8827     maxLengthText : "The maximum length for this field is {0}",
8828   
8829     
8830     /**
8831      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8832      * If available, this function will be called only after the basic validators all return true, and will be passed the
8833      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8834      */
8835     validator : null,
8836     /**
8837      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8838      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8839      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8840      */
8841     regex : null,
8842     /**
8843      * @cfg {String} regexText -- Depricated - use Invalid Text
8844      */
8845     regexText : "",
8846     
8847     /**
8848      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8849      */
8850     invalidText : "",
8851     
8852     
8853     
8854     autocomplete: false,
8855     
8856     
8857     fieldLabel : '',
8858     inputType : 'text',
8859     
8860     name : false,
8861     placeholder: false,
8862     before : false,
8863     after : false,
8864     size : false,
8865     hasFocus : false,
8866     preventMark: false,
8867     isFormField : true,
8868     value : '',
8869     labelWidth : 2,
8870     labelAlign : false,
8871     readOnly : false,
8872     align : false,
8873     formatedValue : false,
8874     forceFeedback : false,
8875     
8876     indicatorpos : 'left',
8877     
8878     labellg : 0,
8879     labelmd : 0,
8880     labelsm : 0,
8881     labelxs : 0,
8882     
8883     capture : '',
8884     accept : '',
8885     
8886     parentLabelAlign : function()
8887     {
8888         var parent = this;
8889         while (parent.parent()) {
8890             parent = parent.parent();
8891             if (typeof(parent.labelAlign) !='undefined') {
8892                 return parent.labelAlign;
8893             }
8894         }
8895         return 'left';
8896         
8897     },
8898     
8899     getAutoCreate : function()
8900     {
8901         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8902         
8903         var id = Roo.id();
8904         
8905         var cfg = {};
8906         
8907         if(this.inputType != 'hidden'){
8908             cfg.cls = 'form-group' //input-group
8909         }
8910         
8911         var input =  {
8912             tag: 'input',
8913             id : id,
8914             type : this.inputType,
8915             value : this.value,
8916             cls : 'form-control',
8917             placeholder : this.placeholder || '',
8918             autocomplete : this.autocomplete || 'new-password'
8919         };
8920         
8921         if(this.capture.length){
8922             input.capture = this.capture;
8923         }
8924         
8925         if(this.accept.length){
8926             input.accept = this.accept + "/*";
8927         }
8928         
8929         if(this.align){
8930             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8931         }
8932         
8933         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8934             input.maxLength = this.maxLength;
8935         }
8936         
8937         if (this.disabled) {
8938             input.disabled=true;
8939         }
8940         
8941         if (this.readOnly) {
8942             input.readonly=true;
8943         }
8944         
8945         if (this.name) {
8946             input.name = this.name;
8947         }
8948         
8949         if (this.size) {
8950             input.cls += ' input-' + this.size;
8951         }
8952         
8953         var settings=this;
8954         ['xs','sm','md','lg'].map(function(size){
8955             if (settings[size]) {
8956                 cfg.cls += ' col-' + size + '-' + settings[size];
8957             }
8958         });
8959         
8960         var inputblock = input;
8961         
8962         var feedback = {
8963             tag: 'span',
8964             cls: 'glyphicon form-control-feedback'
8965         };
8966             
8967         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8968             
8969             inputblock = {
8970                 cls : 'has-feedback',
8971                 cn :  [
8972                     input,
8973                     feedback
8974                 ] 
8975             };  
8976         }
8977         
8978         if (this.before || this.after) {
8979             
8980             inputblock = {
8981                 cls : 'input-group',
8982                 cn :  [] 
8983             };
8984             
8985             if (this.before && typeof(this.before) == 'string') {
8986                 
8987                 inputblock.cn.push({
8988                     tag :'span',
8989                     cls : 'roo-input-before input-group-addon',
8990                     html : this.before
8991                 });
8992             }
8993             if (this.before && typeof(this.before) == 'object') {
8994                 this.before = Roo.factory(this.before);
8995                 
8996                 inputblock.cn.push({
8997                     tag :'span',
8998                     cls : 'roo-input-before input-group-' +
8999                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9000                 });
9001             }
9002             
9003             inputblock.cn.push(input);
9004             
9005             if (this.after && typeof(this.after) == 'string') {
9006                 inputblock.cn.push({
9007                     tag :'span',
9008                     cls : 'roo-input-after input-group-addon',
9009                     html : this.after
9010                 });
9011             }
9012             if (this.after && typeof(this.after) == 'object') {
9013                 this.after = Roo.factory(this.after);
9014                 
9015                 inputblock.cn.push({
9016                     tag :'span',
9017                     cls : 'roo-input-after input-group-' +
9018                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9019                 });
9020             }
9021             
9022             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9023                 inputblock.cls += ' has-feedback';
9024                 inputblock.cn.push(feedback);
9025             }
9026         };
9027         
9028         if (align ==='left' && this.fieldLabel.length) {
9029             
9030             cfg.cls += ' roo-form-group-label-left';
9031             
9032             cfg.cn = [
9033                 {
9034                     tag : 'i',
9035                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9036                     tooltip : 'This field is required'
9037                 },
9038                 {
9039                     tag: 'label',
9040                     'for' :  id,
9041                     cls : 'control-label',
9042                     html : this.fieldLabel
9043
9044                 },
9045                 {
9046                     cls : "", 
9047                     cn: [
9048                         inputblock
9049                     ]
9050                 }
9051             ];
9052             
9053             var labelCfg = cfg.cn[1];
9054             var contentCfg = cfg.cn[2];
9055             
9056             if(this.indicatorpos == 'right'){
9057                 cfg.cn = [
9058                     {
9059                         tag: 'label',
9060                         'for' :  id,
9061                         cls : 'control-label',
9062                         cn : [
9063                             {
9064                                 tag : 'span',
9065                                 html : this.fieldLabel
9066                             },
9067                             {
9068                                 tag : 'i',
9069                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9070                                 tooltip : 'This field is required'
9071                             }
9072                         ]
9073                     },
9074                     {
9075                         cls : "",
9076                         cn: [
9077                             inputblock
9078                         ]
9079                     }
9080
9081                 ];
9082                 
9083                 labelCfg = cfg.cn[0];
9084                 contentCfg = cfg.cn[1];
9085             
9086             }
9087             
9088             if(this.labelWidth > 12){
9089                 labelCfg.style = "width: " + this.labelWidth + 'px';
9090             }
9091             
9092             if(this.labelWidth < 13 && this.labelmd == 0){
9093                 this.labelmd = this.labelWidth;
9094             }
9095             
9096             if(this.labellg > 0){
9097                 labelCfg.cls += ' col-lg-' + this.labellg;
9098                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9099             }
9100             
9101             if(this.labelmd > 0){
9102                 labelCfg.cls += ' col-md-' + this.labelmd;
9103                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9104             }
9105             
9106             if(this.labelsm > 0){
9107                 labelCfg.cls += ' col-sm-' + this.labelsm;
9108                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9109             }
9110             
9111             if(this.labelxs > 0){
9112                 labelCfg.cls += ' col-xs-' + this.labelxs;
9113                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9114             }
9115             
9116             
9117         } else if ( this.fieldLabel.length) {
9118                 
9119             cfg.cn = [
9120                 {
9121                     tag : 'i',
9122                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9123                     tooltip : 'This field is required'
9124                 },
9125                 {
9126                     tag: 'label',
9127                    //cls : 'input-group-addon',
9128                     html : this.fieldLabel
9129
9130                 },
9131
9132                inputblock
9133
9134            ];
9135            
9136            if(this.indicatorpos == 'right'){
9137                 
9138                 cfg.cn = [
9139                     {
9140                         tag: 'label',
9141                        //cls : 'input-group-addon',
9142                         html : this.fieldLabel
9143
9144                     },
9145                     {
9146                         tag : 'i',
9147                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9148                         tooltip : 'This field is required'
9149                     },
9150
9151                    inputblock
9152
9153                ];
9154
9155             }
9156
9157         } else {
9158             
9159             cfg.cn = [
9160
9161                     inputblock
9162
9163             ];
9164                 
9165                 
9166         };
9167         
9168         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9169            cfg.cls += ' navbar-form';
9170         }
9171         
9172         if (this.parentType === 'NavGroup') {
9173            cfg.cls += ' navbar-form';
9174            cfg.tag = 'li';
9175         }
9176         
9177         return cfg;
9178         
9179     },
9180     /**
9181      * return the real input element.
9182      */
9183     inputEl: function ()
9184     {
9185         return this.el.select('input.form-control',true).first();
9186     },
9187     
9188     tooltipEl : function()
9189     {
9190         return this.inputEl();
9191     },
9192     
9193     indicatorEl : function()
9194     {
9195         var indicator = this.el.select('i.roo-required-indicator',true).first();
9196         
9197         if(!indicator){
9198             return false;
9199         }
9200         
9201         return indicator;
9202         
9203     },
9204     
9205     setDisabled : function(v)
9206     {
9207         var i  = this.inputEl().dom;
9208         if (!v) {
9209             i.removeAttribute('disabled');
9210             return;
9211             
9212         }
9213         i.setAttribute('disabled','true');
9214     },
9215     initEvents : function()
9216     {
9217           
9218         this.inputEl().on("keydown" , this.fireKey,  this);
9219         this.inputEl().on("focus", this.onFocus,  this);
9220         this.inputEl().on("blur", this.onBlur,  this);
9221         
9222         this.inputEl().relayEvent('keyup', this);
9223         
9224         this.indicator = this.indicatorEl();
9225         
9226         if(this.indicator){
9227             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9228         }
9229  
9230         // reference to original value for reset
9231         this.originalValue = this.getValue();
9232         //Roo.form.TextField.superclass.initEvents.call(this);
9233         if(this.validationEvent == 'keyup'){
9234             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9235             this.inputEl().on('keyup', this.filterValidation, this);
9236         }
9237         else if(this.validationEvent !== false){
9238             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9239         }
9240         
9241         if(this.selectOnFocus){
9242             this.on("focus", this.preFocus, this);
9243             
9244         }
9245         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9246             this.inputEl().on("keypress", this.filterKeys, this);
9247         } else {
9248             this.inputEl().relayEvent('keypress', this);
9249         }
9250        /* if(this.grow){
9251             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9252             this.el.on("click", this.autoSize,  this);
9253         }
9254         */
9255         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9256             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9257         }
9258         
9259         if (typeof(this.before) == 'object') {
9260             this.before.render(this.el.select('.roo-input-before',true).first());
9261         }
9262         if (typeof(this.after) == 'object') {
9263             this.after.render(this.el.select('.roo-input-after',true).first());
9264         }
9265         
9266         this.inputEl().on('change', this.onChange, this);
9267         
9268     },
9269     filterValidation : function(e){
9270         if(!e.isNavKeyPress()){
9271             this.validationTask.delay(this.validationDelay);
9272         }
9273     },
9274      /**
9275      * Validates the field value
9276      * @return {Boolean} True if the value is valid, else false
9277      */
9278     validate : function(){
9279         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9280         if(this.disabled || this.validateValue(this.getRawValue())){
9281             this.markValid();
9282             return true;
9283         }
9284         
9285         this.markInvalid();
9286         return false;
9287     },
9288     
9289     
9290     /**
9291      * Validates a value according to the field's validation rules and marks the field as invalid
9292      * if the validation fails
9293      * @param {Mixed} value The value to validate
9294      * @return {Boolean} True if the value is valid, else false
9295      */
9296     validateValue : function(value)
9297     {
9298         if(this.getVisibilityEl().hasClass('hidden')){
9299             return true;
9300         }
9301         
9302         if(value.length < 1)  { // if it's blank
9303             if(this.allowBlank){
9304                 return true;
9305             }
9306             return false;
9307         }
9308         
9309         if(value.length < this.minLength){
9310             return false;
9311         }
9312         if(value.length > this.maxLength){
9313             return false;
9314         }
9315         if(this.vtype){
9316             var vt = Roo.form.VTypes;
9317             if(!vt[this.vtype](value, this)){
9318                 return false;
9319             }
9320         }
9321         if(typeof this.validator == "function"){
9322             var msg = this.validator(value);
9323             if(msg !== true){
9324                 return false;
9325             }
9326             if (typeof(msg) == 'string') {
9327                 this.invalidText = msg;
9328             }
9329         }
9330         
9331         if(this.regex && !this.regex.test(value)){
9332             return false;
9333         }
9334         
9335         return true;
9336     },
9337     
9338      // private
9339     fireKey : function(e){
9340         //Roo.log('field ' + e.getKey());
9341         if(e.isNavKeyPress()){
9342             this.fireEvent("specialkey", this, e);
9343         }
9344     },
9345     focus : function (selectText){
9346         if(this.rendered){
9347             this.inputEl().focus();
9348             if(selectText === true){
9349                 this.inputEl().dom.select();
9350             }
9351         }
9352         return this;
9353     } ,
9354     
9355     onFocus : function(){
9356         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9357            // this.el.addClass(this.focusClass);
9358         }
9359         if(!this.hasFocus){
9360             this.hasFocus = true;
9361             this.startValue = this.getValue();
9362             this.fireEvent("focus", this);
9363         }
9364     },
9365     
9366     beforeBlur : Roo.emptyFn,
9367
9368     
9369     // private
9370     onBlur : function(){
9371         this.beforeBlur();
9372         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9373             //this.el.removeClass(this.focusClass);
9374         }
9375         this.hasFocus = false;
9376         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9377             this.validate();
9378         }
9379         var v = this.getValue();
9380         if(String(v) !== String(this.startValue)){
9381             this.fireEvent('change', this, v, this.startValue);
9382         }
9383         this.fireEvent("blur", this);
9384     },
9385     
9386     onChange : function(e)
9387     {
9388         var v = this.getValue();
9389         if(String(v) !== String(this.startValue)){
9390             this.fireEvent('change', this, v, this.startValue);
9391         }
9392         
9393     },
9394     
9395     /**
9396      * Resets the current field value to the originally loaded value and clears any validation messages
9397      */
9398     reset : function(){
9399         this.setValue(this.originalValue);
9400         this.validate();
9401     },
9402      /**
9403      * Returns the name of the field
9404      * @return {Mixed} name The name field
9405      */
9406     getName: function(){
9407         return this.name;
9408     },
9409      /**
9410      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9411      * @return {Mixed} value The field value
9412      */
9413     getValue : function(){
9414         
9415         var v = this.inputEl().getValue();
9416         
9417         return v;
9418     },
9419     /**
9420      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9421      * @return {Mixed} value The field value
9422      */
9423     getRawValue : function(){
9424         var v = this.inputEl().getValue();
9425         
9426         return v;
9427     },
9428     
9429     /**
9430      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9431      * @param {Mixed} value The value to set
9432      */
9433     setRawValue : function(v){
9434         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9435     },
9436     
9437     selectText : function(start, end){
9438         var v = this.getRawValue();
9439         if(v.length > 0){
9440             start = start === undefined ? 0 : start;
9441             end = end === undefined ? v.length : end;
9442             var d = this.inputEl().dom;
9443             if(d.setSelectionRange){
9444                 d.setSelectionRange(start, end);
9445             }else if(d.createTextRange){
9446                 var range = d.createTextRange();
9447                 range.moveStart("character", start);
9448                 range.moveEnd("character", v.length-end);
9449                 range.select();
9450             }
9451         }
9452     },
9453     
9454     /**
9455      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9456      * @param {Mixed} value The value to set
9457      */
9458     setValue : function(v){
9459         this.value = v;
9460         if(this.rendered){
9461             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9462             this.validate();
9463         }
9464     },
9465     
9466     /*
9467     processValue : function(value){
9468         if(this.stripCharsRe){
9469             var newValue = value.replace(this.stripCharsRe, '');
9470             if(newValue !== value){
9471                 this.setRawValue(newValue);
9472                 return newValue;
9473             }
9474         }
9475         return value;
9476     },
9477   */
9478     preFocus : function(){
9479         
9480         if(this.selectOnFocus){
9481             this.inputEl().dom.select();
9482         }
9483     },
9484     filterKeys : function(e){
9485         var k = e.getKey();
9486         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9487             return;
9488         }
9489         var c = e.getCharCode(), cc = String.fromCharCode(c);
9490         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9491             return;
9492         }
9493         if(!this.maskRe.test(cc)){
9494             e.stopEvent();
9495         }
9496     },
9497      /**
9498      * Clear any invalid styles/messages for this field
9499      */
9500     clearInvalid : function(){
9501         
9502         if(!this.el || this.preventMark){ // not rendered
9503             return;
9504         }
9505         
9506      
9507         this.el.removeClass(this.invalidClass);
9508         
9509         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9510             
9511             var feedback = this.el.select('.form-control-feedback', true).first();
9512             
9513             if(feedback){
9514                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9515             }
9516             
9517         }
9518         
9519         if(this.indicator){
9520             this.indicator.removeClass('visible');
9521             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9522         }
9523         
9524         this.fireEvent('valid', this);
9525     },
9526     
9527      /**
9528      * Mark this field as valid
9529      */
9530     markValid : function()
9531     {
9532         if(!this.el  || this.preventMark){ // not rendered...
9533             return;
9534         }
9535         
9536         this.el.removeClass([this.invalidClass, this.validClass]);
9537         
9538         var feedback = this.el.select('.form-control-feedback', true).first();
9539             
9540         if(feedback){
9541             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9542         }
9543         
9544         if(this.indicator){
9545             this.indicator.removeClass('visible');
9546             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9547         }
9548         
9549         if(this.disabled){
9550             return;
9551         }
9552         
9553         if(this.allowBlank && !this.getRawValue().length){
9554             return;
9555         }
9556         
9557         this.el.addClass(this.validClass);
9558         
9559         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9560             
9561             var feedback = this.el.select('.form-control-feedback', true).first();
9562             
9563             if(feedback){
9564                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9565                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9566             }
9567             
9568         }
9569         
9570         this.fireEvent('valid', this);
9571     },
9572     
9573      /**
9574      * Mark this field as invalid
9575      * @param {String} msg The validation message
9576      */
9577     markInvalid : function(msg)
9578     {
9579         if(!this.el  || this.preventMark){ // not rendered
9580             return;
9581         }
9582         
9583         this.el.removeClass([this.invalidClass, this.validClass]);
9584         
9585         var feedback = this.el.select('.form-control-feedback', true).first();
9586             
9587         if(feedback){
9588             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9589         }
9590
9591         if(this.disabled){
9592             return;
9593         }
9594         
9595         if(this.allowBlank && !this.getRawValue().length){
9596             return;
9597         }
9598         
9599         if(this.indicator){
9600             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9601             this.indicator.addClass('visible');
9602         }
9603         
9604         this.el.addClass(this.invalidClass);
9605         
9606         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9607             
9608             var feedback = this.el.select('.form-control-feedback', true).first();
9609             
9610             if(feedback){
9611                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9612                 
9613                 if(this.getValue().length || this.forceFeedback){
9614                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9615                 }
9616                 
9617             }
9618             
9619         }
9620         
9621         this.fireEvent('invalid', this, msg);
9622     },
9623     // private
9624     SafariOnKeyDown : function(event)
9625     {
9626         // this is a workaround for a password hang bug on chrome/ webkit.
9627         if (this.inputEl().dom.type != 'password') {
9628             return;
9629         }
9630         
9631         var isSelectAll = false;
9632         
9633         if(this.inputEl().dom.selectionEnd > 0){
9634             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9635         }
9636         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9637             event.preventDefault();
9638             this.setValue('');
9639             return;
9640         }
9641         
9642         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9643             
9644             event.preventDefault();
9645             // this is very hacky as keydown always get's upper case.
9646             //
9647             var cc = String.fromCharCode(event.getCharCode());
9648             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9649             
9650         }
9651     },
9652     adjustWidth : function(tag, w){
9653         tag = tag.toLowerCase();
9654         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9655             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9656                 if(tag == 'input'){
9657                     return w + 2;
9658                 }
9659                 if(tag == 'textarea'){
9660                     return w-2;
9661                 }
9662             }else if(Roo.isOpera){
9663                 if(tag == 'input'){
9664                     return w + 2;
9665                 }
9666                 if(tag == 'textarea'){
9667                     return w-2;
9668                 }
9669             }
9670         }
9671         return w;
9672     },
9673     
9674     setFieldLabel : function(v)
9675     {
9676         if(!this.rendered){
9677             return;
9678         }
9679         
9680         if(this.indicator){
9681             var ar = this.el.select('label > span',true);
9682             
9683             if (ar.elements.length) {
9684                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9685                 this.fieldLabel = v;
9686                 return;
9687             }
9688             
9689             var br = this.el.select('label',true);
9690             
9691             if(br.elements.length) {
9692                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9693                 this.fieldLabel = v;
9694                 return;
9695             }
9696             
9697             Roo.log('Cannot Found any of label > span || label in input');
9698             return;
9699         }
9700         
9701         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9702         this.fieldLabel = v;
9703         
9704         
9705     }
9706 });
9707
9708  
9709 /*
9710  * - LGPL
9711  *
9712  * Input
9713  * 
9714  */
9715
9716 /**
9717  * @class Roo.bootstrap.TextArea
9718  * @extends Roo.bootstrap.Input
9719  * Bootstrap TextArea class
9720  * @cfg {Number} cols Specifies the visible width of a text area
9721  * @cfg {Number} rows Specifies the visible number of lines in a text area
9722  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9723  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9724  * @cfg {string} html text
9725  * 
9726  * @constructor
9727  * Create a new TextArea
9728  * @param {Object} config The config object
9729  */
9730
9731 Roo.bootstrap.TextArea = function(config){
9732     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9733    
9734 };
9735
9736 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9737      
9738     cols : false,
9739     rows : 5,
9740     readOnly : false,
9741     warp : 'soft',
9742     resize : false,
9743     value: false,
9744     html: false,
9745     
9746     getAutoCreate : function(){
9747         
9748         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9749         
9750         var id = Roo.id();
9751         
9752         var cfg = {};
9753         
9754         if(this.inputType != 'hidden'){
9755             cfg.cls = 'form-group' //input-group
9756         }
9757         
9758         var input =  {
9759             tag: 'textarea',
9760             id : id,
9761             warp : this.warp,
9762             rows : this.rows,
9763             value : this.value || '',
9764             html: this.html || '',
9765             cls : 'form-control',
9766             placeholder : this.placeholder || '' 
9767             
9768         };
9769         
9770         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9771             input.maxLength = this.maxLength;
9772         }
9773         
9774         if(this.resize){
9775             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9776         }
9777         
9778         if(this.cols){
9779             input.cols = this.cols;
9780         }
9781         
9782         if (this.readOnly) {
9783             input.readonly = true;
9784         }
9785         
9786         if (this.name) {
9787             input.name = this.name;
9788         }
9789         
9790         if (this.size) {
9791             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9792         }
9793         
9794         var settings=this;
9795         ['xs','sm','md','lg'].map(function(size){
9796             if (settings[size]) {
9797                 cfg.cls += ' col-' + size + '-' + settings[size];
9798             }
9799         });
9800         
9801         var inputblock = input;
9802         
9803         if(this.hasFeedback && !this.allowBlank){
9804             
9805             var feedback = {
9806                 tag: 'span',
9807                 cls: 'glyphicon form-control-feedback'
9808             };
9809
9810             inputblock = {
9811                 cls : 'has-feedback',
9812                 cn :  [
9813                     input,
9814                     feedback
9815                 ] 
9816             };  
9817         }
9818         
9819         
9820         if (this.before || this.after) {
9821             
9822             inputblock = {
9823                 cls : 'input-group',
9824                 cn :  [] 
9825             };
9826             if (this.before) {
9827                 inputblock.cn.push({
9828                     tag :'span',
9829                     cls : 'input-group-addon',
9830                     html : this.before
9831                 });
9832             }
9833             
9834             inputblock.cn.push(input);
9835             
9836             if(this.hasFeedback && !this.allowBlank){
9837                 inputblock.cls += ' has-feedback';
9838                 inputblock.cn.push(feedback);
9839             }
9840             
9841             if (this.after) {
9842                 inputblock.cn.push({
9843                     tag :'span',
9844                     cls : 'input-group-addon',
9845                     html : this.after
9846                 });
9847             }
9848             
9849         }
9850         
9851         if (align ==='left' && this.fieldLabel.length) {
9852             cfg.cn = [
9853                 {
9854                     tag: 'label',
9855                     'for' :  id,
9856                     cls : 'control-label',
9857                     html : this.fieldLabel
9858                 },
9859                 {
9860                     cls : "",
9861                     cn: [
9862                         inputblock
9863                     ]
9864                 }
9865
9866             ];
9867             
9868             if(this.labelWidth > 12){
9869                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9870             }
9871
9872             if(this.labelWidth < 13 && this.labelmd == 0){
9873                 this.labelmd = this.labelWidth;
9874             }
9875
9876             if(this.labellg > 0){
9877                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9878                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9879             }
9880
9881             if(this.labelmd > 0){
9882                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9883                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9884             }
9885
9886             if(this.labelsm > 0){
9887                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9888                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9889             }
9890
9891             if(this.labelxs > 0){
9892                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9893                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9894             }
9895             
9896         } else if ( this.fieldLabel.length) {
9897             cfg.cn = [
9898
9899                {
9900                    tag: 'label',
9901                    //cls : 'input-group-addon',
9902                    html : this.fieldLabel
9903
9904                },
9905
9906                inputblock
9907
9908            ];
9909
9910         } else {
9911
9912             cfg.cn = [
9913
9914                 inputblock
9915
9916             ];
9917                 
9918         }
9919         
9920         if (this.disabled) {
9921             input.disabled=true;
9922         }
9923         
9924         return cfg;
9925         
9926     },
9927     /**
9928      * return the real textarea element.
9929      */
9930     inputEl: function ()
9931     {
9932         return this.el.select('textarea.form-control',true).first();
9933     },
9934     
9935     /**
9936      * Clear any invalid styles/messages for this field
9937      */
9938     clearInvalid : function()
9939     {
9940         
9941         if(!this.el || this.preventMark){ // not rendered
9942             return;
9943         }
9944         
9945         var label = this.el.select('label', true).first();
9946         var icon = this.el.select('i.fa-star', true).first();
9947         
9948         if(label && icon){
9949             icon.remove();
9950         }
9951         
9952         this.el.removeClass(this.invalidClass);
9953         
9954         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9955             
9956             var feedback = this.el.select('.form-control-feedback', true).first();
9957             
9958             if(feedback){
9959                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9960             }
9961             
9962         }
9963         
9964         this.fireEvent('valid', this);
9965     },
9966     
9967      /**
9968      * Mark this field as valid
9969      */
9970     markValid : function()
9971     {
9972         if(!this.el  || this.preventMark){ // not rendered
9973             return;
9974         }
9975         
9976         this.el.removeClass([this.invalidClass, this.validClass]);
9977         
9978         var feedback = this.el.select('.form-control-feedback', true).first();
9979             
9980         if(feedback){
9981             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9982         }
9983
9984         if(this.disabled || this.allowBlank){
9985             return;
9986         }
9987         
9988         var label = this.el.select('label', true).first();
9989         var icon = this.el.select('i.fa-star', true).first();
9990         
9991         if(label && icon){
9992             icon.remove();
9993         }
9994         
9995         this.el.addClass(this.validClass);
9996         
9997         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9998             
9999             var feedback = this.el.select('.form-control-feedback', true).first();
10000             
10001             if(feedback){
10002                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10003                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10004             }
10005             
10006         }
10007         
10008         this.fireEvent('valid', this);
10009     },
10010     
10011      /**
10012      * Mark this field as invalid
10013      * @param {String} msg The validation message
10014      */
10015     markInvalid : function(msg)
10016     {
10017         if(!this.el  || this.preventMark){ // not rendered
10018             return;
10019         }
10020         
10021         this.el.removeClass([this.invalidClass, this.validClass]);
10022         
10023         var feedback = this.el.select('.form-control-feedback', true).first();
10024             
10025         if(feedback){
10026             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10027         }
10028
10029         if(this.disabled || this.allowBlank){
10030             return;
10031         }
10032         
10033         var label = this.el.select('label', true).first();
10034         var icon = this.el.select('i.fa-star', true).first();
10035         
10036         if(!this.getValue().length && label && !icon){
10037             this.el.createChild({
10038                 tag : 'i',
10039                 cls : 'text-danger fa fa-lg fa-star',
10040                 tooltip : 'This field is required',
10041                 style : 'margin-right:5px;'
10042             }, label, true);
10043         }
10044
10045         this.el.addClass(this.invalidClass);
10046         
10047         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10048             
10049             var feedback = this.el.select('.form-control-feedback', true).first();
10050             
10051             if(feedback){
10052                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10053                 
10054                 if(this.getValue().length || this.forceFeedback){
10055                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10056                 }
10057                 
10058             }
10059             
10060         }
10061         
10062         this.fireEvent('invalid', this, msg);
10063     }
10064 });
10065
10066  
10067 /*
10068  * - LGPL
10069  *
10070  * trigger field - base class for combo..
10071  * 
10072  */
10073  
10074 /**
10075  * @class Roo.bootstrap.TriggerField
10076  * @extends Roo.bootstrap.Input
10077  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10078  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10079  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10080  * for which you can provide a custom implementation.  For example:
10081  * <pre><code>
10082 var trigger = new Roo.bootstrap.TriggerField();
10083 trigger.onTriggerClick = myTriggerFn;
10084 trigger.applyTo('my-field');
10085 </code></pre>
10086  *
10087  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10088  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10089  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10090  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10091  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10092
10093  * @constructor
10094  * Create a new TriggerField.
10095  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10096  * to the base TextField)
10097  */
10098 Roo.bootstrap.TriggerField = function(config){
10099     this.mimicing = false;
10100     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10101 };
10102
10103 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10104     /**
10105      * @cfg {String} triggerClass A CSS class to apply to the trigger
10106      */
10107      /**
10108      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10109      */
10110     hideTrigger:false,
10111
10112     /**
10113      * @cfg {Boolean} removable (true|false) special filter default false
10114      */
10115     removable : false,
10116     
10117     /** @cfg {Boolean} grow @hide */
10118     /** @cfg {Number} growMin @hide */
10119     /** @cfg {Number} growMax @hide */
10120
10121     /**
10122      * @hide 
10123      * @method
10124      */
10125     autoSize: Roo.emptyFn,
10126     // private
10127     monitorTab : true,
10128     // private
10129     deferHeight : true,
10130
10131     
10132     actionMode : 'wrap',
10133     
10134     caret : false,
10135     
10136     
10137     getAutoCreate : function(){
10138        
10139         var align = this.labelAlign || this.parentLabelAlign();
10140         
10141         var id = Roo.id();
10142         
10143         var cfg = {
10144             cls: 'form-group' //input-group
10145         };
10146         
10147         
10148         var input =  {
10149             tag: 'input',
10150             id : id,
10151             type : this.inputType,
10152             cls : 'form-control',
10153             autocomplete: 'new-password',
10154             placeholder : this.placeholder || '' 
10155             
10156         };
10157         if (this.name) {
10158             input.name = this.name;
10159         }
10160         if (this.size) {
10161             input.cls += ' input-' + this.size;
10162         }
10163         
10164         if (this.disabled) {
10165             input.disabled=true;
10166         }
10167         
10168         var inputblock = input;
10169         
10170         if(this.hasFeedback && !this.allowBlank){
10171             
10172             var feedback = {
10173                 tag: 'span',
10174                 cls: 'glyphicon form-control-feedback'
10175             };
10176             
10177             if(this.removable && !this.editable && !this.tickable){
10178                 inputblock = {
10179                     cls : 'has-feedback',
10180                     cn :  [
10181                         inputblock,
10182                         {
10183                             tag: 'button',
10184                             html : 'x',
10185                             cls : 'roo-combo-removable-btn close'
10186                         },
10187                         feedback
10188                     ] 
10189                 };
10190             } else {
10191                 inputblock = {
10192                     cls : 'has-feedback',
10193                     cn :  [
10194                         inputblock,
10195                         feedback
10196                     ] 
10197                 };
10198             }
10199
10200         } else {
10201             if(this.removable && !this.editable && !this.tickable){
10202                 inputblock = {
10203                     cls : 'roo-removable',
10204                     cn :  [
10205                         inputblock,
10206                         {
10207                             tag: 'button',
10208                             html : 'x',
10209                             cls : 'roo-combo-removable-btn close'
10210                         }
10211                     ] 
10212                 };
10213             }
10214         }
10215         
10216         if (this.before || this.after) {
10217             
10218             inputblock = {
10219                 cls : 'input-group',
10220                 cn :  [] 
10221             };
10222             if (this.before) {
10223                 inputblock.cn.push({
10224                     tag :'span',
10225                     cls : 'input-group-addon',
10226                     html : this.before
10227                 });
10228             }
10229             
10230             inputblock.cn.push(input);
10231             
10232             if(this.hasFeedback && !this.allowBlank){
10233                 inputblock.cls += ' has-feedback';
10234                 inputblock.cn.push(feedback);
10235             }
10236             
10237             if (this.after) {
10238                 inputblock.cn.push({
10239                     tag :'span',
10240                     cls : 'input-group-addon',
10241                     html : this.after
10242                 });
10243             }
10244             
10245         };
10246         
10247         var box = {
10248             tag: 'div',
10249             cn: [
10250                 {
10251                     tag: 'input',
10252                     type : 'hidden',
10253                     cls: 'form-hidden-field'
10254                 },
10255                 inputblock
10256             ]
10257             
10258         };
10259         
10260         if(this.multiple){
10261             box = {
10262                 tag: 'div',
10263                 cn: [
10264                     {
10265                         tag: 'input',
10266                         type : 'hidden',
10267                         cls: 'form-hidden-field'
10268                     },
10269                     {
10270                         tag: 'ul',
10271                         cls: 'roo-select2-choices',
10272                         cn:[
10273                             {
10274                                 tag: 'li',
10275                                 cls: 'roo-select2-search-field',
10276                                 cn: [
10277
10278                                     inputblock
10279                                 ]
10280                             }
10281                         ]
10282                     }
10283                 ]
10284             }
10285         };
10286         
10287         var combobox = {
10288             cls: 'roo-select2-container input-group',
10289             cn: [
10290                 box
10291 //                {
10292 //                    tag: 'ul',
10293 //                    cls: 'typeahead typeahead-long dropdown-menu',
10294 //                    style: 'display:none'
10295 //                }
10296             ]
10297         };
10298         
10299         if(!this.multiple && this.showToggleBtn){
10300             
10301             var caret = {
10302                         tag: 'span',
10303                         cls: 'caret'
10304              };
10305             if (this.caret != false) {
10306                 caret = {
10307                      tag: 'i',
10308                      cls: 'fa fa-' + this.caret
10309                 };
10310                 
10311             }
10312             
10313             combobox.cn.push({
10314                 tag :'span',
10315                 cls : 'input-group-addon btn dropdown-toggle',
10316                 cn : [
10317                     caret,
10318                     {
10319                         tag: 'span',
10320                         cls: 'combobox-clear',
10321                         cn  : [
10322                             {
10323                                 tag : 'i',
10324                                 cls: 'icon-remove'
10325                             }
10326                         ]
10327                     }
10328                 ]
10329
10330             })
10331         }
10332         
10333         if(this.multiple){
10334             combobox.cls += ' roo-select2-container-multi';
10335         }
10336         
10337         if (align ==='left' && this.fieldLabel.length) {
10338             
10339             cfg.cls += ' roo-form-group-label-left';
10340
10341             cfg.cn = [
10342                 {
10343                     tag : 'i',
10344                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10345                     tooltip : 'This field is required'
10346                 },
10347                 {
10348                     tag: 'label',
10349                     'for' :  id,
10350                     cls : 'control-label',
10351                     html : this.fieldLabel
10352
10353                 },
10354                 {
10355                     cls : "", 
10356                     cn: [
10357                         combobox
10358                     ]
10359                 }
10360
10361             ];
10362             
10363             var labelCfg = cfg.cn[1];
10364             var contentCfg = cfg.cn[2];
10365             
10366             if(this.indicatorpos == 'right'){
10367                 cfg.cn = [
10368                     {
10369                         tag: 'label',
10370                         'for' :  id,
10371                         cls : 'control-label',
10372                         cn : [
10373                             {
10374                                 tag : 'span',
10375                                 html : this.fieldLabel
10376                             },
10377                             {
10378                                 tag : 'i',
10379                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10380                                 tooltip : 'This field is required'
10381                             }
10382                         ]
10383                     },
10384                     {
10385                         cls : "", 
10386                         cn: [
10387                             combobox
10388                         ]
10389                     }
10390
10391                 ];
10392                 
10393                 labelCfg = cfg.cn[0];
10394                 contentCfg = cfg.cn[1];
10395             }
10396             
10397             if(this.labelWidth > 12){
10398                 labelCfg.style = "width: " + this.labelWidth + 'px';
10399             }
10400             
10401             if(this.labelWidth < 13 && this.labelmd == 0){
10402                 this.labelmd = this.labelWidth;
10403             }
10404             
10405             if(this.labellg > 0){
10406                 labelCfg.cls += ' col-lg-' + this.labellg;
10407                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10408             }
10409             
10410             if(this.labelmd > 0){
10411                 labelCfg.cls += ' col-md-' + this.labelmd;
10412                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10413             }
10414             
10415             if(this.labelsm > 0){
10416                 labelCfg.cls += ' col-sm-' + this.labelsm;
10417                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10418             }
10419             
10420             if(this.labelxs > 0){
10421                 labelCfg.cls += ' col-xs-' + this.labelxs;
10422                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10423             }
10424             
10425         } else if ( this.fieldLabel.length) {
10426 //                Roo.log(" label");
10427             cfg.cn = [
10428                 {
10429                    tag : 'i',
10430                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10431                    tooltip : 'This field is required'
10432                },
10433                {
10434                    tag: 'label',
10435                    //cls : 'input-group-addon',
10436                    html : this.fieldLabel
10437
10438                },
10439
10440                combobox
10441
10442             ];
10443             
10444             if(this.indicatorpos == 'right'){
10445                 
10446                 cfg.cn = [
10447                     {
10448                        tag: 'label',
10449                        cn : [
10450                            {
10451                                tag : 'span',
10452                                html : this.fieldLabel
10453                            },
10454                            {
10455                               tag : 'i',
10456                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10457                               tooltip : 'This field is required'
10458                            }
10459                        ]
10460
10461                     },
10462                     combobox
10463
10464                 ];
10465
10466             }
10467
10468         } else {
10469             
10470 //                Roo.log(" no label && no align");
10471                 cfg = combobox
10472                      
10473                 
10474         }
10475         
10476         var settings=this;
10477         ['xs','sm','md','lg'].map(function(size){
10478             if (settings[size]) {
10479                 cfg.cls += ' col-' + size + '-' + settings[size];
10480             }
10481         });
10482         
10483         return cfg;
10484         
10485     },
10486     
10487     
10488     
10489     // private
10490     onResize : function(w, h){
10491 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10492 //        if(typeof w == 'number'){
10493 //            var x = w - this.trigger.getWidth();
10494 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10495 //            this.trigger.setStyle('left', x+'px');
10496 //        }
10497     },
10498
10499     // private
10500     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10501
10502     // private
10503     getResizeEl : function(){
10504         return this.inputEl();
10505     },
10506
10507     // private
10508     getPositionEl : function(){
10509         return this.inputEl();
10510     },
10511
10512     // private
10513     alignErrorIcon : function(){
10514         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10515     },
10516
10517     // private
10518     initEvents : function(){
10519         
10520         this.createList();
10521         
10522         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10523         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10524         if(!this.multiple && this.showToggleBtn){
10525             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10526             if(this.hideTrigger){
10527                 this.trigger.setDisplayed(false);
10528             }
10529             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10530         }
10531         
10532         if(this.multiple){
10533             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10534         }
10535         
10536         if(this.removable && !this.editable && !this.tickable){
10537             var close = this.closeTriggerEl();
10538             
10539             if(close){
10540                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10541                 close.on('click', this.removeBtnClick, this, close);
10542             }
10543         }
10544         
10545         //this.trigger.addClassOnOver('x-form-trigger-over');
10546         //this.trigger.addClassOnClick('x-form-trigger-click');
10547         
10548         //if(!this.width){
10549         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10550         //}
10551     },
10552     
10553     closeTriggerEl : function()
10554     {
10555         var close = this.el.select('.roo-combo-removable-btn', true).first();
10556         return close ? close : false;
10557     },
10558     
10559     removeBtnClick : function(e, h, el)
10560     {
10561         e.preventDefault();
10562         
10563         if(this.fireEvent("remove", this) !== false){
10564             this.reset();
10565             this.fireEvent("afterremove", this)
10566         }
10567     },
10568     
10569     createList : function()
10570     {
10571         this.list = Roo.get(document.body).createChild({
10572             tag: 'ul',
10573             cls: 'typeahead typeahead-long dropdown-menu',
10574             style: 'display:none'
10575         });
10576         
10577         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10578         
10579     },
10580
10581     // private
10582     initTrigger : function(){
10583        
10584     },
10585
10586     // private
10587     onDestroy : function(){
10588         if(this.trigger){
10589             this.trigger.removeAllListeners();
10590           //  this.trigger.remove();
10591         }
10592         //if(this.wrap){
10593         //    this.wrap.remove();
10594         //}
10595         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10596     },
10597
10598     // private
10599     onFocus : function(){
10600         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10601         /*
10602         if(!this.mimicing){
10603             this.wrap.addClass('x-trigger-wrap-focus');
10604             this.mimicing = true;
10605             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10606             if(this.monitorTab){
10607                 this.el.on("keydown", this.checkTab, this);
10608             }
10609         }
10610         */
10611     },
10612
10613     // private
10614     checkTab : function(e){
10615         if(e.getKey() == e.TAB){
10616             this.triggerBlur();
10617         }
10618     },
10619
10620     // private
10621     onBlur : function(){
10622         // do nothing
10623     },
10624
10625     // private
10626     mimicBlur : function(e, t){
10627         /*
10628         if(!this.wrap.contains(t) && this.validateBlur()){
10629             this.triggerBlur();
10630         }
10631         */
10632     },
10633
10634     // private
10635     triggerBlur : function(){
10636         this.mimicing = false;
10637         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10638         if(this.monitorTab){
10639             this.el.un("keydown", this.checkTab, this);
10640         }
10641         //this.wrap.removeClass('x-trigger-wrap-focus');
10642         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10643     },
10644
10645     // private
10646     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10647     validateBlur : function(e, t){
10648         return true;
10649     },
10650
10651     // private
10652     onDisable : function(){
10653         this.inputEl().dom.disabled = true;
10654         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10655         //if(this.wrap){
10656         //    this.wrap.addClass('x-item-disabled');
10657         //}
10658     },
10659
10660     // private
10661     onEnable : function(){
10662         this.inputEl().dom.disabled = false;
10663         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10664         //if(this.wrap){
10665         //    this.el.removeClass('x-item-disabled');
10666         //}
10667     },
10668
10669     // private
10670     onShow : function(){
10671         var ae = this.getActionEl();
10672         
10673         if(ae){
10674             ae.dom.style.display = '';
10675             ae.dom.style.visibility = 'visible';
10676         }
10677     },
10678
10679     // private
10680     
10681     onHide : function(){
10682         var ae = this.getActionEl();
10683         ae.dom.style.display = 'none';
10684     },
10685
10686     /**
10687      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10688      * by an implementing function.
10689      * @method
10690      * @param {EventObject} e
10691      */
10692     onTriggerClick : Roo.emptyFn
10693 });
10694  /*
10695  * Based on:
10696  * Ext JS Library 1.1.1
10697  * Copyright(c) 2006-2007, Ext JS, LLC.
10698  *
10699  * Originally Released Under LGPL - original licence link has changed is not relivant.
10700  *
10701  * Fork - LGPL
10702  * <script type="text/javascript">
10703  */
10704
10705
10706 /**
10707  * @class Roo.data.SortTypes
10708  * @singleton
10709  * Defines the default sorting (casting?) comparison functions used when sorting data.
10710  */
10711 Roo.data.SortTypes = {
10712     /**
10713      * Default sort that does nothing
10714      * @param {Mixed} s The value being converted
10715      * @return {Mixed} The comparison value
10716      */
10717     none : function(s){
10718         return s;
10719     },
10720     
10721     /**
10722      * The regular expression used to strip tags
10723      * @type {RegExp}
10724      * @property
10725      */
10726     stripTagsRE : /<\/?[^>]+>/gi,
10727     
10728     /**
10729      * Strips all HTML tags to sort on text only
10730      * @param {Mixed} s The value being converted
10731      * @return {String} The comparison value
10732      */
10733     asText : function(s){
10734         return String(s).replace(this.stripTagsRE, "");
10735     },
10736     
10737     /**
10738      * Strips all HTML tags to sort on text only - Case insensitive
10739      * @param {Mixed} s The value being converted
10740      * @return {String} The comparison value
10741      */
10742     asUCText : function(s){
10743         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10744     },
10745     
10746     /**
10747      * Case insensitive string
10748      * @param {Mixed} s The value being converted
10749      * @return {String} The comparison value
10750      */
10751     asUCString : function(s) {
10752         return String(s).toUpperCase();
10753     },
10754     
10755     /**
10756      * Date sorting
10757      * @param {Mixed} s The value being converted
10758      * @return {Number} The comparison value
10759      */
10760     asDate : function(s) {
10761         if(!s){
10762             return 0;
10763         }
10764         if(s instanceof Date){
10765             return s.getTime();
10766         }
10767         return Date.parse(String(s));
10768     },
10769     
10770     /**
10771      * Float sorting
10772      * @param {Mixed} s The value being converted
10773      * @return {Float} The comparison value
10774      */
10775     asFloat : function(s) {
10776         var val = parseFloat(String(s).replace(/,/g, ""));
10777         if(isNaN(val)) {
10778             val = 0;
10779         }
10780         return val;
10781     },
10782     
10783     /**
10784      * Integer sorting
10785      * @param {Mixed} s The value being converted
10786      * @return {Number} The comparison value
10787      */
10788     asInt : function(s) {
10789         var val = parseInt(String(s).replace(/,/g, ""));
10790         if(isNaN(val)) {
10791             val = 0;
10792         }
10793         return val;
10794     }
10795 };/*
10796  * Based on:
10797  * Ext JS Library 1.1.1
10798  * Copyright(c) 2006-2007, Ext JS, LLC.
10799  *
10800  * Originally Released Under LGPL - original licence link has changed is not relivant.
10801  *
10802  * Fork - LGPL
10803  * <script type="text/javascript">
10804  */
10805
10806 /**
10807 * @class Roo.data.Record
10808  * Instances of this class encapsulate both record <em>definition</em> information, and record
10809  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10810  * to access Records cached in an {@link Roo.data.Store} object.<br>
10811  * <p>
10812  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10813  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10814  * objects.<br>
10815  * <p>
10816  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10817  * @constructor
10818  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10819  * {@link #create}. The parameters are the same.
10820  * @param {Array} data An associative Array of data values keyed by the field name.
10821  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10822  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10823  * not specified an integer id is generated.
10824  */
10825 Roo.data.Record = function(data, id){
10826     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10827     this.data = data;
10828 };
10829
10830 /**
10831  * Generate a constructor for a specific record layout.
10832  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10833  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10834  * Each field definition object may contain the following properties: <ul>
10835  * <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,
10836  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10837  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10838  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10839  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10840  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10841  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10842  * this may be omitted.</p></li>
10843  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10844  * <ul><li>auto (Default, implies no conversion)</li>
10845  * <li>string</li>
10846  * <li>int</li>
10847  * <li>float</li>
10848  * <li>boolean</li>
10849  * <li>date</li></ul></p></li>
10850  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10851  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10852  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10853  * by the Reader into an object that will be stored in the Record. It is passed the
10854  * following parameters:<ul>
10855  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10856  * </ul></p></li>
10857  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10858  * </ul>
10859  * <br>usage:<br><pre><code>
10860 var TopicRecord = Roo.data.Record.create(
10861     {name: 'title', mapping: 'topic_title'},
10862     {name: 'author', mapping: 'username'},
10863     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10864     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10865     {name: 'lastPoster', mapping: 'user2'},
10866     {name: 'excerpt', mapping: 'post_text'}
10867 );
10868
10869 var myNewRecord = new TopicRecord({
10870     title: 'Do my job please',
10871     author: 'noobie',
10872     totalPosts: 1,
10873     lastPost: new Date(),
10874     lastPoster: 'Animal',
10875     excerpt: 'No way dude!'
10876 });
10877 myStore.add(myNewRecord);
10878 </code></pre>
10879  * @method create
10880  * @static
10881  */
10882 Roo.data.Record.create = function(o){
10883     var f = function(){
10884         f.superclass.constructor.apply(this, arguments);
10885     };
10886     Roo.extend(f, Roo.data.Record);
10887     var p = f.prototype;
10888     p.fields = new Roo.util.MixedCollection(false, function(field){
10889         return field.name;
10890     });
10891     for(var i = 0, len = o.length; i < len; i++){
10892         p.fields.add(new Roo.data.Field(o[i]));
10893     }
10894     f.getField = function(name){
10895         return p.fields.get(name);  
10896     };
10897     return f;
10898 };
10899
10900 Roo.data.Record.AUTO_ID = 1000;
10901 Roo.data.Record.EDIT = 'edit';
10902 Roo.data.Record.REJECT = 'reject';
10903 Roo.data.Record.COMMIT = 'commit';
10904
10905 Roo.data.Record.prototype = {
10906     /**
10907      * Readonly flag - true if this record has been modified.
10908      * @type Boolean
10909      */
10910     dirty : false,
10911     editing : false,
10912     error: null,
10913     modified: null,
10914
10915     // private
10916     join : function(store){
10917         this.store = store;
10918     },
10919
10920     /**
10921      * Set the named field to the specified value.
10922      * @param {String} name The name of the field to set.
10923      * @param {Object} value The value to set the field to.
10924      */
10925     set : function(name, value){
10926         if(this.data[name] == value){
10927             return;
10928         }
10929         this.dirty = true;
10930         if(!this.modified){
10931             this.modified = {};
10932         }
10933         if(typeof this.modified[name] == 'undefined'){
10934             this.modified[name] = this.data[name];
10935         }
10936         this.data[name] = value;
10937         if(!this.editing && this.store){
10938             this.store.afterEdit(this);
10939         }       
10940     },
10941
10942     /**
10943      * Get the value of the named field.
10944      * @param {String} name The name of the field to get the value of.
10945      * @return {Object} The value of the field.
10946      */
10947     get : function(name){
10948         return this.data[name]; 
10949     },
10950
10951     // private
10952     beginEdit : function(){
10953         this.editing = true;
10954         this.modified = {}; 
10955     },
10956
10957     // private
10958     cancelEdit : function(){
10959         this.editing = false;
10960         delete this.modified;
10961     },
10962
10963     // private
10964     endEdit : function(){
10965         this.editing = false;
10966         if(this.dirty && this.store){
10967             this.store.afterEdit(this);
10968         }
10969     },
10970
10971     /**
10972      * Usually called by the {@link Roo.data.Store} which owns the Record.
10973      * Rejects all changes made to the Record since either creation, or the last commit operation.
10974      * Modified fields are reverted to their original values.
10975      * <p>
10976      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10977      * of reject operations.
10978      */
10979     reject : function(){
10980         var m = this.modified;
10981         for(var n in m){
10982             if(typeof m[n] != "function"){
10983                 this.data[n] = m[n];
10984             }
10985         }
10986         this.dirty = false;
10987         delete this.modified;
10988         this.editing = false;
10989         if(this.store){
10990             this.store.afterReject(this);
10991         }
10992     },
10993
10994     /**
10995      * Usually called by the {@link Roo.data.Store} which owns the Record.
10996      * Commits all changes made to the Record since either creation, or the last commit operation.
10997      * <p>
10998      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10999      * of commit operations.
11000      */
11001     commit : function(){
11002         this.dirty = false;
11003         delete this.modified;
11004         this.editing = false;
11005         if(this.store){
11006             this.store.afterCommit(this);
11007         }
11008     },
11009
11010     // private
11011     hasError : function(){
11012         return this.error != null;
11013     },
11014
11015     // private
11016     clearError : function(){
11017         this.error = null;
11018     },
11019
11020     /**
11021      * Creates a copy of this record.
11022      * @param {String} id (optional) A new record id if you don't want to use this record's id
11023      * @return {Record}
11024      */
11025     copy : function(newId) {
11026         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11027     }
11028 };/*
11029  * Based on:
11030  * Ext JS Library 1.1.1
11031  * Copyright(c) 2006-2007, Ext JS, LLC.
11032  *
11033  * Originally Released Under LGPL - original licence link has changed is not relivant.
11034  *
11035  * Fork - LGPL
11036  * <script type="text/javascript">
11037  */
11038
11039
11040
11041 /**
11042  * @class Roo.data.Store
11043  * @extends Roo.util.Observable
11044  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11045  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11046  * <p>
11047  * 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
11048  * has no knowledge of the format of the data returned by the Proxy.<br>
11049  * <p>
11050  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11051  * instances from the data object. These records are cached and made available through accessor functions.
11052  * @constructor
11053  * Creates a new Store.
11054  * @param {Object} config A config object containing the objects needed for the Store to access data,
11055  * and read the data into Records.
11056  */
11057 Roo.data.Store = function(config){
11058     this.data = new Roo.util.MixedCollection(false);
11059     this.data.getKey = function(o){
11060         return o.id;
11061     };
11062     this.baseParams = {};
11063     // private
11064     this.paramNames = {
11065         "start" : "start",
11066         "limit" : "limit",
11067         "sort" : "sort",
11068         "dir" : "dir",
11069         "multisort" : "_multisort"
11070     };
11071
11072     if(config && config.data){
11073         this.inlineData = config.data;
11074         delete config.data;
11075     }
11076
11077     Roo.apply(this, config);
11078     
11079     if(this.reader){ // reader passed
11080         this.reader = Roo.factory(this.reader, Roo.data);
11081         this.reader.xmodule = this.xmodule || false;
11082         if(!this.recordType){
11083             this.recordType = this.reader.recordType;
11084         }
11085         if(this.reader.onMetaChange){
11086             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11087         }
11088     }
11089
11090     if(this.recordType){
11091         this.fields = this.recordType.prototype.fields;
11092     }
11093     this.modified = [];
11094
11095     this.addEvents({
11096         /**
11097          * @event datachanged
11098          * Fires when the data cache has changed, and a widget which is using this Store
11099          * as a Record cache should refresh its view.
11100          * @param {Store} this
11101          */
11102         datachanged : true,
11103         /**
11104          * @event metachange
11105          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11106          * @param {Store} this
11107          * @param {Object} meta The JSON metadata
11108          */
11109         metachange : true,
11110         /**
11111          * @event add
11112          * Fires when Records have been added to the Store
11113          * @param {Store} this
11114          * @param {Roo.data.Record[]} records The array of Records added
11115          * @param {Number} index The index at which the record(s) were added
11116          */
11117         add : true,
11118         /**
11119          * @event remove
11120          * Fires when a Record has been removed from the Store
11121          * @param {Store} this
11122          * @param {Roo.data.Record} record The Record that was removed
11123          * @param {Number} index The index at which the record was removed
11124          */
11125         remove : true,
11126         /**
11127          * @event update
11128          * Fires when a Record has been updated
11129          * @param {Store} this
11130          * @param {Roo.data.Record} record The Record that was updated
11131          * @param {String} operation The update operation being performed.  Value may be one of:
11132          * <pre><code>
11133  Roo.data.Record.EDIT
11134  Roo.data.Record.REJECT
11135  Roo.data.Record.COMMIT
11136          * </code></pre>
11137          */
11138         update : true,
11139         /**
11140          * @event clear
11141          * Fires when the data cache has been cleared.
11142          * @param {Store} this
11143          */
11144         clear : true,
11145         /**
11146          * @event beforeload
11147          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11148          * the load action will be canceled.
11149          * @param {Store} this
11150          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11151          */
11152         beforeload : true,
11153         /**
11154          * @event beforeloadadd
11155          * Fires after a new set of Records has been loaded.
11156          * @param {Store} this
11157          * @param {Roo.data.Record[]} records The Records that were loaded
11158          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11159          */
11160         beforeloadadd : true,
11161         /**
11162          * @event load
11163          * Fires after a new set of Records has been loaded, before they are added to the store.
11164          * @param {Store} this
11165          * @param {Roo.data.Record[]} records The Records that were loaded
11166          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11167          * @params {Object} return from reader
11168          */
11169         load : true,
11170         /**
11171          * @event loadexception
11172          * Fires if an exception occurs in the Proxy during loading.
11173          * Called with the signature of the Proxy's "loadexception" event.
11174          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11175          * 
11176          * @param {Proxy} 
11177          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11178          * @param {Object} load options 
11179          * @param {Object} jsonData from your request (normally this contains the Exception)
11180          */
11181         loadexception : true
11182     });
11183     
11184     if(this.proxy){
11185         this.proxy = Roo.factory(this.proxy, Roo.data);
11186         this.proxy.xmodule = this.xmodule || false;
11187         this.relayEvents(this.proxy,  ["loadexception"]);
11188     }
11189     this.sortToggle = {};
11190     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11191
11192     Roo.data.Store.superclass.constructor.call(this);
11193
11194     if(this.inlineData){
11195         this.loadData(this.inlineData);
11196         delete this.inlineData;
11197     }
11198 };
11199
11200 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11201      /**
11202     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11203     * without a remote query - used by combo/forms at present.
11204     */
11205     
11206     /**
11207     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11208     */
11209     /**
11210     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11211     */
11212     /**
11213     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11214     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11215     */
11216     /**
11217     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11218     * on any HTTP request
11219     */
11220     /**
11221     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11222     */
11223     /**
11224     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11225     */
11226     multiSort: false,
11227     /**
11228     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11229     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11230     */
11231     remoteSort : false,
11232
11233     /**
11234     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11235      * loaded or when a record is removed. (defaults to false).
11236     */
11237     pruneModifiedRecords : false,
11238
11239     // private
11240     lastOptions : null,
11241
11242     /**
11243      * Add Records to the Store and fires the add event.
11244      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11245      */
11246     add : function(records){
11247         records = [].concat(records);
11248         for(var i = 0, len = records.length; i < len; i++){
11249             records[i].join(this);
11250         }
11251         var index = this.data.length;
11252         this.data.addAll(records);
11253         this.fireEvent("add", this, records, index);
11254     },
11255
11256     /**
11257      * Remove a Record from the Store and fires the remove event.
11258      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11259      */
11260     remove : function(record){
11261         var index = this.data.indexOf(record);
11262         this.data.removeAt(index);
11263  
11264         if(this.pruneModifiedRecords){
11265             this.modified.remove(record);
11266         }
11267         this.fireEvent("remove", this, record, index);
11268     },
11269
11270     /**
11271      * Remove all Records from the Store and fires the clear event.
11272      */
11273     removeAll : function(){
11274         this.data.clear();
11275         if(this.pruneModifiedRecords){
11276             this.modified = [];
11277         }
11278         this.fireEvent("clear", this);
11279     },
11280
11281     /**
11282      * Inserts Records to the Store at the given index and fires the add event.
11283      * @param {Number} index The start index at which to insert the passed Records.
11284      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11285      */
11286     insert : function(index, records){
11287         records = [].concat(records);
11288         for(var i = 0, len = records.length; i < len; i++){
11289             this.data.insert(index, records[i]);
11290             records[i].join(this);
11291         }
11292         this.fireEvent("add", this, records, index);
11293     },
11294
11295     /**
11296      * Get the index within the cache of the passed Record.
11297      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11298      * @return {Number} The index of the passed Record. Returns -1 if not found.
11299      */
11300     indexOf : function(record){
11301         return this.data.indexOf(record);
11302     },
11303
11304     /**
11305      * Get the index within the cache of the Record with the passed id.
11306      * @param {String} id The id of the Record to find.
11307      * @return {Number} The index of the Record. Returns -1 if not found.
11308      */
11309     indexOfId : function(id){
11310         return this.data.indexOfKey(id);
11311     },
11312
11313     /**
11314      * Get the Record with the specified id.
11315      * @param {String} id The id of the Record to find.
11316      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11317      */
11318     getById : function(id){
11319         return this.data.key(id);
11320     },
11321
11322     /**
11323      * Get the Record at the specified index.
11324      * @param {Number} index The index of the Record to find.
11325      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11326      */
11327     getAt : function(index){
11328         return this.data.itemAt(index);
11329     },
11330
11331     /**
11332      * Returns a range of Records between specified indices.
11333      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11334      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11335      * @return {Roo.data.Record[]} An array of Records
11336      */
11337     getRange : function(start, end){
11338         return this.data.getRange(start, end);
11339     },
11340
11341     // private
11342     storeOptions : function(o){
11343         o = Roo.apply({}, o);
11344         delete o.callback;
11345         delete o.scope;
11346         this.lastOptions = o;
11347     },
11348
11349     /**
11350      * Loads the Record cache from the configured Proxy using the configured Reader.
11351      * <p>
11352      * If using remote paging, then the first load call must specify the <em>start</em>
11353      * and <em>limit</em> properties in the options.params property to establish the initial
11354      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11355      * <p>
11356      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11357      * and this call will return before the new data has been loaded. Perform any post-processing
11358      * in a callback function, or in a "load" event handler.</strong>
11359      * <p>
11360      * @param {Object} options An object containing properties which control loading options:<ul>
11361      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11362      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11363      * passed the following arguments:<ul>
11364      * <li>r : Roo.data.Record[]</li>
11365      * <li>options: Options object from the load call</li>
11366      * <li>success: Boolean success indicator</li></ul></li>
11367      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11368      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11369      * </ul>
11370      */
11371     load : function(options){
11372         options = options || {};
11373         if(this.fireEvent("beforeload", this, options) !== false){
11374             this.storeOptions(options);
11375             var p = Roo.apply(options.params || {}, this.baseParams);
11376             // if meta was not loaded from remote source.. try requesting it.
11377             if (!this.reader.metaFromRemote) {
11378                 p._requestMeta = 1;
11379             }
11380             if(this.sortInfo && this.remoteSort){
11381                 var pn = this.paramNames;
11382                 p[pn["sort"]] = this.sortInfo.field;
11383                 p[pn["dir"]] = this.sortInfo.direction;
11384             }
11385             if (this.multiSort) {
11386                 var pn = this.paramNames;
11387                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11388             }
11389             
11390             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11391         }
11392     },
11393
11394     /**
11395      * Reloads the Record cache from the configured Proxy using the configured Reader and
11396      * the options from the last load operation performed.
11397      * @param {Object} options (optional) An object containing properties which may override the options
11398      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11399      * the most recently used options are reused).
11400      */
11401     reload : function(options){
11402         this.load(Roo.applyIf(options||{}, this.lastOptions));
11403     },
11404
11405     // private
11406     // Called as a callback by the Reader during a load operation.
11407     loadRecords : function(o, options, success){
11408         if(!o || success === false){
11409             if(success !== false){
11410                 this.fireEvent("load", this, [], options, o);
11411             }
11412             if(options.callback){
11413                 options.callback.call(options.scope || this, [], options, false);
11414             }
11415             return;
11416         }
11417         // if data returned failure - throw an exception.
11418         if (o.success === false) {
11419             // show a message if no listener is registered.
11420             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11421                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11422             }
11423             // loadmask wil be hooked into this..
11424             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11425             return;
11426         }
11427         var r = o.records, t = o.totalRecords || r.length;
11428         
11429         this.fireEvent("beforeloadadd", this, r, options, o);
11430         
11431         if(!options || options.add !== true){
11432             if(this.pruneModifiedRecords){
11433                 this.modified = [];
11434             }
11435             for(var i = 0, len = r.length; i < len; i++){
11436                 r[i].join(this);
11437             }
11438             if(this.snapshot){
11439                 this.data = this.snapshot;
11440                 delete this.snapshot;
11441             }
11442             this.data.clear();
11443             this.data.addAll(r);
11444             this.totalLength = t;
11445             this.applySort();
11446             this.fireEvent("datachanged", this);
11447         }else{
11448             this.totalLength = Math.max(t, this.data.length+r.length);
11449             this.add(r);
11450         }
11451         
11452         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11453                 
11454             var e = new Roo.data.Record({});
11455
11456             e.set(this.parent.displayField, this.parent.emptyTitle);
11457             e.set(this.parent.valueField, '');
11458
11459             this.insert(0, e);
11460         }
11461             
11462         this.fireEvent("load", this, r, options, o);
11463         if(options.callback){
11464             options.callback.call(options.scope || this, r, options, true);
11465         }
11466     },
11467
11468
11469     /**
11470      * Loads data from a passed data block. A Reader which understands the format of the data
11471      * must have been configured in the constructor.
11472      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11473      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11474      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11475      */
11476     loadData : function(o, append){
11477         var r = this.reader.readRecords(o);
11478         this.loadRecords(r, {add: append}, true);
11479     },
11480
11481     /**
11482      * Gets the number of cached records.
11483      * <p>
11484      * <em>If using paging, this may not be the total size of the dataset. If the data object
11485      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11486      * the data set size</em>
11487      */
11488     getCount : function(){
11489         return this.data.length || 0;
11490     },
11491
11492     /**
11493      * Gets the total number of records in the dataset as returned by the server.
11494      * <p>
11495      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11496      * the dataset size</em>
11497      */
11498     getTotalCount : function(){
11499         return this.totalLength || 0;
11500     },
11501
11502     /**
11503      * Returns the sort state of the Store as an object with two properties:
11504      * <pre><code>
11505  field {String} The name of the field by which the Records are sorted
11506  direction {String} The sort order, "ASC" or "DESC"
11507      * </code></pre>
11508      */
11509     getSortState : function(){
11510         return this.sortInfo;
11511     },
11512
11513     // private
11514     applySort : function(){
11515         if(this.sortInfo && !this.remoteSort){
11516             var s = this.sortInfo, f = s.field;
11517             var st = this.fields.get(f).sortType;
11518             var fn = function(r1, r2){
11519                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11520                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11521             };
11522             this.data.sort(s.direction, fn);
11523             if(this.snapshot && this.snapshot != this.data){
11524                 this.snapshot.sort(s.direction, fn);
11525             }
11526         }
11527     },
11528
11529     /**
11530      * Sets the default sort column and order to be used by the next load operation.
11531      * @param {String} fieldName The name of the field to sort by.
11532      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11533      */
11534     setDefaultSort : function(field, dir){
11535         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11536     },
11537
11538     /**
11539      * Sort the Records.
11540      * If remote sorting is used, the sort is performed on the server, and the cache is
11541      * reloaded. If local sorting is used, the cache is sorted internally.
11542      * @param {String} fieldName The name of the field to sort by.
11543      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11544      */
11545     sort : function(fieldName, dir){
11546         var f = this.fields.get(fieldName);
11547         if(!dir){
11548             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11549             
11550             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11551                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11552             }else{
11553                 dir = f.sortDir;
11554             }
11555         }
11556         this.sortToggle[f.name] = dir;
11557         this.sortInfo = {field: f.name, direction: dir};
11558         if(!this.remoteSort){
11559             this.applySort();
11560             this.fireEvent("datachanged", this);
11561         }else{
11562             this.load(this.lastOptions);
11563         }
11564     },
11565
11566     /**
11567      * Calls the specified function for each of the Records in the cache.
11568      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11569      * Returning <em>false</em> aborts and exits the iteration.
11570      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11571      */
11572     each : function(fn, scope){
11573         this.data.each(fn, scope);
11574     },
11575
11576     /**
11577      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11578      * (e.g., during paging).
11579      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11580      */
11581     getModifiedRecords : function(){
11582         return this.modified;
11583     },
11584
11585     // private
11586     createFilterFn : function(property, value, anyMatch){
11587         if(!value.exec){ // not a regex
11588             value = String(value);
11589             if(value.length == 0){
11590                 return false;
11591             }
11592             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11593         }
11594         return function(r){
11595             return value.test(r.data[property]);
11596         };
11597     },
11598
11599     /**
11600      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11601      * @param {String} property A field on your records
11602      * @param {Number} start The record index to start at (defaults to 0)
11603      * @param {Number} end The last record index to include (defaults to length - 1)
11604      * @return {Number} The sum
11605      */
11606     sum : function(property, start, end){
11607         var rs = this.data.items, v = 0;
11608         start = start || 0;
11609         end = (end || end === 0) ? end : rs.length-1;
11610
11611         for(var i = start; i <= end; i++){
11612             v += (rs[i].data[property] || 0);
11613         }
11614         return v;
11615     },
11616
11617     /**
11618      * Filter the records by a specified property.
11619      * @param {String} field A field on your records
11620      * @param {String/RegExp} value Either a string that the field
11621      * should start with or a RegExp to test against the field
11622      * @param {Boolean} anyMatch True to match any part not just the beginning
11623      */
11624     filter : function(property, value, anyMatch){
11625         var fn = this.createFilterFn(property, value, anyMatch);
11626         return fn ? this.filterBy(fn) : this.clearFilter();
11627     },
11628
11629     /**
11630      * Filter by a function. The specified function will be called with each
11631      * record in this data source. If the function returns true the record is included,
11632      * otherwise it is filtered.
11633      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11634      * @param {Object} scope (optional) The scope of the function (defaults to this)
11635      */
11636     filterBy : function(fn, scope){
11637         this.snapshot = this.snapshot || this.data;
11638         this.data = this.queryBy(fn, scope||this);
11639         this.fireEvent("datachanged", this);
11640     },
11641
11642     /**
11643      * Query the records by a specified property.
11644      * @param {String} field A field on your records
11645      * @param {String/RegExp} value Either a string that the field
11646      * should start with or a RegExp to test against the field
11647      * @param {Boolean} anyMatch True to match any part not just the beginning
11648      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11649      */
11650     query : function(property, value, anyMatch){
11651         var fn = this.createFilterFn(property, value, anyMatch);
11652         return fn ? this.queryBy(fn) : this.data.clone();
11653     },
11654
11655     /**
11656      * Query by a function. The specified function will be called with each
11657      * record in this data source. If the function returns true the record is included
11658      * in the results.
11659      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11660      * @param {Object} scope (optional) The scope of the function (defaults to this)
11661       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11662      **/
11663     queryBy : function(fn, scope){
11664         var data = this.snapshot || this.data;
11665         return data.filterBy(fn, scope||this);
11666     },
11667
11668     /**
11669      * Collects unique values for a particular dataIndex from this store.
11670      * @param {String} dataIndex The property to collect
11671      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11672      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11673      * @return {Array} An array of the unique values
11674      **/
11675     collect : function(dataIndex, allowNull, bypassFilter){
11676         var d = (bypassFilter === true && this.snapshot) ?
11677                 this.snapshot.items : this.data.items;
11678         var v, sv, r = [], l = {};
11679         for(var i = 0, len = d.length; i < len; i++){
11680             v = d[i].data[dataIndex];
11681             sv = String(v);
11682             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11683                 l[sv] = true;
11684                 r[r.length] = v;
11685             }
11686         }
11687         return r;
11688     },
11689
11690     /**
11691      * Revert to a view of the Record cache with no filtering applied.
11692      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11693      */
11694     clearFilter : function(suppressEvent){
11695         if(this.snapshot && this.snapshot != this.data){
11696             this.data = this.snapshot;
11697             delete this.snapshot;
11698             if(suppressEvent !== true){
11699                 this.fireEvent("datachanged", this);
11700             }
11701         }
11702     },
11703
11704     // private
11705     afterEdit : function(record){
11706         if(this.modified.indexOf(record) == -1){
11707             this.modified.push(record);
11708         }
11709         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11710     },
11711     
11712     // private
11713     afterReject : function(record){
11714         this.modified.remove(record);
11715         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11716     },
11717
11718     // private
11719     afterCommit : function(record){
11720         this.modified.remove(record);
11721         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11722     },
11723
11724     /**
11725      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11726      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11727      */
11728     commitChanges : function(){
11729         var m = this.modified.slice(0);
11730         this.modified = [];
11731         for(var i = 0, len = m.length; i < len; i++){
11732             m[i].commit();
11733         }
11734     },
11735
11736     /**
11737      * Cancel outstanding changes on all changed records.
11738      */
11739     rejectChanges : function(){
11740         var m = this.modified.slice(0);
11741         this.modified = [];
11742         for(var i = 0, len = m.length; i < len; i++){
11743             m[i].reject();
11744         }
11745     },
11746
11747     onMetaChange : function(meta, rtype, o){
11748         this.recordType = rtype;
11749         this.fields = rtype.prototype.fields;
11750         delete this.snapshot;
11751         this.sortInfo = meta.sortInfo || this.sortInfo;
11752         this.modified = [];
11753         this.fireEvent('metachange', this, this.reader.meta);
11754     },
11755     
11756     moveIndex : function(data, type)
11757     {
11758         var index = this.indexOf(data);
11759         
11760         var newIndex = index + type;
11761         
11762         this.remove(data);
11763         
11764         this.insert(newIndex, data);
11765         
11766     }
11767 });/*
11768  * Based on:
11769  * Ext JS Library 1.1.1
11770  * Copyright(c) 2006-2007, Ext JS, LLC.
11771  *
11772  * Originally Released Under LGPL - original licence link has changed is not relivant.
11773  *
11774  * Fork - LGPL
11775  * <script type="text/javascript">
11776  */
11777
11778 /**
11779  * @class Roo.data.SimpleStore
11780  * @extends Roo.data.Store
11781  * Small helper class to make creating Stores from Array data easier.
11782  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11783  * @cfg {Array} fields An array of field definition objects, or field name strings.
11784  * @cfg {Array} data The multi-dimensional array of data
11785  * @constructor
11786  * @param {Object} config
11787  */
11788 Roo.data.SimpleStore = function(config){
11789     Roo.data.SimpleStore.superclass.constructor.call(this, {
11790         isLocal : true,
11791         reader: new Roo.data.ArrayReader({
11792                 id: config.id
11793             },
11794             Roo.data.Record.create(config.fields)
11795         ),
11796         proxy : new Roo.data.MemoryProxy(config.data)
11797     });
11798     this.load();
11799 };
11800 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11801  * Based on:
11802  * Ext JS Library 1.1.1
11803  * Copyright(c) 2006-2007, Ext JS, LLC.
11804  *
11805  * Originally Released Under LGPL - original licence link has changed is not relivant.
11806  *
11807  * Fork - LGPL
11808  * <script type="text/javascript">
11809  */
11810
11811 /**
11812 /**
11813  * @extends Roo.data.Store
11814  * @class Roo.data.JsonStore
11815  * Small helper class to make creating Stores for JSON data easier. <br/>
11816 <pre><code>
11817 var store = new Roo.data.JsonStore({
11818     url: 'get-images.php',
11819     root: 'images',
11820     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11821 });
11822 </code></pre>
11823  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11824  * JsonReader and HttpProxy (unless inline data is provided).</b>
11825  * @cfg {Array} fields An array of field definition objects, or field name strings.
11826  * @constructor
11827  * @param {Object} config
11828  */
11829 Roo.data.JsonStore = function(c){
11830     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11831         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11832         reader: new Roo.data.JsonReader(c, c.fields)
11833     }));
11834 };
11835 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11836  * Based on:
11837  * Ext JS Library 1.1.1
11838  * Copyright(c) 2006-2007, Ext JS, LLC.
11839  *
11840  * Originally Released Under LGPL - original licence link has changed is not relivant.
11841  *
11842  * Fork - LGPL
11843  * <script type="text/javascript">
11844  */
11845
11846  
11847 Roo.data.Field = function(config){
11848     if(typeof config == "string"){
11849         config = {name: config};
11850     }
11851     Roo.apply(this, config);
11852     
11853     if(!this.type){
11854         this.type = "auto";
11855     }
11856     
11857     var st = Roo.data.SortTypes;
11858     // named sortTypes are supported, here we look them up
11859     if(typeof this.sortType == "string"){
11860         this.sortType = st[this.sortType];
11861     }
11862     
11863     // set default sortType for strings and dates
11864     if(!this.sortType){
11865         switch(this.type){
11866             case "string":
11867                 this.sortType = st.asUCString;
11868                 break;
11869             case "date":
11870                 this.sortType = st.asDate;
11871                 break;
11872             default:
11873                 this.sortType = st.none;
11874         }
11875     }
11876
11877     // define once
11878     var stripRe = /[\$,%]/g;
11879
11880     // prebuilt conversion function for this field, instead of
11881     // switching every time we're reading a value
11882     if(!this.convert){
11883         var cv, dateFormat = this.dateFormat;
11884         switch(this.type){
11885             case "":
11886             case "auto":
11887             case undefined:
11888                 cv = function(v){ return v; };
11889                 break;
11890             case "string":
11891                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11892                 break;
11893             case "int":
11894                 cv = function(v){
11895                     return v !== undefined && v !== null && v !== '' ?
11896                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11897                     };
11898                 break;
11899             case "float":
11900                 cv = function(v){
11901                     return v !== undefined && v !== null && v !== '' ?
11902                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11903                     };
11904                 break;
11905             case "bool":
11906             case "boolean":
11907                 cv = function(v){ return v === true || v === "true" || v == 1; };
11908                 break;
11909             case "date":
11910                 cv = function(v){
11911                     if(!v){
11912                         return '';
11913                     }
11914                     if(v instanceof Date){
11915                         return v;
11916                     }
11917                     if(dateFormat){
11918                         if(dateFormat == "timestamp"){
11919                             return new Date(v*1000);
11920                         }
11921                         return Date.parseDate(v, dateFormat);
11922                     }
11923                     var parsed = Date.parse(v);
11924                     return parsed ? new Date(parsed) : null;
11925                 };
11926              break;
11927             
11928         }
11929         this.convert = cv;
11930     }
11931 };
11932
11933 Roo.data.Field.prototype = {
11934     dateFormat: null,
11935     defaultValue: "",
11936     mapping: null,
11937     sortType : null,
11938     sortDir : "ASC"
11939 };/*
11940  * Based on:
11941  * Ext JS Library 1.1.1
11942  * Copyright(c) 2006-2007, Ext JS, LLC.
11943  *
11944  * Originally Released Under LGPL - original licence link has changed is not relivant.
11945  *
11946  * Fork - LGPL
11947  * <script type="text/javascript">
11948  */
11949  
11950 // Base class for reading structured data from a data source.  This class is intended to be
11951 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11952
11953 /**
11954  * @class Roo.data.DataReader
11955  * Base class for reading structured data from a data source.  This class is intended to be
11956  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11957  */
11958
11959 Roo.data.DataReader = function(meta, recordType){
11960     
11961     this.meta = meta;
11962     
11963     this.recordType = recordType instanceof Array ? 
11964         Roo.data.Record.create(recordType) : recordType;
11965 };
11966
11967 Roo.data.DataReader.prototype = {
11968      /**
11969      * Create an empty record
11970      * @param {Object} data (optional) - overlay some values
11971      * @return {Roo.data.Record} record created.
11972      */
11973     newRow :  function(d) {
11974         var da =  {};
11975         this.recordType.prototype.fields.each(function(c) {
11976             switch( c.type) {
11977                 case 'int' : da[c.name] = 0; break;
11978                 case 'date' : da[c.name] = new Date(); break;
11979                 case 'float' : da[c.name] = 0.0; break;
11980                 case 'boolean' : da[c.name] = false; break;
11981                 default : da[c.name] = ""; break;
11982             }
11983             
11984         });
11985         return new this.recordType(Roo.apply(da, d));
11986     }
11987     
11988 };/*
11989  * Based on:
11990  * Ext JS Library 1.1.1
11991  * Copyright(c) 2006-2007, Ext JS, LLC.
11992  *
11993  * Originally Released Under LGPL - original licence link has changed is not relivant.
11994  *
11995  * Fork - LGPL
11996  * <script type="text/javascript">
11997  */
11998
11999 /**
12000  * @class Roo.data.DataProxy
12001  * @extends Roo.data.Observable
12002  * This class is an abstract base class for implementations which provide retrieval of
12003  * unformatted data objects.<br>
12004  * <p>
12005  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12006  * (of the appropriate type which knows how to parse the data object) to provide a block of
12007  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12008  * <p>
12009  * Custom implementations must implement the load method as described in
12010  * {@link Roo.data.HttpProxy#load}.
12011  */
12012 Roo.data.DataProxy = function(){
12013     this.addEvents({
12014         /**
12015          * @event beforeload
12016          * Fires before a network request is made to retrieve a data object.
12017          * @param {Object} This DataProxy object.
12018          * @param {Object} params The params parameter to the load function.
12019          */
12020         beforeload : true,
12021         /**
12022          * @event load
12023          * Fires before the load method's callback is called.
12024          * @param {Object} This DataProxy object.
12025          * @param {Object} o The data object.
12026          * @param {Object} arg The callback argument object passed to the load function.
12027          */
12028         load : true,
12029         /**
12030          * @event loadexception
12031          * Fires if an Exception occurs during data retrieval.
12032          * @param {Object} This DataProxy object.
12033          * @param {Object} o The data object.
12034          * @param {Object} arg The callback argument object passed to the load function.
12035          * @param {Object} e The Exception.
12036          */
12037         loadexception : true
12038     });
12039     Roo.data.DataProxy.superclass.constructor.call(this);
12040 };
12041
12042 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12043
12044     /**
12045      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12046      */
12047 /*
12048  * Based on:
12049  * Ext JS Library 1.1.1
12050  * Copyright(c) 2006-2007, Ext JS, LLC.
12051  *
12052  * Originally Released Under LGPL - original licence link has changed is not relivant.
12053  *
12054  * Fork - LGPL
12055  * <script type="text/javascript">
12056  */
12057 /**
12058  * @class Roo.data.MemoryProxy
12059  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12060  * to the Reader when its load method is called.
12061  * @constructor
12062  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12063  */
12064 Roo.data.MemoryProxy = function(data){
12065     if (data.data) {
12066         data = data.data;
12067     }
12068     Roo.data.MemoryProxy.superclass.constructor.call(this);
12069     this.data = data;
12070 };
12071
12072 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12073     
12074     /**
12075      * Load data from the requested source (in this case an in-memory
12076      * data object passed to the constructor), read the data object into
12077      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12078      * process that block using the passed callback.
12079      * @param {Object} params This parameter is not used by the MemoryProxy class.
12080      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12081      * object into a block of Roo.data.Records.
12082      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12083      * The function must be passed <ul>
12084      * <li>The Record block object</li>
12085      * <li>The "arg" argument from the load function</li>
12086      * <li>A boolean success indicator</li>
12087      * </ul>
12088      * @param {Object} scope The scope in which to call the callback
12089      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12090      */
12091     load : function(params, reader, callback, scope, arg){
12092         params = params || {};
12093         var result;
12094         try {
12095             result = reader.readRecords(this.data);
12096         }catch(e){
12097             this.fireEvent("loadexception", this, arg, null, e);
12098             callback.call(scope, null, arg, false);
12099             return;
12100         }
12101         callback.call(scope, result, arg, true);
12102     },
12103     
12104     // private
12105     update : function(params, records){
12106         
12107     }
12108 });/*
12109  * Based on:
12110  * Ext JS Library 1.1.1
12111  * Copyright(c) 2006-2007, Ext JS, LLC.
12112  *
12113  * Originally Released Under LGPL - original licence link has changed is not relivant.
12114  *
12115  * Fork - LGPL
12116  * <script type="text/javascript">
12117  */
12118 /**
12119  * @class Roo.data.HttpProxy
12120  * @extends Roo.data.DataProxy
12121  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12122  * configured to reference a certain URL.<br><br>
12123  * <p>
12124  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12125  * from which the running page was served.<br><br>
12126  * <p>
12127  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12128  * <p>
12129  * Be aware that to enable the browser to parse an XML document, the server must set
12130  * the Content-Type header in the HTTP response to "text/xml".
12131  * @constructor
12132  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12133  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12134  * will be used to make the request.
12135  */
12136 Roo.data.HttpProxy = function(conn){
12137     Roo.data.HttpProxy.superclass.constructor.call(this);
12138     // is conn a conn config or a real conn?
12139     this.conn = conn;
12140     this.useAjax = !conn || !conn.events;
12141   
12142 };
12143
12144 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12145     // thse are take from connection...
12146     
12147     /**
12148      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12149      */
12150     /**
12151      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12152      * extra parameters to each request made by this object. (defaults to undefined)
12153      */
12154     /**
12155      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12156      *  to each request made by this object. (defaults to undefined)
12157      */
12158     /**
12159      * @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)
12160      */
12161     /**
12162      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12163      */
12164      /**
12165      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12166      * @type Boolean
12167      */
12168   
12169
12170     /**
12171      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12172      * @type Boolean
12173      */
12174     /**
12175      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12176      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12177      * a finer-grained basis than the DataProxy events.
12178      */
12179     getConnection : function(){
12180         return this.useAjax ? Roo.Ajax : this.conn;
12181     },
12182
12183     /**
12184      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12185      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12186      * process that block using the passed callback.
12187      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12188      * for the request to the remote server.
12189      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12190      * object into a block of Roo.data.Records.
12191      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12192      * The function must be passed <ul>
12193      * <li>The Record block object</li>
12194      * <li>The "arg" argument from the load function</li>
12195      * <li>A boolean success indicator</li>
12196      * </ul>
12197      * @param {Object} scope The scope in which to call the callback
12198      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12199      */
12200     load : function(params, reader, callback, scope, arg){
12201         if(this.fireEvent("beforeload", this, params) !== false){
12202             var  o = {
12203                 params : params || {},
12204                 request: {
12205                     callback : callback,
12206                     scope : scope,
12207                     arg : arg
12208                 },
12209                 reader: reader,
12210                 callback : this.loadResponse,
12211                 scope: this
12212             };
12213             if(this.useAjax){
12214                 Roo.applyIf(o, this.conn);
12215                 if(this.activeRequest){
12216                     Roo.Ajax.abort(this.activeRequest);
12217                 }
12218                 this.activeRequest = Roo.Ajax.request(o);
12219             }else{
12220                 this.conn.request(o);
12221             }
12222         }else{
12223             callback.call(scope||this, null, arg, false);
12224         }
12225     },
12226
12227     // private
12228     loadResponse : function(o, success, response){
12229         delete this.activeRequest;
12230         if(!success){
12231             this.fireEvent("loadexception", this, o, response);
12232             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12233             return;
12234         }
12235         var result;
12236         try {
12237             result = o.reader.read(response);
12238         }catch(e){
12239             this.fireEvent("loadexception", this, o, response, e);
12240             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12241             return;
12242         }
12243         
12244         this.fireEvent("load", this, o, o.request.arg);
12245         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12246     },
12247
12248     // private
12249     update : function(dataSet){
12250
12251     },
12252
12253     // private
12254     updateResponse : function(dataSet){
12255
12256     }
12257 });/*
12258  * Based on:
12259  * Ext JS Library 1.1.1
12260  * Copyright(c) 2006-2007, Ext JS, LLC.
12261  *
12262  * Originally Released Under LGPL - original licence link has changed is not relivant.
12263  *
12264  * Fork - LGPL
12265  * <script type="text/javascript">
12266  */
12267
12268 /**
12269  * @class Roo.data.ScriptTagProxy
12270  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12271  * other than the originating domain of the running page.<br><br>
12272  * <p>
12273  * <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
12274  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12275  * <p>
12276  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12277  * source code that is used as the source inside a &lt;script> tag.<br><br>
12278  * <p>
12279  * In order for the browser to process the returned data, the server must wrap the data object
12280  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12281  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12282  * depending on whether the callback name was passed:
12283  * <p>
12284  * <pre><code>
12285 boolean scriptTag = false;
12286 String cb = request.getParameter("callback");
12287 if (cb != null) {
12288     scriptTag = true;
12289     response.setContentType("text/javascript");
12290 } else {
12291     response.setContentType("application/x-json");
12292 }
12293 Writer out = response.getWriter();
12294 if (scriptTag) {
12295     out.write(cb + "(");
12296 }
12297 out.print(dataBlock.toJsonString());
12298 if (scriptTag) {
12299     out.write(");");
12300 }
12301 </pre></code>
12302  *
12303  * @constructor
12304  * @param {Object} config A configuration object.
12305  */
12306 Roo.data.ScriptTagProxy = function(config){
12307     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12308     Roo.apply(this, config);
12309     this.head = document.getElementsByTagName("head")[0];
12310 };
12311
12312 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12313
12314 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12315     /**
12316      * @cfg {String} url The URL from which to request the data object.
12317      */
12318     /**
12319      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12320      */
12321     timeout : 30000,
12322     /**
12323      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12324      * the server the name of the callback function set up by the load call to process the returned data object.
12325      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12326      * javascript output which calls this named function passing the data object as its only parameter.
12327      */
12328     callbackParam : "callback",
12329     /**
12330      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12331      * name to the request.
12332      */
12333     nocache : true,
12334
12335     /**
12336      * Load data from the configured URL, read the data object into
12337      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12338      * process that block using the passed callback.
12339      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12340      * for the request to the remote server.
12341      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12342      * object into a block of Roo.data.Records.
12343      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12344      * The function must be passed <ul>
12345      * <li>The Record block object</li>
12346      * <li>The "arg" argument from the load function</li>
12347      * <li>A boolean success indicator</li>
12348      * </ul>
12349      * @param {Object} scope The scope in which to call the callback
12350      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12351      */
12352     load : function(params, reader, callback, scope, arg){
12353         if(this.fireEvent("beforeload", this, params) !== false){
12354
12355             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12356
12357             var url = this.url;
12358             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12359             if(this.nocache){
12360                 url += "&_dc=" + (new Date().getTime());
12361             }
12362             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12363             var trans = {
12364                 id : transId,
12365                 cb : "stcCallback"+transId,
12366                 scriptId : "stcScript"+transId,
12367                 params : params,
12368                 arg : arg,
12369                 url : url,
12370                 callback : callback,
12371                 scope : scope,
12372                 reader : reader
12373             };
12374             var conn = this;
12375
12376             window[trans.cb] = function(o){
12377                 conn.handleResponse(o, trans);
12378             };
12379
12380             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12381
12382             if(this.autoAbort !== false){
12383                 this.abort();
12384             }
12385
12386             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12387
12388             var script = document.createElement("script");
12389             script.setAttribute("src", url);
12390             script.setAttribute("type", "text/javascript");
12391             script.setAttribute("id", trans.scriptId);
12392             this.head.appendChild(script);
12393
12394             this.trans = trans;
12395         }else{
12396             callback.call(scope||this, null, arg, false);
12397         }
12398     },
12399
12400     // private
12401     isLoading : function(){
12402         return this.trans ? true : false;
12403     },
12404
12405     /**
12406      * Abort the current server request.
12407      */
12408     abort : function(){
12409         if(this.isLoading()){
12410             this.destroyTrans(this.trans);
12411         }
12412     },
12413
12414     // private
12415     destroyTrans : function(trans, isLoaded){
12416         this.head.removeChild(document.getElementById(trans.scriptId));
12417         clearTimeout(trans.timeoutId);
12418         if(isLoaded){
12419             window[trans.cb] = undefined;
12420             try{
12421                 delete window[trans.cb];
12422             }catch(e){}
12423         }else{
12424             // if hasn't been loaded, wait for load to remove it to prevent script error
12425             window[trans.cb] = function(){
12426                 window[trans.cb] = undefined;
12427                 try{
12428                     delete window[trans.cb];
12429                 }catch(e){}
12430             };
12431         }
12432     },
12433
12434     // private
12435     handleResponse : function(o, trans){
12436         this.trans = false;
12437         this.destroyTrans(trans, true);
12438         var result;
12439         try {
12440             result = trans.reader.readRecords(o);
12441         }catch(e){
12442             this.fireEvent("loadexception", this, o, trans.arg, e);
12443             trans.callback.call(trans.scope||window, null, trans.arg, false);
12444             return;
12445         }
12446         this.fireEvent("load", this, o, trans.arg);
12447         trans.callback.call(trans.scope||window, result, trans.arg, true);
12448     },
12449
12450     // private
12451     handleFailure : function(trans){
12452         this.trans = false;
12453         this.destroyTrans(trans, false);
12454         this.fireEvent("loadexception", this, null, trans.arg);
12455         trans.callback.call(trans.scope||window, null, trans.arg, false);
12456     }
12457 });/*
12458  * Based on:
12459  * Ext JS Library 1.1.1
12460  * Copyright(c) 2006-2007, Ext JS, LLC.
12461  *
12462  * Originally Released Under LGPL - original licence link has changed is not relivant.
12463  *
12464  * Fork - LGPL
12465  * <script type="text/javascript">
12466  */
12467
12468 /**
12469  * @class Roo.data.JsonReader
12470  * @extends Roo.data.DataReader
12471  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12472  * based on mappings in a provided Roo.data.Record constructor.
12473  * 
12474  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12475  * in the reply previously. 
12476  * 
12477  * <p>
12478  * Example code:
12479  * <pre><code>
12480 var RecordDef = Roo.data.Record.create([
12481     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12482     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12483 ]);
12484 var myReader = new Roo.data.JsonReader({
12485     totalProperty: "results",    // The property which contains the total dataset size (optional)
12486     root: "rows",                // The property which contains an Array of row objects
12487     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12488 }, RecordDef);
12489 </code></pre>
12490  * <p>
12491  * This would consume a JSON file like this:
12492  * <pre><code>
12493 { 'results': 2, 'rows': [
12494     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12495     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12496 }
12497 </code></pre>
12498  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12499  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12500  * paged from the remote server.
12501  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12502  * @cfg {String} root name of the property which contains the Array of row objects.
12503  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12504  * @cfg {Array} fields Array of field definition objects
12505  * @constructor
12506  * Create a new JsonReader
12507  * @param {Object} meta Metadata configuration options
12508  * @param {Object} recordType Either an Array of field definition objects,
12509  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12510  */
12511 Roo.data.JsonReader = function(meta, recordType){
12512     
12513     meta = meta || {};
12514     // set some defaults:
12515     Roo.applyIf(meta, {
12516         totalProperty: 'total',
12517         successProperty : 'success',
12518         root : 'data',
12519         id : 'id'
12520     });
12521     
12522     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12523 };
12524 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12525     
12526     /**
12527      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12528      * Used by Store query builder to append _requestMeta to params.
12529      * 
12530      */
12531     metaFromRemote : false,
12532     /**
12533      * This method is only used by a DataProxy which has retrieved data from a remote server.
12534      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12535      * @return {Object} data A data block which is used by an Roo.data.Store object as
12536      * a cache of Roo.data.Records.
12537      */
12538     read : function(response){
12539         var json = response.responseText;
12540        
12541         var o = /* eval:var:o */ eval("("+json+")");
12542         if(!o) {
12543             throw {message: "JsonReader.read: Json object not found"};
12544         }
12545         
12546         if(o.metaData){
12547             
12548             delete this.ef;
12549             this.metaFromRemote = true;
12550             this.meta = o.metaData;
12551             this.recordType = Roo.data.Record.create(o.metaData.fields);
12552             this.onMetaChange(this.meta, this.recordType, o);
12553         }
12554         return this.readRecords(o);
12555     },
12556
12557     // private function a store will implement
12558     onMetaChange : function(meta, recordType, o){
12559
12560     },
12561
12562     /**
12563          * @ignore
12564          */
12565     simpleAccess: function(obj, subsc) {
12566         return obj[subsc];
12567     },
12568
12569         /**
12570          * @ignore
12571          */
12572     getJsonAccessor: function(){
12573         var re = /[\[\.]/;
12574         return function(expr) {
12575             try {
12576                 return(re.test(expr))
12577                     ? new Function("obj", "return obj." + expr)
12578                     : function(obj){
12579                         return obj[expr];
12580                     };
12581             } catch(e){}
12582             return Roo.emptyFn;
12583         };
12584     }(),
12585
12586     /**
12587      * Create a data block containing Roo.data.Records from an XML document.
12588      * @param {Object} o An object which contains an Array of row objects in the property specified
12589      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12590      * which contains the total size of the dataset.
12591      * @return {Object} data A data block which is used by an Roo.data.Store object as
12592      * a cache of Roo.data.Records.
12593      */
12594     readRecords : function(o){
12595         /**
12596          * After any data loads, the raw JSON data is available for further custom processing.
12597          * @type Object
12598          */
12599         this.o = o;
12600         var s = this.meta, Record = this.recordType,
12601             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12602
12603 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12604         if (!this.ef) {
12605             if(s.totalProperty) {
12606                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12607                 }
12608                 if(s.successProperty) {
12609                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12610                 }
12611                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12612                 if (s.id) {
12613                         var g = this.getJsonAccessor(s.id);
12614                         this.getId = function(rec) {
12615                                 var r = g(rec);  
12616                                 return (r === undefined || r === "") ? null : r;
12617                         };
12618                 } else {
12619                         this.getId = function(){return null;};
12620                 }
12621             this.ef = [];
12622             for(var jj = 0; jj < fl; jj++){
12623                 f = fi[jj];
12624                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12625                 this.ef[jj] = this.getJsonAccessor(map);
12626             }
12627         }
12628
12629         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12630         if(s.totalProperty){
12631             var vt = parseInt(this.getTotal(o), 10);
12632             if(!isNaN(vt)){
12633                 totalRecords = vt;
12634             }
12635         }
12636         if(s.successProperty){
12637             var vs = this.getSuccess(o);
12638             if(vs === false || vs === 'false'){
12639                 success = false;
12640             }
12641         }
12642         var records = [];
12643         for(var i = 0; i < c; i++){
12644                 var n = root[i];
12645             var values = {};
12646             var id = this.getId(n);
12647             for(var j = 0; j < fl; j++){
12648                 f = fi[j];
12649             var v = this.ef[j](n);
12650             if (!f.convert) {
12651                 Roo.log('missing convert for ' + f.name);
12652                 Roo.log(f);
12653                 continue;
12654             }
12655             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12656             }
12657             var record = new Record(values, id);
12658             record.json = n;
12659             records[i] = record;
12660         }
12661         return {
12662             raw : o,
12663             success : success,
12664             records : records,
12665             totalRecords : totalRecords
12666         };
12667     }
12668 });/*
12669  * Based on:
12670  * Ext JS Library 1.1.1
12671  * Copyright(c) 2006-2007, Ext JS, LLC.
12672  *
12673  * Originally Released Under LGPL - original licence link has changed is not relivant.
12674  *
12675  * Fork - LGPL
12676  * <script type="text/javascript">
12677  */
12678
12679 /**
12680  * @class Roo.data.ArrayReader
12681  * @extends Roo.data.DataReader
12682  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12683  * Each element of that Array represents a row of data fields. The
12684  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12685  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12686  * <p>
12687  * Example code:.
12688  * <pre><code>
12689 var RecordDef = Roo.data.Record.create([
12690     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12691     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12692 ]);
12693 var myReader = new Roo.data.ArrayReader({
12694     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12695 }, RecordDef);
12696 </code></pre>
12697  * <p>
12698  * This would consume an Array like this:
12699  * <pre><code>
12700 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12701   </code></pre>
12702  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12703  * @constructor
12704  * Create a new JsonReader
12705  * @param {Object} meta Metadata configuration options.
12706  * @param {Object} recordType Either an Array of field definition objects
12707  * as specified to {@link Roo.data.Record#create},
12708  * or an {@link Roo.data.Record} object
12709  * created using {@link Roo.data.Record#create}.
12710  */
12711 Roo.data.ArrayReader = function(meta, recordType){
12712     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12713 };
12714
12715 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12716     /**
12717      * Create a data block containing Roo.data.Records from an XML document.
12718      * @param {Object} o An Array of row objects which represents the dataset.
12719      * @return {Object} data A data block which is used by an Roo.data.Store object as
12720      * a cache of Roo.data.Records.
12721      */
12722     readRecords : function(o){
12723         var sid = this.meta ? this.meta.id : null;
12724         var recordType = this.recordType, fields = recordType.prototype.fields;
12725         var records = [];
12726         var root = o;
12727             for(var i = 0; i < root.length; i++){
12728                     var n = root[i];
12729                 var values = {};
12730                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12731                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12732                 var f = fields.items[j];
12733                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12734                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12735                 v = f.convert(v);
12736                 values[f.name] = v;
12737             }
12738                 var record = new recordType(values, id);
12739                 record.json = n;
12740                 records[records.length] = record;
12741             }
12742             return {
12743                 records : records,
12744                 totalRecords : records.length
12745             };
12746     }
12747 });/*
12748  * - LGPL
12749  * * 
12750  */
12751
12752 /**
12753  * @class Roo.bootstrap.ComboBox
12754  * @extends Roo.bootstrap.TriggerField
12755  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12756  * @cfg {Boolean} append (true|false) default false
12757  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12758  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12759  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12760  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12761  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12762  * @cfg {Boolean} animate default true
12763  * @cfg {Boolean} emptyResultText only for touch device
12764  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12765  * @cfg {String} emptyTitle default ''
12766  * @constructor
12767  * Create a new ComboBox.
12768  * @param {Object} config Configuration options
12769  */
12770 Roo.bootstrap.ComboBox = function(config){
12771     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12772     this.addEvents({
12773         /**
12774          * @event expand
12775          * Fires when the dropdown list is expanded
12776         * @param {Roo.bootstrap.ComboBox} combo This combo box
12777         */
12778         'expand' : true,
12779         /**
12780          * @event collapse
12781          * Fires when the dropdown list is collapsed
12782         * @param {Roo.bootstrap.ComboBox} combo This combo box
12783         */
12784         'collapse' : true,
12785         /**
12786          * @event beforeselect
12787          * Fires before a list item is selected. Return false to cancel the selection.
12788         * @param {Roo.bootstrap.ComboBox} combo This combo box
12789         * @param {Roo.data.Record} record The data record returned from the underlying store
12790         * @param {Number} index The index of the selected item in the dropdown list
12791         */
12792         'beforeselect' : true,
12793         /**
12794          * @event select
12795          * Fires when a list item is selected
12796         * @param {Roo.bootstrap.ComboBox} combo This combo box
12797         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12798         * @param {Number} index The index of the selected item in the dropdown list
12799         */
12800         'select' : true,
12801         /**
12802          * @event beforequery
12803          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12804          * The event object passed has these properties:
12805         * @param {Roo.bootstrap.ComboBox} combo This combo box
12806         * @param {String} query The query
12807         * @param {Boolean} forceAll true to force "all" query
12808         * @param {Boolean} cancel true to cancel the query
12809         * @param {Object} e The query event object
12810         */
12811         'beforequery': true,
12812          /**
12813          * @event add
12814          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12815         * @param {Roo.bootstrap.ComboBox} combo This combo box
12816         */
12817         'add' : true,
12818         /**
12819          * @event edit
12820          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12821         * @param {Roo.bootstrap.ComboBox} combo This combo box
12822         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12823         */
12824         'edit' : true,
12825         /**
12826          * @event remove
12827          * Fires when the remove value from the combobox array
12828         * @param {Roo.bootstrap.ComboBox} combo This combo box
12829         */
12830         'remove' : true,
12831         /**
12832          * @event afterremove
12833          * Fires when the remove value from the combobox array
12834         * @param {Roo.bootstrap.ComboBox} combo This combo box
12835         */
12836         'afterremove' : true,
12837         /**
12838          * @event specialfilter
12839          * Fires when specialfilter
12840             * @param {Roo.bootstrap.ComboBox} combo This combo box
12841             */
12842         'specialfilter' : true,
12843         /**
12844          * @event tick
12845          * Fires when tick the element
12846             * @param {Roo.bootstrap.ComboBox} combo This combo box
12847             */
12848         'tick' : true,
12849         /**
12850          * @event touchviewdisplay
12851          * Fires when touch view require special display (default is using displayField)
12852             * @param {Roo.bootstrap.ComboBox} combo This combo box
12853             * @param {Object} cfg set html .
12854             */
12855         'touchviewdisplay' : true
12856         
12857     });
12858     
12859     this.item = [];
12860     this.tickItems = [];
12861     
12862     this.selectedIndex = -1;
12863     if(this.mode == 'local'){
12864         if(config.queryDelay === undefined){
12865             this.queryDelay = 10;
12866         }
12867         if(config.minChars === undefined){
12868             this.minChars = 0;
12869         }
12870     }
12871 };
12872
12873 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12874      
12875     /**
12876      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12877      * rendering into an Roo.Editor, defaults to false)
12878      */
12879     /**
12880      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12881      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12882      */
12883     /**
12884      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12885      */
12886     /**
12887      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12888      * the dropdown list (defaults to undefined, with no header element)
12889      */
12890
12891      /**
12892      * @cfg {String/Roo.Template} tpl The template to use to render the output
12893      */
12894      
12895      /**
12896      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12897      */
12898     listWidth: undefined,
12899     /**
12900      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12901      * mode = 'remote' or 'text' if mode = 'local')
12902      */
12903     displayField: undefined,
12904     
12905     /**
12906      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12907      * mode = 'remote' or 'value' if mode = 'local'). 
12908      * Note: use of a valueField requires the user make a selection
12909      * in order for a value to be mapped.
12910      */
12911     valueField: undefined,
12912     /**
12913      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12914      */
12915     modalTitle : '',
12916     
12917     /**
12918      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12919      * field's data value (defaults to the underlying DOM element's name)
12920      */
12921     hiddenName: undefined,
12922     /**
12923      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12924      */
12925     listClass: '',
12926     /**
12927      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12928      */
12929     selectedClass: 'active',
12930     
12931     /**
12932      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12933      */
12934     shadow:'sides',
12935     /**
12936      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12937      * anchor positions (defaults to 'tl-bl')
12938      */
12939     listAlign: 'tl-bl?',
12940     /**
12941      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12942      */
12943     maxHeight: 300,
12944     /**
12945      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12946      * query specified by the allQuery config option (defaults to 'query')
12947      */
12948     triggerAction: 'query',
12949     /**
12950      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12951      * (defaults to 4, does not apply if editable = false)
12952      */
12953     minChars : 4,
12954     /**
12955      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12956      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12957      */
12958     typeAhead: false,
12959     /**
12960      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12961      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12962      */
12963     queryDelay: 500,
12964     /**
12965      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12966      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12967      */
12968     pageSize: 0,
12969     /**
12970      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12971      * when editable = true (defaults to false)
12972      */
12973     selectOnFocus:false,
12974     /**
12975      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12976      */
12977     queryParam: 'query',
12978     /**
12979      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12980      * when mode = 'remote' (defaults to 'Loading...')
12981      */
12982     loadingText: 'Loading...',
12983     /**
12984      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12985      */
12986     resizable: false,
12987     /**
12988      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12989      */
12990     handleHeight : 8,
12991     /**
12992      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12993      * traditional select (defaults to true)
12994      */
12995     editable: true,
12996     /**
12997      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12998      */
12999     allQuery: '',
13000     /**
13001      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13002      */
13003     mode: 'remote',
13004     /**
13005      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13006      * listWidth has a higher value)
13007      */
13008     minListWidth : 70,
13009     /**
13010      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13011      * allow the user to set arbitrary text into the field (defaults to false)
13012      */
13013     forceSelection:false,
13014     /**
13015      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13016      * if typeAhead = true (defaults to 250)
13017      */
13018     typeAheadDelay : 250,
13019     /**
13020      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13021      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13022      */
13023     valueNotFoundText : undefined,
13024     /**
13025      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13026      */
13027     blockFocus : false,
13028     
13029     /**
13030      * @cfg {Boolean} disableClear Disable showing of clear button.
13031      */
13032     disableClear : false,
13033     /**
13034      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13035      */
13036     alwaysQuery : false,
13037     
13038     /**
13039      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13040      */
13041     multiple : false,
13042     
13043     /**
13044      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13045      */
13046     invalidClass : "has-warning",
13047     
13048     /**
13049      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13050      */
13051     validClass : "has-success",
13052     
13053     /**
13054      * @cfg {Boolean} specialFilter (true|false) special filter default false
13055      */
13056     specialFilter : false,
13057     
13058     /**
13059      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13060      */
13061     mobileTouchView : true,
13062     
13063     /**
13064      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13065      */
13066     useNativeIOS : false,
13067     
13068     ios_options : false,
13069     
13070     //private
13071     addicon : false,
13072     editicon: false,
13073     
13074     page: 0,
13075     hasQuery: false,
13076     append: false,
13077     loadNext: false,
13078     autoFocus : true,
13079     tickable : false,
13080     btnPosition : 'right',
13081     triggerList : true,
13082     showToggleBtn : true,
13083     animate : true,
13084     emptyResultText: 'Empty',
13085     triggerText : 'Select',
13086     emptyTitle : '',
13087     
13088     // element that contains real text value.. (when hidden is used..)
13089     
13090     getAutoCreate : function()
13091     {   
13092         var cfg = false;
13093         //render
13094         /*
13095          * Render classic select for iso
13096          */
13097         
13098         if(Roo.isIOS && this.useNativeIOS){
13099             cfg = this.getAutoCreateNativeIOS();
13100             return cfg;
13101         }
13102         
13103         /*
13104          * Touch Devices
13105          */
13106         
13107         if(Roo.isTouch && this.mobileTouchView){
13108             cfg = this.getAutoCreateTouchView();
13109             return cfg;;
13110         }
13111         
13112         /*
13113          *  Normal ComboBox
13114          */
13115         if(!this.tickable){
13116             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13117             return cfg;
13118         }
13119         
13120         /*
13121          *  ComboBox with tickable selections
13122          */
13123              
13124         var align = this.labelAlign || this.parentLabelAlign();
13125         
13126         cfg = {
13127             cls : 'form-group roo-combobox-tickable' //input-group
13128         };
13129         
13130         var btn_text_select = '';
13131         var btn_text_done = '';
13132         var btn_text_cancel = '';
13133         
13134         if (this.btn_text_show) {
13135             btn_text_select = 'Select';
13136             btn_text_done = 'Done';
13137             btn_text_cancel = 'Cancel'; 
13138         }
13139         
13140         var buttons = {
13141             tag : 'div',
13142             cls : 'tickable-buttons',
13143             cn : [
13144                 {
13145                     tag : 'button',
13146                     type : 'button',
13147                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13148                     //html : this.triggerText
13149                     html: btn_text_select
13150                 },
13151                 {
13152                     tag : 'button',
13153                     type : 'button',
13154                     name : 'ok',
13155                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13156                     //html : 'Done'
13157                     html: btn_text_done
13158                 },
13159                 {
13160                     tag : 'button',
13161                     type : 'button',
13162                     name : 'cancel',
13163                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13164                     //html : 'Cancel'
13165                     html: btn_text_cancel
13166                 }
13167             ]
13168         };
13169         
13170         if(this.editable){
13171             buttons.cn.unshift({
13172                 tag: 'input',
13173                 cls: 'roo-select2-search-field-input'
13174             });
13175         }
13176         
13177         var _this = this;
13178         
13179         Roo.each(buttons.cn, function(c){
13180             if (_this.size) {
13181                 c.cls += ' btn-' + _this.size;
13182             }
13183
13184             if (_this.disabled) {
13185                 c.disabled = true;
13186             }
13187         });
13188         
13189         var box = {
13190             tag: 'div',
13191             cn: [
13192                 {
13193                     tag: 'input',
13194                     type : 'hidden',
13195                     cls: 'form-hidden-field'
13196                 },
13197                 {
13198                     tag: 'ul',
13199                     cls: 'roo-select2-choices',
13200                     cn:[
13201                         {
13202                             tag: 'li',
13203                             cls: 'roo-select2-search-field',
13204                             cn: [
13205                                 buttons
13206                             ]
13207                         }
13208                     ]
13209                 }
13210             ]
13211         };
13212         
13213         var combobox = {
13214             cls: 'roo-select2-container input-group roo-select2-container-multi',
13215             cn: [
13216                 box
13217 //                {
13218 //                    tag: 'ul',
13219 //                    cls: 'typeahead typeahead-long dropdown-menu',
13220 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13221 //                }
13222             ]
13223         };
13224         
13225         if(this.hasFeedback && !this.allowBlank){
13226             
13227             var feedback = {
13228                 tag: 'span',
13229                 cls: 'glyphicon form-control-feedback'
13230             };
13231
13232             combobox.cn.push(feedback);
13233         }
13234         
13235         
13236         if (align ==='left' && this.fieldLabel.length) {
13237             
13238             cfg.cls += ' roo-form-group-label-left';
13239             
13240             cfg.cn = [
13241                 {
13242                     tag : 'i',
13243                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13244                     tooltip : 'This field is required'
13245                 },
13246                 {
13247                     tag: 'label',
13248                     'for' :  id,
13249                     cls : 'control-label',
13250                     html : this.fieldLabel
13251
13252                 },
13253                 {
13254                     cls : "", 
13255                     cn: [
13256                         combobox
13257                     ]
13258                 }
13259
13260             ];
13261             
13262             var labelCfg = cfg.cn[1];
13263             var contentCfg = cfg.cn[2];
13264             
13265
13266             if(this.indicatorpos == 'right'){
13267                 
13268                 cfg.cn = [
13269                     {
13270                         tag: 'label',
13271                         'for' :  id,
13272                         cls : 'control-label',
13273                         cn : [
13274                             {
13275                                 tag : 'span',
13276                                 html : this.fieldLabel
13277                             },
13278                             {
13279                                 tag : 'i',
13280                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13281                                 tooltip : 'This field is required'
13282                             }
13283                         ]
13284                     },
13285                     {
13286                         cls : "",
13287                         cn: [
13288                             combobox
13289                         ]
13290                     }
13291
13292                 ];
13293                 
13294                 
13295                 
13296                 labelCfg = cfg.cn[0];
13297                 contentCfg = cfg.cn[1];
13298             
13299             }
13300             
13301             if(this.labelWidth > 12){
13302                 labelCfg.style = "width: " + this.labelWidth + 'px';
13303             }
13304             
13305             if(this.labelWidth < 13 && this.labelmd == 0){
13306                 this.labelmd = this.labelWidth;
13307             }
13308             
13309             if(this.labellg > 0){
13310                 labelCfg.cls += ' col-lg-' + this.labellg;
13311                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13312             }
13313             
13314             if(this.labelmd > 0){
13315                 labelCfg.cls += ' col-md-' + this.labelmd;
13316                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13317             }
13318             
13319             if(this.labelsm > 0){
13320                 labelCfg.cls += ' col-sm-' + this.labelsm;
13321                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13322             }
13323             
13324             if(this.labelxs > 0){
13325                 labelCfg.cls += ' col-xs-' + this.labelxs;
13326                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13327             }
13328                 
13329                 
13330         } else if ( this.fieldLabel.length) {
13331 //                Roo.log(" label");
13332                  cfg.cn = [
13333                     {
13334                         tag : 'i',
13335                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13336                         tooltip : 'This field is required'
13337                     },
13338                     {
13339                         tag: 'label',
13340                         //cls : 'input-group-addon',
13341                         html : this.fieldLabel
13342                     },
13343                     combobox
13344                 ];
13345                 
13346                 if(this.indicatorpos == 'right'){
13347                     cfg.cn = [
13348                         {
13349                             tag: 'label',
13350                             //cls : 'input-group-addon',
13351                             html : this.fieldLabel
13352                         },
13353                         {
13354                             tag : 'i',
13355                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13356                             tooltip : 'This field is required'
13357                         },
13358                         combobox
13359                     ];
13360                     
13361                 }
13362
13363         } else {
13364             
13365 //                Roo.log(" no label && no align");
13366                 cfg = combobox
13367                      
13368                 
13369         }
13370          
13371         var settings=this;
13372         ['xs','sm','md','lg'].map(function(size){
13373             if (settings[size]) {
13374                 cfg.cls += ' col-' + size + '-' + settings[size];
13375             }
13376         });
13377         
13378         return cfg;
13379         
13380     },
13381     
13382     _initEventsCalled : false,
13383     
13384     // private
13385     initEvents: function()
13386     {   
13387         if (this._initEventsCalled) { // as we call render... prevent looping...
13388             return;
13389         }
13390         this._initEventsCalled = true;
13391         
13392         if (!this.store) {
13393             throw "can not find store for combo";
13394         }
13395         
13396         this.indicator = this.indicatorEl();
13397         
13398         this.store = Roo.factory(this.store, Roo.data);
13399         this.store.parent = this;
13400         
13401         // if we are building from html. then this element is so complex, that we can not really
13402         // use the rendered HTML.
13403         // so we have to trash and replace the previous code.
13404         if (Roo.XComponent.build_from_html) {
13405             // remove this element....
13406             var e = this.el.dom, k=0;
13407             while (e ) { e = e.previousSibling;  ++k;}
13408
13409             this.el.remove();
13410             
13411             this.el=false;
13412             this.rendered = false;
13413             
13414             this.render(this.parent().getChildContainer(true), k);
13415         }
13416         
13417         if(Roo.isIOS && this.useNativeIOS){
13418             this.initIOSView();
13419             return;
13420         }
13421         
13422         /*
13423          * Touch Devices
13424          */
13425         
13426         if(Roo.isTouch && this.mobileTouchView){
13427             this.initTouchView();
13428             return;
13429         }
13430         
13431         if(this.tickable){
13432             this.initTickableEvents();
13433             return;
13434         }
13435         
13436         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13437         
13438         if(this.hiddenName){
13439             
13440             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13441             
13442             this.hiddenField.dom.value =
13443                 this.hiddenValue !== undefined ? this.hiddenValue :
13444                 this.value !== undefined ? this.value : '';
13445
13446             // prevent input submission
13447             this.el.dom.removeAttribute('name');
13448             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13449              
13450              
13451         }
13452         //if(Roo.isGecko){
13453         //    this.el.dom.setAttribute('autocomplete', 'off');
13454         //}
13455         
13456         var cls = 'x-combo-list';
13457         
13458         //this.list = new Roo.Layer({
13459         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13460         //});
13461         
13462         var _this = this;
13463         
13464         (function(){
13465             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13466             _this.list.setWidth(lw);
13467         }).defer(100);
13468         
13469         this.list.on('mouseover', this.onViewOver, this);
13470         this.list.on('mousemove', this.onViewMove, this);
13471         this.list.on('scroll', this.onViewScroll, this);
13472         
13473         /*
13474         this.list.swallowEvent('mousewheel');
13475         this.assetHeight = 0;
13476
13477         if(this.title){
13478             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13479             this.assetHeight += this.header.getHeight();
13480         }
13481
13482         this.innerList = this.list.createChild({cls:cls+'-inner'});
13483         this.innerList.on('mouseover', this.onViewOver, this);
13484         this.innerList.on('mousemove', this.onViewMove, this);
13485         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13486         
13487         if(this.allowBlank && !this.pageSize && !this.disableClear){
13488             this.footer = this.list.createChild({cls:cls+'-ft'});
13489             this.pageTb = new Roo.Toolbar(this.footer);
13490            
13491         }
13492         if(this.pageSize){
13493             this.footer = this.list.createChild({cls:cls+'-ft'});
13494             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13495                     {pageSize: this.pageSize});
13496             
13497         }
13498         
13499         if (this.pageTb && this.allowBlank && !this.disableClear) {
13500             var _this = this;
13501             this.pageTb.add(new Roo.Toolbar.Fill(), {
13502                 cls: 'x-btn-icon x-btn-clear',
13503                 text: '&#160;',
13504                 handler: function()
13505                 {
13506                     _this.collapse();
13507                     _this.clearValue();
13508                     _this.onSelect(false, -1);
13509                 }
13510             });
13511         }
13512         if (this.footer) {
13513             this.assetHeight += this.footer.getHeight();
13514         }
13515         */
13516             
13517         if(!this.tpl){
13518             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13519         }
13520
13521         this.view = new Roo.View(this.list, this.tpl, {
13522             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13523         });
13524         //this.view.wrapEl.setDisplayed(false);
13525         this.view.on('click', this.onViewClick, this);
13526         
13527         
13528         this.store.on('beforeload', this.onBeforeLoad, this);
13529         this.store.on('load', this.onLoad, this);
13530         this.store.on('loadexception', this.onLoadException, this);
13531         /*
13532         if(this.resizable){
13533             this.resizer = new Roo.Resizable(this.list,  {
13534                pinned:true, handles:'se'
13535             });
13536             this.resizer.on('resize', function(r, w, h){
13537                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13538                 this.listWidth = w;
13539                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13540                 this.restrictHeight();
13541             }, this);
13542             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13543         }
13544         */
13545         if(!this.editable){
13546             this.editable = true;
13547             this.setEditable(false);
13548         }
13549         
13550         /*
13551         
13552         if (typeof(this.events.add.listeners) != 'undefined') {
13553             
13554             this.addicon = this.wrap.createChild(
13555                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13556        
13557             this.addicon.on('click', function(e) {
13558                 this.fireEvent('add', this);
13559             }, this);
13560         }
13561         if (typeof(this.events.edit.listeners) != 'undefined') {
13562             
13563             this.editicon = this.wrap.createChild(
13564                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13565             if (this.addicon) {
13566                 this.editicon.setStyle('margin-left', '40px');
13567             }
13568             this.editicon.on('click', function(e) {
13569                 
13570                 // we fire even  if inothing is selected..
13571                 this.fireEvent('edit', this, this.lastData );
13572                 
13573             }, this);
13574         }
13575         */
13576         
13577         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13578             "up" : function(e){
13579                 this.inKeyMode = true;
13580                 this.selectPrev();
13581             },
13582
13583             "down" : function(e){
13584                 if(!this.isExpanded()){
13585                     this.onTriggerClick();
13586                 }else{
13587                     this.inKeyMode = true;
13588                     this.selectNext();
13589                 }
13590             },
13591
13592             "enter" : function(e){
13593 //                this.onViewClick();
13594                 //return true;
13595                 this.collapse();
13596                 
13597                 if(this.fireEvent("specialkey", this, e)){
13598                     this.onViewClick(false);
13599                 }
13600                 
13601                 return true;
13602             },
13603
13604             "esc" : function(e){
13605                 this.collapse();
13606             },
13607
13608             "tab" : function(e){
13609                 this.collapse();
13610                 
13611                 if(this.fireEvent("specialkey", this, e)){
13612                     this.onViewClick(false);
13613                 }
13614                 
13615                 return true;
13616             },
13617
13618             scope : this,
13619
13620             doRelay : function(foo, bar, hname){
13621                 if(hname == 'down' || this.scope.isExpanded()){
13622                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13623                 }
13624                 return true;
13625             },
13626
13627             forceKeyDown: true
13628         });
13629         
13630         
13631         this.queryDelay = Math.max(this.queryDelay || 10,
13632                 this.mode == 'local' ? 10 : 250);
13633         
13634         
13635         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13636         
13637         if(this.typeAhead){
13638             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13639         }
13640         if(this.editable !== false){
13641             this.inputEl().on("keyup", this.onKeyUp, this);
13642         }
13643         if(this.forceSelection){
13644             this.inputEl().on('blur', this.doForce, this);
13645         }
13646         
13647         if(this.multiple){
13648             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13649             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13650         }
13651     },
13652     
13653     initTickableEvents: function()
13654     {   
13655         this.createList();
13656         
13657         if(this.hiddenName){
13658             
13659             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13660             
13661             this.hiddenField.dom.value =
13662                 this.hiddenValue !== undefined ? this.hiddenValue :
13663                 this.value !== undefined ? this.value : '';
13664
13665             // prevent input submission
13666             this.el.dom.removeAttribute('name');
13667             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13668              
13669              
13670         }
13671         
13672 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13673         
13674         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13675         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13676         if(this.triggerList){
13677             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13678         }
13679          
13680         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13681         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13682         
13683         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13684         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13685         
13686         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13687         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13688         
13689         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13690         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13691         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13692         
13693         this.okBtn.hide();
13694         this.cancelBtn.hide();
13695         
13696         var _this = this;
13697         
13698         (function(){
13699             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13700             _this.list.setWidth(lw);
13701         }).defer(100);
13702         
13703         this.list.on('mouseover', this.onViewOver, this);
13704         this.list.on('mousemove', this.onViewMove, this);
13705         
13706         this.list.on('scroll', this.onViewScroll, this);
13707         
13708         if(!this.tpl){
13709             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13710                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13711         }
13712
13713         this.view = new Roo.View(this.list, this.tpl, {
13714             singleSelect:true,
13715             tickable:true,
13716             parent:this,
13717             store: this.store,
13718             selectedClass: this.selectedClass
13719         });
13720         
13721         //this.view.wrapEl.setDisplayed(false);
13722         this.view.on('click', this.onViewClick, this);
13723         
13724         
13725         
13726         this.store.on('beforeload', this.onBeforeLoad, this);
13727         this.store.on('load', this.onLoad, this);
13728         this.store.on('loadexception', this.onLoadException, this);
13729         
13730         if(this.editable){
13731             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13732                 "up" : function(e){
13733                     this.inKeyMode = true;
13734                     this.selectPrev();
13735                 },
13736
13737                 "down" : function(e){
13738                     this.inKeyMode = true;
13739                     this.selectNext();
13740                 },
13741
13742                 "enter" : function(e){
13743                     if(this.fireEvent("specialkey", this, e)){
13744                         this.onViewClick(false);
13745                     }
13746                     
13747                     return true;
13748                 },
13749
13750                 "esc" : function(e){
13751                     this.onTickableFooterButtonClick(e, false, false);
13752                 },
13753
13754                 "tab" : function(e){
13755                     this.fireEvent("specialkey", this, e);
13756                     
13757                     this.onTickableFooterButtonClick(e, false, false);
13758                     
13759                     return true;
13760                 },
13761
13762                 scope : this,
13763
13764                 doRelay : function(e, fn, key){
13765                     if(this.scope.isExpanded()){
13766                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13767                     }
13768                     return true;
13769                 },
13770
13771                 forceKeyDown: true
13772             });
13773         }
13774         
13775         this.queryDelay = Math.max(this.queryDelay || 10,
13776                 this.mode == 'local' ? 10 : 250);
13777         
13778         
13779         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13780         
13781         if(this.typeAhead){
13782             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13783         }
13784         
13785         if(this.editable !== false){
13786             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13787         }
13788         
13789         this.indicator = this.indicatorEl();
13790         
13791         if(this.indicator){
13792             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13793             this.indicator.hide();
13794         }
13795         
13796     },
13797
13798     onDestroy : function(){
13799         if(this.view){
13800             this.view.setStore(null);
13801             this.view.el.removeAllListeners();
13802             this.view.el.remove();
13803             this.view.purgeListeners();
13804         }
13805         if(this.list){
13806             this.list.dom.innerHTML  = '';
13807         }
13808         
13809         if(this.store){
13810             this.store.un('beforeload', this.onBeforeLoad, this);
13811             this.store.un('load', this.onLoad, this);
13812             this.store.un('loadexception', this.onLoadException, this);
13813         }
13814         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13815     },
13816
13817     // private
13818     fireKey : function(e){
13819         if(e.isNavKeyPress() && !this.list.isVisible()){
13820             this.fireEvent("specialkey", this, e);
13821         }
13822     },
13823
13824     // private
13825     onResize: function(w, h){
13826 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13827 //        
13828 //        if(typeof w != 'number'){
13829 //            // we do not handle it!?!?
13830 //            return;
13831 //        }
13832 //        var tw = this.trigger.getWidth();
13833 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13834 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13835 //        var x = w - tw;
13836 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13837 //            
13838 //        //this.trigger.setStyle('left', x+'px');
13839 //        
13840 //        if(this.list && this.listWidth === undefined){
13841 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13842 //            this.list.setWidth(lw);
13843 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13844 //        }
13845         
13846     
13847         
13848     },
13849
13850     /**
13851      * Allow or prevent the user from directly editing the field text.  If false is passed,
13852      * the user will only be able to select from the items defined in the dropdown list.  This method
13853      * is the runtime equivalent of setting the 'editable' config option at config time.
13854      * @param {Boolean} value True to allow the user to directly edit the field text
13855      */
13856     setEditable : function(value){
13857         if(value == this.editable){
13858             return;
13859         }
13860         this.editable = value;
13861         if(!value){
13862             this.inputEl().dom.setAttribute('readOnly', true);
13863             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13864             this.inputEl().addClass('x-combo-noedit');
13865         }else{
13866             this.inputEl().dom.setAttribute('readOnly', false);
13867             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13868             this.inputEl().removeClass('x-combo-noedit');
13869         }
13870     },
13871
13872     // private
13873     
13874     onBeforeLoad : function(combo,opts){
13875         if(!this.hasFocus){
13876             return;
13877         }
13878          if (!opts.add) {
13879             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13880          }
13881         this.restrictHeight();
13882         this.selectedIndex = -1;
13883     },
13884
13885     // private
13886     onLoad : function(){
13887         
13888         this.hasQuery = false;
13889         
13890         if(!this.hasFocus){
13891             return;
13892         }
13893         
13894         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13895             this.loading.hide();
13896         }
13897         
13898         if(this.store.getCount() > 0){
13899             
13900             this.expand();
13901             this.restrictHeight();
13902             if(this.lastQuery == this.allQuery){
13903                 if(this.editable && !this.tickable){
13904                     this.inputEl().dom.select();
13905                 }
13906                 
13907                 if(
13908                     !this.selectByValue(this.value, true) &&
13909                     this.autoFocus && 
13910                     (
13911                         !this.store.lastOptions ||
13912                         typeof(this.store.lastOptions.add) == 'undefined' || 
13913                         this.store.lastOptions.add != true
13914                     )
13915                 ){
13916                     this.select(0, true);
13917                 }
13918             }else{
13919                 if(this.autoFocus){
13920                     this.selectNext();
13921                 }
13922                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13923                     this.taTask.delay(this.typeAheadDelay);
13924                 }
13925             }
13926         }else{
13927             this.onEmptyResults();
13928         }
13929         
13930         //this.el.focus();
13931     },
13932     // private
13933     onLoadException : function()
13934     {
13935         this.hasQuery = false;
13936         
13937         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13938             this.loading.hide();
13939         }
13940         
13941         if(this.tickable && this.editable){
13942             return;
13943         }
13944         
13945         this.collapse();
13946         // only causes errors at present
13947         //Roo.log(this.store.reader.jsonData);
13948         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13949             // fixme
13950             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13951         //}
13952         
13953         
13954     },
13955     // private
13956     onTypeAhead : function(){
13957         if(this.store.getCount() > 0){
13958             var r = this.store.getAt(0);
13959             var newValue = r.data[this.displayField];
13960             var len = newValue.length;
13961             var selStart = this.getRawValue().length;
13962             
13963             if(selStart != len){
13964                 this.setRawValue(newValue);
13965                 this.selectText(selStart, newValue.length);
13966             }
13967         }
13968     },
13969
13970     // private
13971     onSelect : function(record, index){
13972         
13973         if(this.fireEvent('beforeselect', this, record, index) !== false){
13974         
13975             this.setFromData(index > -1 ? record.data : false);
13976             
13977             this.collapse();
13978             this.fireEvent('select', this, record, index);
13979         }
13980     },
13981
13982     /**
13983      * Returns the currently selected field value or empty string if no value is set.
13984      * @return {String} value The selected value
13985      */
13986     getValue : function()
13987     {
13988         if(Roo.isIOS && this.useNativeIOS){
13989             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13990         }
13991         
13992         if(this.multiple){
13993             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13994         }
13995         
13996         if(this.valueField){
13997             return typeof this.value != 'undefined' ? this.value : '';
13998         }else{
13999             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14000         }
14001     },
14002     
14003     getRawValue : function()
14004     {
14005         if(Roo.isIOS && this.useNativeIOS){
14006             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14007         }
14008         
14009         var v = this.inputEl().getValue();
14010         
14011         return v;
14012     },
14013
14014     /**
14015      * Clears any text/value currently set in the field
14016      */
14017     clearValue : function(){
14018         
14019         if(this.hiddenField){
14020             this.hiddenField.dom.value = '';
14021         }
14022         this.value = '';
14023         this.setRawValue('');
14024         this.lastSelectionText = '';
14025         this.lastData = false;
14026         
14027         var close = this.closeTriggerEl();
14028         
14029         if(close){
14030             close.hide();
14031         }
14032         
14033         this.validate();
14034         
14035     },
14036
14037     /**
14038      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14039      * will be displayed in the field.  If the value does not match the data value of an existing item,
14040      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14041      * Otherwise the field will be blank (although the value will still be set).
14042      * @param {String} value The value to match
14043      */
14044     setValue : function(v)
14045     {
14046         if(Roo.isIOS && this.useNativeIOS){
14047             this.setIOSValue(v);
14048             return;
14049         }
14050         
14051         if(this.multiple){
14052             this.syncValue();
14053             return;
14054         }
14055         
14056         var text = v;
14057         if(this.valueField){
14058             var r = this.findRecord(this.valueField, v);
14059             if(r){
14060                 text = r.data[this.displayField];
14061             }else if(this.valueNotFoundText !== undefined){
14062                 text = this.valueNotFoundText;
14063             }
14064         }
14065         this.lastSelectionText = text;
14066         if(this.hiddenField){
14067             this.hiddenField.dom.value = v;
14068         }
14069         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14070         this.value = v;
14071         
14072         var close = this.closeTriggerEl();
14073         
14074         if(close){
14075             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14076         }
14077         
14078         this.validate();
14079     },
14080     /**
14081      * @property {Object} the last set data for the element
14082      */
14083     
14084     lastData : false,
14085     /**
14086      * Sets the value of the field based on a object which is related to the record format for the store.
14087      * @param {Object} value the value to set as. or false on reset?
14088      */
14089     setFromData : function(o){
14090         
14091         if(this.multiple){
14092             this.addItem(o);
14093             return;
14094         }
14095             
14096         var dv = ''; // display value
14097         var vv = ''; // value value..
14098         this.lastData = o;
14099         if (this.displayField) {
14100             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14101         } else {
14102             // this is an error condition!!!
14103             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14104         }
14105         
14106         if(this.valueField){
14107             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14108         }
14109         
14110         var close = this.closeTriggerEl();
14111         
14112         if(close){
14113             if(dv.length || vv * 1 > 0){
14114                 close.show() ;
14115                 this.blockFocus=true;
14116             } else {
14117                 close.hide();
14118             }             
14119         }
14120         
14121         if(this.hiddenField){
14122             this.hiddenField.dom.value = vv;
14123             
14124             this.lastSelectionText = dv;
14125             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14126             this.value = vv;
14127             return;
14128         }
14129         // no hidden field.. - we store the value in 'value', but still display
14130         // display field!!!!
14131         this.lastSelectionText = dv;
14132         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14133         this.value = vv;
14134         
14135         
14136         
14137     },
14138     // private
14139     reset : function(){
14140         // overridden so that last data is reset..
14141         
14142         if(this.multiple){
14143             this.clearItem();
14144             return;
14145         }
14146         
14147         this.setValue(this.originalValue);
14148         //this.clearInvalid();
14149         this.lastData = false;
14150         if (this.view) {
14151             this.view.clearSelections();
14152         }
14153         
14154         this.validate();
14155     },
14156     // private
14157     findRecord : function(prop, value){
14158         var record;
14159         if(this.store.getCount() > 0){
14160             this.store.each(function(r){
14161                 if(r.data[prop] == value){
14162                     record = r;
14163                     return false;
14164                 }
14165                 return true;
14166             });
14167         }
14168         return record;
14169     },
14170     
14171     getName: function()
14172     {
14173         // returns hidden if it's set..
14174         if (!this.rendered) {return ''};
14175         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14176         
14177     },
14178     // private
14179     onViewMove : function(e, t){
14180         this.inKeyMode = false;
14181     },
14182
14183     // private
14184     onViewOver : function(e, t){
14185         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14186             return;
14187         }
14188         var item = this.view.findItemFromChild(t);
14189         
14190         if(item){
14191             var index = this.view.indexOf(item);
14192             this.select(index, false);
14193         }
14194     },
14195
14196     // private
14197     onViewClick : function(view, doFocus, el, e)
14198     {
14199         var index = this.view.getSelectedIndexes()[0];
14200         
14201         var r = this.store.getAt(index);
14202         
14203         if(this.tickable){
14204             
14205             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14206                 return;
14207             }
14208             
14209             var rm = false;
14210             var _this = this;
14211             
14212             Roo.each(this.tickItems, function(v,k){
14213                 
14214                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14215                     Roo.log(v);
14216                     _this.tickItems.splice(k, 1);
14217                     
14218                     if(typeof(e) == 'undefined' && view == false){
14219                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14220                     }
14221                     
14222                     rm = true;
14223                     return;
14224                 }
14225             });
14226             
14227             if(rm){
14228                 return;
14229             }
14230             
14231             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14232                 this.tickItems.push(r.data);
14233             }
14234             
14235             if(typeof(e) == 'undefined' && view == false){
14236                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14237             }
14238                     
14239             return;
14240         }
14241         
14242         if(r){
14243             this.onSelect(r, index);
14244         }
14245         if(doFocus !== false && !this.blockFocus){
14246             this.inputEl().focus();
14247         }
14248     },
14249
14250     // private
14251     restrictHeight : function(){
14252         //this.innerList.dom.style.height = '';
14253         //var inner = this.innerList.dom;
14254         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14255         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14256         //this.list.beginUpdate();
14257         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14258         this.list.alignTo(this.inputEl(), this.listAlign);
14259         this.list.alignTo(this.inputEl(), this.listAlign);
14260         //this.list.endUpdate();
14261     },
14262
14263     // private
14264     onEmptyResults : function(){
14265         
14266         if(this.tickable && this.editable){
14267             this.hasFocus = false;
14268             this.restrictHeight();
14269             return;
14270         }
14271         
14272         this.collapse();
14273     },
14274
14275     /**
14276      * Returns true if the dropdown list is expanded, else false.
14277      */
14278     isExpanded : function(){
14279         return this.list.isVisible();
14280     },
14281
14282     /**
14283      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14284      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14285      * @param {String} value The data value of the item to select
14286      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14287      * selected item if it is not currently in view (defaults to true)
14288      * @return {Boolean} True if the value matched an item in the list, else false
14289      */
14290     selectByValue : function(v, scrollIntoView){
14291         if(v !== undefined && v !== null){
14292             var r = this.findRecord(this.valueField || this.displayField, v);
14293             if(r){
14294                 this.select(this.store.indexOf(r), scrollIntoView);
14295                 return true;
14296             }
14297         }
14298         return false;
14299     },
14300
14301     /**
14302      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14303      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14304      * @param {Number} index The zero-based index of the list item to select
14305      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14306      * selected item if it is not currently in view (defaults to true)
14307      */
14308     select : function(index, scrollIntoView){
14309         this.selectedIndex = index;
14310         this.view.select(index);
14311         if(scrollIntoView !== false){
14312             var el = this.view.getNode(index);
14313             /*
14314              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14315              */
14316             if(el){
14317                 this.list.scrollChildIntoView(el, false);
14318             }
14319         }
14320     },
14321
14322     // private
14323     selectNext : 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 < ct-1){
14329                 this.select(this.selectedIndex+1);
14330             }
14331         }
14332     },
14333
14334     // private
14335     selectPrev : function(){
14336         var ct = this.store.getCount();
14337         if(ct > 0){
14338             if(this.selectedIndex == -1){
14339                 this.select(0);
14340             }else if(this.selectedIndex != 0){
14341                 this.select(this.selectedIndex-1);
14342             }
14343         }
14344     },
14345
14346     // private
14347     onKeyUp : function(e){
14348         if(this.editable !== false && !e.isSpecialKey()){
14349             this.lastKey = e.getKey();
14350             this.dqTask.delay(this.queryDelay);
14351         }
14352     },
14353
14354     // private
14355     validateBlur : function(){
14356         return !this.list || !this.list.isVisible();   
14357     },
14358
14359     // private
14360     initQuery : function(){
14361         
14362         var v = this.getRawValue();
14363         
14364         if(this.tickable && this.editable){
14365             v = this.tickableInputEl().getValue();
14366         }
14367         
14368         this.doQuery(v);
14369     },
14370
14371     // private
14372     doForce : function(){
14373         if(this.inputEl().dom.value.length > 0){
14374             this.inputEl().dom.value =
14375                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14376              
14377         }
14378     },
14379
14380     /**
14381      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14382      * query allowing the query action to be canceled if needed.
14383      * @param {String} query The SQL query to execute
14384      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14385      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14386      * saved in the current store (defaults to false)
14387      */
14388     doQuery : function(q, forceAll){
14389         
14390         if(q === undefined || q === null){
14391             q = '';
14392         }
14393         var qe = {
14394             query: q,
14395             forceAll: forceAll,
14396             combo: this,
14397             cancel:false
14398         };
14399         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14400             return false;
14401         }
14402         q = qe.query;
14403         
14404         forceAll = qe.forceAll;
14405         if(forceAll === true || (q.length >= this.minChars)){
14406             
14407             this.hasQuery = true;
14408             
14409             if(this.lastQuery != q || this.alwaysQuery){
14410                 this.lastQuery = q;
14411                 if(this.mode == 'local'){
14412                     this.selectedIndex = -1;
14413                     if(forceAll){
14414                         this.store.clearFilter();
14415                     }else{
14416                         
14417                         if(this.specialFilter){
14418                             this.fireEvent('specialfilter', this);
14419                             this.onLoad();
14420                             return;
14421                         }
14422                         
14423                         this.store.filter(this.displayField, q);
14424                     }
14425                     
14426                     this.store.fireEvent("datachanged", this.store);
14427                     
14428                     this.onLoad();
14429                     
14430                     
14431                 }else{
14432                     
14433                     this.store.baseParams[this.queryParam] = q;
14434                     
14435                     var options = {params : this.getParams(q)};
14436                     
14437                     if(this.loadNext){
14438                         options.add = true;
14439                         options.params.start = this.page * this.pageSize;
14440                     }
14441                     
14442                     this.store.load(options);
14443                     
14444                     /*
14445                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14446                      *  we should expand the list on onLoad
14447                      *  so command out it
14448                      */
14449 //                    this.expand();
14450                 }
14451             }else{
14452                 this.selectedIndex = -1;
14453                 this.onLoad();   
14454             }
14455         }
14456         
14457         this.loadNext = false;
14458     },
14459     
14460     // private
14461     getParams : function(q){
14462         var p = {};
14463         //p[this.queryParam] = q;
14464         
14465         if(this.pageSize){
14466             p.start = 0;
14467             p.limit = this.pageSize;
14468         }
14469         return p;
14470     },
14471
14472     /**
14473      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14474      */
14475     collapse : function(){
14476         if(!this.isExpanded()){
14477             return;
14478         }
14479         
14480         this.list.hide();
14481         
14482         this.hasFocus = false;
14483         
14484         if(this.tickable){
14485             this.okBtn.hide();
14486             this.cancelBtn.hide();
14487             this.trigger.show();
14488             
14489             if(this.editable){
14490                 this.tickableInputEl().dom.value = '';
14491                 this.tickableInputEl().blur();
14492             }
14493             
14494         }
14495         
14496         Roo.get(document).un('mousedown', this.collapseIf, this);
14497         Roo.get(document).un('mousewheel', this.collapseIf, this);
14498         if (!this.editable) {
14499             Roo.get(document).un('keydown', this.listKeyPress, this);
14500         }
14501         this.fireEvent('collapse', this);
14502         
14503         this.validate();
14504     },
14505
14506     // private
14507     collapseIf : function(e){
14508         var in_combo  = e.within(this.el);
14509         var in_list =  e.within(this.list);
14510         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14511         
14512         if (in_combo || in_list || is_list) {
14513             //e.stopPropagation();
14514             return;
14515         }
14516         
14517         if(this.tickable){
14518             this.onTickableFooterButtonClick(e, false, false);
14519         }
14520
14521         this.collapse();
14522         
14523     },
14524
14525     /**
14526      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14527      */
14528     expand : function(){
14529        
14530         if(this.isExpanded() || !this.hasFocus){
14531             return;
14532         }
14533         
14534         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14535         this.list.setWidth(lw);
14536         
14537         Roo.log('expand');
14538         
14539         this.list.show();
14540         
14541         this.restrictHeight();
14542         
14543         if(this.tickable){
14544             
14545             this.tickItems = Roo.apply([], this.item);
14546             
14547             this.okBtn.show();
14548             this.cancelBtn.show();
14549             this.trigger.hide();
14550             
14551             if(this.editable){
14552                 this.tickableInputEl().focus();
14553             }
14554             
14555         }
14556         
14557         Roo.get(document).on('mousedown', this.collapseIf, this);
14558         Roo.get(document).on('mousewheel', this.collapseIf, this);
14559         if (!this.editable) {
14560             Roo.get(document).on('keydown', this.listKeyPress, this);
14561         }
14562         
14563         this.fireEvent('expand', this);
14564     },
14565
14566     // private
14567     // Implements the default empty TriggerField.onTriggerClick function
14568     onTriggerClick : function(e)
14569     {
14570         Roo.log('trigger click');
14571         
14572         if(this.disabled || !this.triggerList){
14573             return;
14574         }
14575         
14576         this.page = 0;
14577         this.loadNext = false;
14578         
14579         if(this.isExpanded()){
14580             this.collapse();
14581             if (!this.blockFocus) {
14582                 this.inputEl().focus();
14583             }
14584             
14585         }else {
14586             this.hasFocus = true;
14587             if(this.triggerAction == 'all') {
14588                 this.doQuery(this.allQuery, true);
14589             } else {
14590                 this.doQuery(this.getRawValue());
14591             }
14592             if (!this.blockFocus) {
14593                 this.inputEl().focus();
14594             }
14595         }
14596     },
14597     
14598     onTickableTriggerClick : function(e)
14599     {
14600         if(this.disabled){
14601             return;
14602         }
14603         
14604         this.page = 0;
14605         this.loadNext = false;
14606         this.hasFocus = true;
14607         
14608         if(this.triggerAction == 'all') {
14609             this.doQuery(this.allQuery, true);
14610         } else {
14611             this.doQuery(this.getRawValue());
14612         }
14613     },
14614     
14615     onSearchFieldClick : function(e)
14616     {
14617         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14618             this.onTickableFooterButtonClick(e, false, false);
14619             return;
14620         }
14621         
14622         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14623             return;
14624         }
14625         
14626         this.page = 0;
14627         this.loadNext = false;
14628         this.hasFocus = true;
14629         
14630         if(this.triggerAction == 'all') {
14631             this.doQuery(this.allQuery, true);
14632         } else {
14633             this.doQuery(this.getRawValue());
14634         }
14635     },
14636     
14637     listKeyPress : function(e)
14638     {
14639         //Roo.log('listkeypress');
14640         // scroll to first matching element based on key pres..
14641         if (e.isSpecialKey()) {
14642             return false;
14643         }
14644         var k = String.fromCharCode(e.getKey()).toUpperCase();
14645         //Roo.log(k);
14646         var match  = false;
14647         var csel = this.view.getSelectedNodes();
14648         var cselitem = false;
14649         if (csel.length) {
14650             var ix = this.view.indexOf(csel[0]);
14651             cselitem  = this.store.getAt(ix);
14652             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14653                 cselitem = false;
14654             }
14655             
14656         }
14657         
14658         this.store.each(function(v) { 
14659             if (cselitem) {
14660                 // start at existing selection.
14661                 if (cselitem.id == v.id) {
14662                     cselitem = false;
14663                 }
14664                 return true;
14665             }
14666                 
14667             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14668                 match = this.store.indexOf(v);
14669                 return false;
14670             }
14671             return true;
14672         }, this);
14673         
14674         if (match === false) {
14675             return true; // no more action?
14676         }
14677         // scroll to?
14678         this.view.select(match);
14679         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14680         sn.scrollIntoView(sn.dom.parentNode, false);
14681     },
14682     
14683     onViewScroll : function(e, t){
14684         
14685         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){
14686             return;
14687         }
14688         
14689         this.hasQuery = true;
14690         
14691         this.loading = this.list.select('.loading', true).first();
14692         
14693         if(this.loading === null){
14694             this.list.createChild({
14695                 tag: 'div',
14696                 cls: 'loading roo-select2-more-results roo-select2-active',
14697                 html: 'Loading more results...'
14698             });
14699             
14700             this.loading = this.list.select('.loading', true).first();
14701             
14702             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14703             
14704             this.loading.hide();
14705         }
14706         
14707         this.loading.show();
14708         
14709         var _combo = this;
14710         
14711         this.page++;
14712         this.loadNext = true;
14713         
14714         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14715         
14716         return;
14717     },
14718     
14719     addItem : function(o)
14720     {   
14721         var dv = ''; // display value
14722         
14723         if (this.displayField) {
14724             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14725         } else {
14726             // this is an error condition!!!
14727             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14728         }
14729         
14730         if(!dv.length){
14731             return;
14732         }
14733         
14734         var choice = this.choices.createChild({
14735             tag: 'li',
14736             cls: 'roo-select2-search-choice',
14737             cn: [
14738                 {
14739                     tag: 'div',
14740                     html: dv
14741                 },
14742                 {
14743                     tag: 'a',
14744                     href: '#',
14745                     cls: 'roo-select2-search-choice-close fa fa-times',
14746                     tabindex: '-1'
14747                 }
14748             ]
14749             
14750         }, this.searchField);
14751         
14752         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14753         
14754         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14755         
14756         this.item.push(o);
14757         
14758         this.lastData = o;
14759         
14760         this.syncValue();
14761         
14762         this.inputEl().dom.value = '';
14763         
14764         this.validate();
14765     },
14766     
14767     onRemoveItem : function(e, _self, o)
14768     {
14769         e.preventDefault();
14770         
14771         this.lastItem = Roo.apply([], this.item);
14772         
14773         var index = this.item.indexOf(o.data) * 1;
14774         
14775         if( index < 0){
14776             Roo.log('not this item?!');
14777             return;
14778         }
14779         
14780         this.item.splice(index, 1);
14781         o.item.remove();
14782         
14783         this.syncValue();
14784         
14785         this.fireEvent('remove', this, e);
14786         
14787         this.validate();
14788         
14789     },
14790     
14791     syncValue : function()
14792     {
14793         if(!this.item.length){
14794             this.clearValue();
14795             return;
14796         }
14797             
14798         var value = [];
14799         var _this = this;
14800         Roo.each(this.item, function(i){
14801             if(_this.valueField){
14802                 value.push(i[_this.valueField]);
14803                 return;
14804             }
14805
14806             value.push(i);
14807         });
14808
14809         this.value = value.join(',');
14810
14811         if(this.hiddenField){
14812             this.hiddenField.dom.value = this.value;
14813         }
14814         
14815         this.store.fireEvent("datachanged", this.store);
14816         
14817         this.validate();
14818     },
14819     
14820     clearItem : function()
14821     {
14822         if(!this.multiple){
14823             return;
14824         }
14825         
14826         this.item = [];
14827         
14828         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14829            c.remove();
14830         });
14831         
14832         this.syncValue();
14833         
14834         this.validate();
14835         
14836         if(this.tickable && !Roo.isTouch){
14837             this.view.refresh();
14838         }
14839     },
14840     
14841     inputEl: function ()
14842     {
14843         if(Roo.isIOS && this.useNativeIOS){
14844             return this.el.select('select.roo-ios-select', true).first();
14845         }
14846         
14847         if(Roo.isTouch && this.mobileTouchView){
14848             return this.el.select('input.form-control',true).first();
14849         }
14850         
14851         if(this.tickable){
14852             return this.searchField;
14853         }
14854         
14855         return this.el.select('input.form-control',true).first();
14856     },
14857     
14858     onTickableFooterButtonClick : function(e, btn, el)
14859     {
14860         e.preventDefault();
14861         
14862         this.lastItem = Roo.apply([], this.item);
14863         
14864         if(btn && btn.name == 'cancel'){
14865             this.tickItems = Roo.apply([], this.item);
14866             this.collapse();
14867             return;
14868         }
14869         
14870         this.clearItem();
14871         
14872         var _this = this;
14873         
14874         Roo.each(this.tickItems, function(o){
14875             _this.addItem(o);
14876         });
14877         
14878         this.collapse();
14879         
14880     },
14881     
14882     validate : function()
14883     {
14884         if(this.getVisibilityEl().hasClass('hidden')){
14885             return true;
14886         }
14887         
14888         var v = this.getRawValue();
14889         
14890         if(this.multiple){
14891             v = this.getValue();
14892         }
14893         
14894         if(this.disabled || this.allowBlank || v.length){
14895             this.markValid();
14896             return true;
14897         }
14898         
14899         this.markInvalid();
14900         return false;
14901     },
14902     
14903     tickableInputEl : function()
14904     {
14905         if(!this.tickable || !this.editable){
14906             return this.inputEl();
14907         }
14908         
14909         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14910     },
14911     
14912     
14913     getAutoCreateTouchView : function()
14914     {
14915         var id = Roo.id();
14916         
14917         var cfg = {
14918             cls: 'form-group' //input-group
14919         };
14920         
14921         var input =  {
14922             tag: 'input',
14923             id : id,
14924             type : this.inputType,
14925             cls : 'form-control x-combo-noedit',
14926             autocomplete: 'new-password',
14927             placeholder : this.placeholder || '',
14928             readonly : true
14929         };
14930         
14931         if (this.name) {
14932             input.name = this.name;
14933         }
14934         
14935         if (this.size) {
14936             input.cls += ' input-' + this.size;
14937         }
14938         
14939         if (this.disabled) {
14940             input.disabled = true;
14941         }
14942         
14943         var inputblock = {
14944             cls : '',
14945             cn : [
14946                 input
14947             ]
14948         };
14949         
14950         if(this.before){
14951             inputblock.cls += ' input-group';
14952             
14953             inputblock.cn.unshift({
14954                 tag :'span',
14955                 cls : 'input-group-addon',
14956                 html : this.before
14957             });
14958         }
14959         
14960         if(this.removable && !this.multiple){
14961             inputblock.cls += ' roo-removable';
14962             
14963             inputblock.cn.push({
14964                 tag: 'button',
14965                 html : 'x',
14966                 cls : 'roo-combo-removable-btn close'
14967             });
14968         }
14969
14970         if(this.hasFeedback && !this.allowBlank){
14971             
14972             inputblock.cls += ' has-feedback';
14973             
14974             inputblock.cn.push({
14975                 tag: 'span',
14976                 cls: 'glyphicon form-control-feedback'
14977             });
14978             
14979         }
14980         
14981         if (this.after) {
14982             
14983             inputblock.cls += (this.before) ? '' : ' input-group';
14984             
14985             inputblock.cn.push({
14986                 tag :'span',
14987                 cls : 'input-group-addon',
14988                 html : this.after
14989             });
14990         }
14991
14992         var box = {
14993             tag: 'div',
14994             cn: [
14995                 {
14996                     tag: 'input',
14997                     type : 'hidden',
14998                     cls: 'form-hidden-field'
14999                 },
15000                 inputblock
15001             ]
15002             
15003         };
15004         
15005         if(this.multiple){
15006             box = {
15007                 tag: 'div',
15008                 cn: [
15009                     {
15010                         tag: 'input',
15011                         type : 'hidden',
15012                         cls: 'form-hidden-field'
15013                     },
15014                     {
15015                         tag: 'ul',
15016                         cls: 'roo-select2-choices',
15017                         cn:[
15018                             {
15019                                 tag: 'li',
15020                                 cls: 'roo-select2-search-field',
15021                                 cn: [
15022
15023                                     inputblock
15024                                 ]
15025                             }
15026                         ]
15027                     }
15028                 ]
15029             }
15030         };
15031         
15032         var combobox = {
15033             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15034             cn: [
15035                 box
15036             ]
15037         };
15038         
15039         if(!this.multiple && this.showToggleBtn){
15040             
15041             var caret = {
15042                         tag: 'span',
15043                         cls: 'caret'
15044             };
15045             
15046             if (this.caret != false) {
15047                 caret = {
15048                      tag: 'i',
15049                      cls: 'fa fa-' + this.caret
15050                 };
15051                 
15052             }
15053             
15054             combobox.cn.push({
15055                 tag :'span',
15056                 cls : 'input-group-addon btn dropdown-toggle',
15057                 cn : [
15058                     caret,
15059                     {
15060                         tag: 'span',
15061                         cls: 'combobox-clear',
15062                         cn  : [
15063                             {
15064                                 tag : 'i',
15065                                 cls: 'icon-remove'
15066                             }
15067                         ]
15068                     }
15069                 ]
15070
15071             })
15072         }
15073         
15074         if(this.multiple){
15075             combobox.cls += ' roo-select2-container-multi';
15076         }
15077         
15078         var align = this.labelAlign || this.parentLabelAlign();
15079         
15080         if (align ==='left' && this.fieldLabel.length) {
15081
15082             cfg.cn = [
15083                 {
15084                    tag : 'i',
15085                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15086                    tooltip : 'This field is required'
15087                 },
15088                 {
15089                     tag: 'label',
15090                     cls : 'control-label',
15091                     html : this.fieldLabel
15092
15093                 },
15094                 {
15095                     cls : '', 
15096                     cn: [
15097                         combobox
15098                     ]
15099                 }
15100             ];
15101             
15102             var labelCfg = cfg.cn[1];
15103             var contentCfg = cfg.cn[2];
15104             
15105
15106             if(this.indicatorpos == 'right'){
15107                 cfg.cn = [
15108                     {
15109                         tag: 'label',
15110                         'for' :  id,
15111                         cls : 'control-label',
15112                         cn : [
15113                             {
15114                                 tag : 'span',
15115                                 html : this.fieldLabel
15116                             },
15117                             {
15118                                 tag : 'i',
15119                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15120                                 tooltip : 'This field is required'
15121                             }
15122                         ]
15123                     },
15124                     {
15125                         cls : "",
15126                         cn: [
15127                             combobox
15128                         ]
15129                     }
15130
15131                 ];
15132                 
15133                 labelCfg = cfg.cn[0];
15134                 contentCfg = cfg.cn[1];
15135             }
15136             
15137            
15138             
15139             if(this.labelWidth > 12){
15140                 labelCfg.style = "width: " + this.labelWidth + 'px';
15141             }
15142             
15143             if(this.labelWidth < 13 && this.labelmd == 0){
15144                 this.labelmd = this.labelWidth;
15145             }
15146             
15147             if(this.labellg > 0){
15148                 labelCfg.cls += ' col-lg-' + this.labellg;
15149                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15150             }
15151             
15152             if(this.labelmd > 0){
15153                 labelCfg.cls += ' col-md-' + this.labelmd;
15154                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15155             }
15156             
15157             if(this.labelsm > 0){
15158                 labelCfg.cls += ' col-sm-' + this.labelsm;
15159                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15160             }
15161             
15162             if(this.labelxs > 0){
15163                 labelCfg.cls += ' col-xs-' + this.labelxs;
15164                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15165             }
15166                 
15167                 
15168         } else if ( this.fieldLabel.length) {
15169             cfg.cn = [
15170                 {
15171                    tag : 'i',
15172                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15173                    tooltip : 'This field is required'
15174                 },
15175                 {
15176                     tag: 'label',
15177                     cls : 'control-label',
15178                     html : this.fieldLabel
15179
15180                 },
15181                 {
15182                     cls : '', 
15183                     cn: [
15184                         combobox
15185                     ]
15186                 }
15187             ];
15188             
15189             if(this.indicatorpos == 'right'){
15190                 cfg.cn = [
15191                     {
15192                         tag: 'label',
15193                         cls : 'control-label',
15194                         html : this.fieldLabel,
15195                         cn : [
15196                             {
15197                                tag : 'i',
15198                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15199                                tooltip : 'This field is required'
15200                             }
15201                         ]
15202                     },
15203                     {
15204                         cls : '', 
15205                         cn: [
15206                             combobox
15207                         ]
15208                     }
15209                 ];
15210             }
15211         } else {
15212             cfg.cn = combobox;    
15213         }
15214         
15215         
15216         var settings = this;
15217         
15218         ['xs','sm','md','lg'].map(function(size){
15219             if (settings[size]) {
15220                 cfg.cls += ' col-' + size + '-' + settings[size];
15221             }
15222         });
15223         
15224         return cfg;
15225     },
15226     
15227     initTouchView : function()
15228     {
15229         this.renderTouchView();
15230         
15231         this.touchViewEl.on('scroll', function(){
15232             this.el.dom.scrollTop = 0;
15233         }, this);
15234         
15235         this.originalValue = this.getValue();
15236         
15237         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15238         
15239         this.inputEl().on("click", this.showTouchView, this);
15240         if (this.triggerEl) {
15241             this.triggerEl.on("click", this.showTouchView, this);
15242         }
15243         
15244         
15245         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15246         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15247         
15248         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15249         
15250         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15251         this.store.on('load', this.onTouchViewLoad, this);
15252         this.store.on('loadexception', this.onTouchViewLoadException, this);
15253         
15254         if(this.hiddenName){
15255             
15256             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15257             
15258             this.hiddenField.dom.value =
15259                 this.hiddenValue !== undefined ? this.hiddenValue :
15260                 this.value !== undefined ? this.value : '';
15261         
15262             this.el.dom.removeAttribute('name');
15263             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15264         }
15265         
15266         if(this.multiple){
15267             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15268             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15269         }
15270         
15271         if(this.removable && !this.multiple){
15272             var close = this.closeTriggerEl();
15273             if(close){
15274                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15275                 close.on('click', this.removeBtnClick, this, close);
15276             }
15277         }
15278         /*
15279          * fix the bug in Safari iOS8
15280          */
15281         this.inputEl().on("focus", function(e){
15282             document.activeElement.blur();
15283         }, this);
15284         
15285         return;
15286         
15287         
15288     },
15289     
15290     renderTouchView : function()
15291     {
15292         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15293         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15294         
15295         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15296         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15297         
15298         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15299         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15300         this.touchViewBodyEl.setStyle('overflow', 'auto');
15301         
15302         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15303         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15304         
15305         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15306         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15307         
15308     },
15309     
15310     showTouchView : function()
15311     {
15312         if(this.disabled){
15313             return;
15314         }
15315         
15316         this.touchViewHeaderEl.hide();
15317
15318         if(this.modalTitle.length){
15319             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15320             this.touchViewHeaderEl.show();
15321         }
15322
15323         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15324         this.touchViewEl.show();
15325
15326         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15327         
15328         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15329         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15330
15331         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15332
15333         if(this.modalTitle.length){
15334             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15335         }
15336         
15337         this.touchViewBodyEl.setHeight(bodyHeight);
15338
15339         if(this.animate){
15340             var _this = this;
15341             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15342         }else{
15343             this.touchViewEl.addClass('in');
15344         }
15345
15346         this.doTouchViewQuery();
15347         
15348     },
15349     
15350     hideTouchView : function()
15351     {
15352         this.touchViewEl.removeClass('in');
15353
15354         if(this.animate){
15355             var _this = this;
15356             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15357         }else{
15358             this.touchViewEl.setStyle('display', 'none');
15359         }
15360         
15361     },
15362     
15363     setTouchViewValue : function()
15364     {
15365         if(this.multiple){
15366             this.clearItem();
15367         
15368             var _this = this;
15369
15370             Roo.each(this.tickItems, function(o){
15371                 this.addItem(o);
15372             }, this);
15373         }
15374         
15375         this.hideTouchView();
15376     },
15377     
15378     doTouchViewQuery : function()
15379     {
15380         var qe = {
15381             query: '',
15382             forceAll: true,
15383             combo: this,
15384             cancel:false
15385         };
15386         
15387         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15388             return false;
15389         }
15390         
15391         if(!this.alwaysQuery || this.mode == 'local'){
15392             this.onTouchViewLoad();
15393             return;
15394         }
15395         
15396         this.store.load();
15397     },
15398     
15399     onTouchViewBeforeLoad : function(combo,opts)
15400     {
15401         return;
15402     },
15403
15404     // private
15405     onTouchViewLoad : function()
15406     {
15407         if(this.store.getCount() < 1){
15408             this.onTouchViewEmptyResults();
15409             return;
15410         }
15411         
15412         this.clearTouchView();
15413         
15414         var rawValue = this.getRawValue();
15415         
15416         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15417         
15418         this.tickItems = [];
15419         
15420         this.store.data.each(function(d, rowIndex){
15421             var row = this.touchViewListGroup.createChild(template);
15422             
15423             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15424                 row.addClass(d.data.cls);
15425             }
15426             
15427             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15428                 var cfg = {
15429                     data : d.data,
15430                     html : d.data[this.displayField]
15431                 };
15432                 
15433                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15434                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15435                 }
15436             }
15437             row.removeClass('selected');
15438             if(!this.multiple && this.valueField &&
15439                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15440             {
15441                 // radio buttons..
15442                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15443                 row.addClass('selected');
15444             }
15445             
15446             if(this.multiple && this.valueField &&
15447                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15448             {
15449                 
15450                 // checkboxes...
15451                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15452                 this.tickItems.push(d.data);
15453             }
15454             
15455             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15456             
15457         }, this);
15458         
15459         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15460         
15461         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15462
15463         if(this.modalTitle.length){
15464             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15465         }
15466
15467         var listHeight = this.touchViewListGroup.getHeight();
15468         
15469         var _this = this;
15470         
15471         if(firstChecked && listHeight > bodyHeight){
15472             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15473         }
15474         
15475     },
15476     
15477     onTouchViewLoadException : function()
15478     {
15479         this.hideTouchView();
15480     },
15481     
15482     onTouchViewEmptyResults : function()
15483     {
15484         this.clearTouchView();
15485         
15486         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15487         
15488         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15489         
15490     },
15491     
15492     clearTouchView : function()
15493     {
15494         this.touchViewListGroup.dom.innerHTML = '';
15495     },
15496     
15497     onTouchViewClick : function(e, el, o)
15498     {
15499         e.preventDefault();
15500         
15501         var row = o.row;
15502         var rowIndex = o.rowIndex;
15503         
15504         var r = this.store.getAt(rowIndex);
15505         
15506         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15507             
15508             if(!this.multiple){
15509                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15510                     c.dom.removeAttribute('checked');
15511                 }, this);
15512
15513                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15514
15515                 this.setFromData(r.data);
15516
15517                 var close = this.closeTriggerEl();
15518
15519                 if(close){
15520                     close.show();
15521                 }
15522
15523                 this.hideTouchView();
15524
15525                 this.fireEvent('select', this, r, rowIndex);
15526
15527                 return;
15528             }
15529
15530             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15531                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15532                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15533                 return;
15534             }
15535
15536             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15537             this.addItem(r.data);
15538             this.tickItems.push(r.data);
15539         }
15540     },
15541     
15542     getAutoCreateNativeIOS : function()
15543     {
15544         var cfg = {
15545             cls: 'form-group' //input-group,
15546         };
15547         
15548         var combobox =  {
15549             tag: 'select',
15550             cls : 'roo-ios-select'
15551         };
15552         
15553         if (this.name) {
15554             combobox.name = this.name;
15555         }
15556         
15557         if (this.disabled) {
15558             combobox.disabled = true;
15559         }
15560         
15561         var settings = this;
15562         
15563         ['xs','sm','md','lg'].map(function(size){
15564             if (settings[size]) {
15565                 cfg.cls += ' col-' + size + '-' + settings[size];
15566             }
15567         });
15568         
15569         cfg.cn = combobox;
15570         
15571         return cfg;
15572         
15573     },
15574     
15575     initIOSView : function()
15576     {
15577         this.store.on('load', this.onIOSViewLoad, this);
15578         
15579         return;
15580     },
15581     
15582     onIOSViewLoad : function()
15583     {
15584         if(this.store.getCount() < 1){
15585             return;
15586         }
15587         
15588         this.clearIOSView();
15589         
15590         if(this.allowBlank) {
15591             
15592             var default_text = '-- SELECT --';
15593             
15594             if(this.placeholder.length){
15595                 default_text = this.placeholder;
15596             }
15597             
15598             if(this.emptyTitle.length){
15599                 default_text += ' - ' + this.emptyTitle + ' -';
15600             }
15601             
15602             var opt = this.inputEl().createChild({
15603                 tag: 'option',
15604                 value : 0,
15605                 html : default_text
15606             });
15607             
15608             var o = {};
15609             o[this.valueField] = 0;
15610             o[this.displayField] = default_text;
15611             
15612             this.ios_options.push({
15613                 data : o,
15614                 el : opt
15615             });
15616             
15617         }
15618         
15619         this.store.data.each(function(d, rowIndex){
15620             
15621             var html = '';
15622             
15623             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15624                 html = d.data[this.displayField];
15625             }
15626             
15627             var value = '';
15628             
15629             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15630                 value = d.data[this.valueField];
15631             }
15632             
15633             var option = {
15634                 tag: 'option',
15635                 value : value,
15636                 html : html
15637             };
15638             
15639             if(this.value == d.data[this.valueField]){
15640                 option['selected'] = true;
15641             }
15642             
15643             var opt = this.inputEl().createChild(option);
15644             
15645             this.ios_options.push({
15646                 data : d.data,
15647                 el : opt
15648             });
15649             
15650         }, this);
15651         
15652         this.inputEl().on('change', function(){
15653            this.fireEvent('select', this);
15654         }, this);
15655         
15656     },
15657     
15658     clearIOSView: function()
15659     {
15660         this.inputEl().dom.innerHTML = '';
15661         
15662         this.ios_options = [];
15663     },
15664     
15665     setIOSValue: function(v)
15666     {
15667         this.value = v;
15668         
15669         if(!this.ios_options){
15670             return;
15671         }
15672         
15673         Roo.each(this.ios_options, function(opts){
15674            
15675            opts.el.dom.removeAttribute('selected');
15676            
15677            if(opts.data[this.valueField] != v){
15678                return;
15679            }
15680            
15681            opts.el.dom.setAttribute('selected', true);
15682            
15683         }, this);
15684     }
15685
15686     /** 
15687     * @cfg {Boolean} grow 
15688     * @hide 
15689     */
15690     /** 
15691     * @cfg {Number} growMin 
15692     * @hide 
15693     */
15694     /** 
15695     * @cfg {Number} growMax 
15696     * @hide 
15697     */
15698     /**
15699      * @hide
15700      * @method autoSize
15701      */
15702 });
15703
15704 Roo.apply(Roo.bootstrap.ComboBox,  {
15705     
15706     header : {
15707         tag: 'div',
15708         cls: 'modal-header',
15709         cn: [
15710             {
15711                 tag: 'h4',
15712                 cls: 'modal-title'
15713             }
15714         ]
15715     },
15716     
15717     body : {
15718         tag: 'div',
15719         cls: 'modal-body',
15720         cn: [
15721             {
15722                 tag: 'ul',
15723                 cls: 'list-group'
15724             }
15725         ]
15726     },
15727     
15728     listItemRadio : {
15729         tag: 'li',
15730         cls: 'list-group-item',
15731         cn: [
15732             {
15733                 tag: 'span',
15734                 cls: 'roo-combobox-list-group-item-value'
15735             },
15736             {
15737                 tag: 'div',
15738                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15739                 cn: [
15740                     {
15741                         tag: 'input',
15742                         type: 'radio'
15743                     },
15744                     {
15745                         tag: 'label'
15746                     }
15747                 ]
15748             }
15749         ]
15750     },
15751     
15752     listItemCheckbox : {
15753         tag: 'li',
15754         cls: 'list-group-item',
15755         cn: [
15756             {
15757                 tag: 'span',
15758                 cls: 'roo-combobox-list-group-item-value'
15759             },
15760             {
15761                 tag: 'div',
15762                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15763                 cn: [
15764                     {
15765                         tag: 'input',
15766                         type: 'checkbox'
15767                     },
15768                     {
15769                         tag: 'label'
15770                     }
15771                 ]
15772             }
15773         ]
15774     },
15775     
15776     emptyResult : {
15777         tag: 'div',
15778         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15779     },
15780     
15781     footer : {
15782         tag: 'div',
15783         cls: 'modal-footer',
15784         cn: [
15785             {
15786                 tag: 'div',
15787                 cls: 'row',
15788                 cn: [
15789                     {
15790                         tag: 'div',
15791                         cls: 'col-xs-6 text-left',
15792                         cn: {
15793                             tag: 'button',
15794                             cls: 'btn btn-danger roo-touch-view-cancel',
15795                             html: 'Cancel'
15796                         }
15797                     },
15798                     {
15799                         tag: 'div',
15800                         cls: 'col-xs-6 text-right',
15801                         cn: {
15802                             tag: 'button',
15803                             cls: 'btn btn-success roo-touch-view-ok',
15804                             html: 'OK'
15805                         }
15806                     }
15807                 ]
15808             }
15809         ]
15810         
15811     }
15812 });
15813
15814 Roo.apply(Roo.bootstrap.ComboBox,  {
15815     
15816     touchViewTemplate : {
15817         tag: 'div',
15818         cls: 'modal fade roo-combobox-touch-view',
15819         cn: [
15820             {
15821                 tag: 'div',
15822                 cls: 'modal-dialog',
15823                 style : 'position:fixed', // we have to fix position....
15824                 cn: [
15825                     {
15826                         tag: 'div',
15827                         cls: 'modal-content',
15828                         cn: [
15829                             Roo.bootstrap.ComboBox.header,
15830                             Roo.bootstrap.ComboBox.body,
15831                             Roo.bootstrap.ComboBox.footer
15832                         ]
15833                     }
15834                 ]
15835             }
15836         ]
15837     }
15838 });/*
15839  * Based on:
15840  * Ext JS Library 1.1.1
15841  * Copyright(c) 2006-2007, Ext JS, LLC.
15842  *
15843  * Originally Released Under LGPL - original licence link has changed is not relivant.
15844  *
15845  * Fork - LGPL
15846  * <script type="text/javascript">
15847  */
15848
15849 /**
15850  * @class Roo.View
15851  * @extends Roo.util.Observable
15852  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15853  * This class also supports single and multi selection modes. <br>
15854  * Create a data model bound view:
15855  <pre><code>
15856  var store = new Roo.data.Store(...);
15857
15858  var view = new Roo.View({
15859     el : "my-element",
15860     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15861  
15862     singleSelect: true,
15863     selectedClass: "ydataview-selected",
15864     store: store
15865  });
15866
15867  // listen for node click?
15868  view.on("click", function(vw, index, node, e){
15869  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15870  });
15871
15872  // load XML data
15873  dataModel.load("foobar.xml");
15874  </code></pre>
15875  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15876  * <br><br>
15877  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15878  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15879  * 
15880  * Note: old style constructor is still suported (container, template, config)
15881  * 
15882  * @constructor
15883  * Create a new View
15884  * @param {Object} config The config object
15885  * 
15886  */
15887 Roo.View = function(config, depreciated_tpl, depreciated_config){
15888     
15889     this.parent = false;
15890     
15891     if (typeof(depreciated_tpl) == 'undefined') {
15892         // new way.. - universal constructor.
15893         Roo.apply(this, config);
15894         this.el  = Roo.get(this.el);
15895     } else {
15896         // old format..
15897         this.el  = Roo.get(config);
15898         this.tpl = depreciated_tpl;
15899         Roo.apply(this, depreciated_config);
15900     }
15901     this.wrapEl  = this.el.wrap().wrap();
15902     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15903     
15904     
15905     if(typeof(this.tpl) == "string"){
15906         this.tpl = new Roo.Template(this.tpl);
15907     } else {
15908         // support xtype ctors..
15909         this.tpl = new Roo.factory(this.tpl, Roo);
15910     }
15911     
15912     
15913     this.tpl.compile();
15914     
15915     /** @private */
15916     this.addEvents({
15917         /**
15918          * @event beforeclick
15919          * Fires before a click is processed. Returns false to cancel the default action.
15920          * @param {Roo.View} this
15921          * @param {Number} index The index of the target node
15922          * @param {HTMLElement} node The target node
15923          * @param {Roo.EventObject} e The raw event object
15924          */
15925             "beforeclick" : true,
15926         /**
15927          * @event click
15928          * Fires when a template node is clicked.
15929          * @param {Roo.View} this
15930          * @param {Number} index The index of the target node
15931          * @param {HTMLElement} node The target node
15932          * @param {Roo.EventObject} e The raw event object
15933          */
15934             "click" : true,
15935         /**
15936          * @event dblclick
15937          * Fires when a template node is double clicked.
15938          * @param {Roo.View} this
15939          * @param {Number} index The index of the target node
15940          * @param {HTMLElement} node The target node
15941          * @param {Roo.EventObject} e The raw event object
15942          */
15943             "dblclick" : true,
15944         /**
15945          * @event contextmenu
15946          * Fires when a template node is right clicked.
15947          * @param {Roo.View} this
15948          * @param {Number} index The index of the target node
15949          * @param {HTMLElement} node The target node
15950          * @param {Roo.EventObject} e The raw event object
15951          */
15952             "contextmenu" : true,
15953         /**
15954          * @event selectionchange
15955          * Fires when the selected nodes change.
15956          * @param {Roo.View} this
15957          * @param {Array} selections Array of the selected nodes
15958          */
15959             "selectionchange" : true,
15960     
15961         /**
15962          * @event beforeselect
15963          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15964          * @param {Roo.View} this
15965          * @param {HTMLElement} node The node to be selected
15966          * @param {Array} selections Array of currently selected nodes
15967          */
15968             "beforeselect" : true,
15969         /**
15970          * @event preparedata
15971          * Fires on every row to render, to allow you to change the data.
15972          * @param {Roo.View} this
15973          * @param {Object} data to be rendered (change this)
15974          */
15975           "preparedata" : true
15976           
15977           
15978         });
15979
15980
15981
15982     this.el.on({
15983         "click": this.onClick,
15984         "dblclick": this.onDblClick,
15985         "contextmenu": this.onContextMenu,
15986         scope:this
15987     });
15988
15989     this.selections = [];
15990     this.nodes = [];
15991     this.cmp = new Roo.CompositeElementLite([]);
15992     if(this.store){
15993         this.store = Roo.factory(this.store, Roo.data);
15994         this.setStore(this.store, true);
15995     }
15996     
15997     if ( this.footer && this.footer.xtype) {
15998            
15999          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16000         
16001         this.footer.dataSource = this.store;
16002         this.footer.container = fctr;
16003         this.footer = Roo.factory(this.footer, Roo);
16004         fctr.insertFirst(this.el);
16005         
16006         // this is a bit insane - as the paging toolbar seems to detach the el..
16007 //        dom.parentNode.parentNode.parentNode
16008          // they get detached?
16009     }
16010     
16011     
16012     Roo.View.superclass.constructor.call(this);
16013     
16014     
16015 };
16016
16017 Roo.extend(Roo.View, Roo.util.Observable, {
16018     
16019      /**
16020      * @cfg {Roo.data.Store} store Data store to load data from.
16021      */
16022     store : false,
16023     
16024     /**
16025      * @cfg {String|Roo.Element} el The container element.
16026      */
16027     el : '',
16028     
16029     /**
16030      * @cfg {String|Roo.Template} tpl The template used by this View 
16031      */
16032     tpl : false,
16033     /**
16034      * @cfg {String} dataName the named area of the template to use as the data area
16035      *                          Works with domtemplates roo-name="name"
16036      */
16037     dataName: false,
16038     /**
16039      * @cfg {String} selectedClass The css class to add to selected nodes
16040      */
16041     selectedClass : "x-view-selected",
16042      /**
16043      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16044      */
16045     emptyText : "",
16046     
16047     /**
16048      * @cfg {String} text to display on mask (default Loading)
16049      */
16050     mask : false,
16051     /**
16052      * @cfg {Boolean} multiSelect Allow multiple selection
16053      */
16054     multiSelect : false,
16055     /**
16056      * @cfg {Boolean} singleSelect Allow single selection
16057      */
16058     singleSelect:  false,
16059     
16060     /**
16061      * @cfg {Boolean} toggleSelect - selecting 
16062      */
16063     toggleSelect : false,
16064     
16065     /**
16066      * @cfg {Boolean} tickable - selecting 
16067      */
16068     tickable : false,
16069     
16070     /**
16071      * Returns the element this view is bound to.
16072      * @return {Roo.Element}
16073      */
16074     getEl : function(){
16075         return this.wrapEl;
16076     },
16077     
16078     
16079
16080     /**
16081      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16082      */
16083     refresh : function(){
16084         //Roo.log('refresh');
16085         var t = this.tpl;
16086         
16087         // if we are using something like 'domtemplate', then
16088         // the what gets used is:
16089         // t.applySubtemplate(NAME, data, wrapping data..)
16090         // the outer template then get' applied with
16091         //     the store 'extra data'
16092         // and the body get's added to the
16093         //      roo-name="data" node?
16094         //      <span class='roo-tpl-{name}'></span> ?????
16095         
16096         
16097         
16098         this.clearSelections();
16099         this.el.update("");
16100         var html = [];
16101         var records = this.store.getRange();
16102         if(records.length < 1) {
16103             
16104             // is this valid??  = should it render a template??
16105             
16106             this.el.update(this.emptyText);
16107             return;
16108         }
16109         var el = this.el;
16110         if (this.dataName) {
16111             this.el.update(t.apply(this.store.meta)); //????
16112             el = this.el.child('.roo-tpl-' + this.dataName);
16113         }
16114         
16115         for(var i = 0, len = records.length; i < len; i++){
16116             var data = this.prepareData(records[i].data, i, records[i]);
16117             this.fireEvent("preparedata", this, data, i, records[i]);
16118             
16119             var d = Roo.apply({}, data);
16120             
16121             if(this.tickable){
16122                 Roo.apply(d, {'roo-id' : Roo.id()});
16123                 
16124                 var _this = this;
16125             
16126                 Roo.each(this.parent.item, function(item){
16127                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16128                         return;
16129                     }
16130                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16131                 });
16132             }
16133             
16134             html[html.length] = Roo.util.Format.trim(
16135                 this.dataName ?
16136                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16137                     t.apply(d)
16138             );
16139         }
16140         
16141         
16142         
16143         el.update(html.join(""));
16144         this.nodes = el.dom.childNodes;
16145         this.updateIndexes(0);
16146     },
16147     
16148
16149     /**
16150      * Function to override to reformat the data that is sent to
16151      * the template for each node.
16152      * DEPRICATED - use the preparedata event handler.
16153      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16154      * a JSON object for an UpdateManager bound view).
16155      */
16156     prepareData : function(data, index, record)
16157     {
16158         this.fireEvent("preparedata", this, data, index, record);
16159         return data;
16160     },
16161
16162     onUpdate : function(ds, record){
16163         // Roo.log('on update');   
16164         this.clearSelections();
16165         var index = this.store.indexOf(record);
16166         var n = this.nodes[index];
16167         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16168         n.parentNode.removeChild(n);
16169         this.updateIndexes(index, index);
16170     },
16171
16172     
16173     
16174 // --------- FIXME     
16175     onAdd : function(ds, records, index)
16176     {
16177         //Roo.log(['on Add', ds, records, index] );        
16178         this.clearSelections();
16179         if(this.nodes.length == 0){
16180             this.refresh();
16181             return;
16182         }
16183         var n = this.nodes[index];
16184         for(var i = 0, len = records.length; i < len; i++){
16185             var d = this.prepareData(records[i].data, i, records[i]);
16186             if(n){
16187                 this.tpl.insertBefore(n, d);
16188             }else{
16189                 
16190                 this.tpl.append(this.el, d);
16191             }
16192         }
16193         this.updateIndexes(index);
16194     },
16195
16196     onRemove : function(ds, record, index){
16197        // Roo.log('onRemove');
16198         this.clearSelections();
16199         var el = this.dataName  ?
16200             this.el.child('.roo-tpl-' + this.dataName) :
16201             this.el; 
16202         
16203         el.dom.removeChild(this.nodes[index]);
16204         this.updateIndexes(index);
16205     },
16206
16207     /**
16208      * Refresh an individual node.
16209      * @param {Number} index
16210      */
16211     refreshNode : function(index){
16212         this.onUpdate(this.store, this.store.getAt(index));
16213     },
16214
16215     updateIndexes : function(startIndex, endIndex){
16216         var ns = this.nodes;
16217         startIndex = startIndex || 0;
16218         endIndex = endIndex || ns.length - 1;
16219         for(var i = startIndex; i <= endIndex; i++){
16220             ns[i].nodeIndex = i;
16221         }
16222     },
16223
16224     /**
16225      * Changes the data store this view uses and refresh the view.
16226      * @param {Store} store
16227      */
16228     setStore : function(store, initial){
16229         if(!initial && this.store){
16230             this.store.un("datachanged", this.refresh);
16231             this.store.un("add", this.onAdd);
16232             this.store.un("remove", this.onRemove);
16233             this.store.un("update", this.onUpdate);
16234             this.store.un("clear", this.refresh);
16235             this.store.un("beforeload", this.onBeforeLoad);
16236             this.store.un("load", this.onLoad);
16237             this.store.un("loadexception", this.onLoad);
16238         }
16239         if(store){
16240           
16241             store.on("datachanged", this.refresh, this);
16242             store.on("add", this.onAdd, this);
16243             store.on("remove", this.onRemove, this);
16244             store.on("update", this.onUpdate, this);
16245             store.on("clear", this.refresh, this);
16246             store.on("beforeload", this.onBeforeLoad, this);
16247             store.on("load", this.onLoad, this);
16248             store.on("loadexception", this.onLoad, this);
16249         }
16250         
16251         if(store){
16252             this.refresh();
16253         }
16254     },
16255     /**
16256      * onbeforeLoad - masks the loading area.
16257      *
16258      */
16259     onBeforeLoad : function(store,opts)
16260     {
16261          //Roo.log('onBeforeLoad');   
16262         if (!opts.add) {
16263             this.el.update("");
16264         }
16265         this.el.mask(this.mask ? this.mask : "Loading" ); 
16266     },
16267     onLoad : function ()
16268     {
16269         this.el.unmask();
16270     },
16271     
16272
16273     /**
16274      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16275      * @param {HTMLElement} node
16276      * @return {HTMLElement} The template node
16277      */
16278     findItemFromChild : function(node){
16279         var el = this.dataName  ?
16280             this.el.child('.roo-tpl-' + this.dataName,true) :
16281             this.el.dom; 
16282         
16283         if(!node || node.parentNode == el){
16284                     return node;
16285             }
16286             var p = node.parentNode;
16287             while(p && p != el){
16288             if(p.parentNode == el){
16289                 return p;
16290             }
16291             p = p.parentNode;
16292         }
16293             return null;
16294     },
16295
16296     /** @ignore */
16297     onClick : function(e){
16298         var item = this.findItemFromChild(e.getTarget());
16299         if(item){
16300             var index = this.indexOf(item);
16301             if(this.onItemClick(item, index, e) !== false){
16302                 this.fireEvent("click", this, index, item, e);
16303             }
16304         }else{
16305             this.clearSelections();
16306         }
16307     },
16308
16309     /** @ignore */
16310     onContextMenu : function(e){
16311         var item = this.findItemFromChild(e.getTarget());
16312         if(item){
16313             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16314         }
16315     },
16316
16317     /** @ignore */
16318     onDblClick : function(e){
16319         var item = this.findItemFromChild(e.getTarget());
16320         if(item){
16321             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16322         }
16323     },
16324
16325     onItemClick : function(item, index, e)
16326     {
16327         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16328             return false;
16329         }
16330         if (this.toggleSelect) {
16331             var m = this.isSelected(item) ? 'unselect' : 'select';
16332             //Roo.log(m);
16333             var _t = this;
16334             _t[m](item, true, false);
16335             return true;
16336         }
16337         if(this.multiSelect || this.singleSelect){
16338             if(this.multiSelect && e.shiftKey && this.lastSelection){
16339                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16340             }else{
16341                 this.select(item, this.multiSelect && e.ctrlKey);
16342                 this.lastSelection = item;
16343             }
16344             
16345             if(!this.tickable){
16346                 e.preventDefault();
16347             }
16348             
16349         }
16350         return true;
16351     },
16352
16353     /**
16354      * Get the number of selected nodes.
16355      * @return {Number}
16356      */
16357     getSelectionCount : function(){
16358         return this.selections.length;
16359     },
16360
16361     /**
16362      * Get the currently selected nodes.
16363      * @return {Array} An array of HTMLElements
16364      */
16365     getSelectedNodes : function(){
16366         return this.selections;
16367     },
16368
16369     /**
16370      * Get the indexes of the selected nodes.
16371      * @return {Array}
16372      */
16373     getSelectedIndexes : function(){
16374         var indexes = [], s = this.selections;
16375         for(var i = 0, len = s.length; i < len; i++){
16376             indexes.push(s[i].nodeIndex);
16377         }
16378         return indexes;
16379     },
16380
16381     /**
16382      * Clear all selections
16383      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16384      */
16385     clearSelections : function(suppressEvent){
16386         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16387             this.cmp.elements = this.selections;
16388             this.cmp.removeClass(this.selectedClass);
16389             this.selections = [];
16390             if(!suppressEvent){
16391                 this.fireEvent("selectionchange", this, this.selections);
16392             }
16393         }
16394     },
16395
16396     /**
16397      * Returns true if the passed node is selected
16398      * @param {HTMLElement/Number} node The node or node index
16399      * @return {Boolean}
16400      */
16401     isSelected : function(node){
16402         var s = this.selections;
16403         if(s.length < 1){
16404             return false;
16405         }
16406         node = this.getNode(node);
16407         return s.indexOf(node) !== -1;
16408     },
16409
16410     /**
16411      * Selects nodes.
16412      * @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
16413      * @param {Boolean} keepExisting (optional) true to keep existing selections
16414      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16415      */
16416     select : function(nodeInfo, keepExisting, suppressEvent){
16417         if(nodeInfo instanceof Array){
16418             if(!keepExisting){
16419                 this.clearSelections(true);
16420             }
16421             for(var i = 0, len = nodeInfo.length; i < len; i++){
16422                 this.select(nodeInfo[i], true, true);
16423             }
16424             return;
16425         } 
16426         var node = this.getNode(nodeInfo);
16427         if(!node || this.isSelected(node)){
16428             return; // already selected.
16429         }
16430         if(!keepExisting){
16431             this.clearSelections(true);
16432         }
16433         
16434         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16435             Roo.fly(node).addClass(this.selectedClass);
16436             this.selections.push(node);
16437             if(!suppressEvent){
16438                 this.fireEvent("selectionchange", this, this.selections);
16439             }
16440         }
16441         
16442         
16443     },
16444       /**
16445      * Unselects nodes.
16446      * @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
16447      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16448      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16449      */
16450     unselect : function(nodeInfo, keepExisting, suppressEvent)
16451     {
16452         if(nodeInfo instanceof Array){
16453             Roo.each(this.selections, function(s) {
16454                 this.unselect(s, nodeInfo);
16455             }, this);
16456             return;
16457         }
16458         var node = this.getNode(nodeInfo);
16459         if(!node || !this.isSelected(node)){
16460             //Roo.log("not selected");
16461             return; // not selected.
16462         }
16463         // fireevent???
16464         var ns = [];
16465         Roo.each(this.selections, function(s) {
16466             if (s == node ) {
16467                 Roo.fly(node).removeClass(this.selectedClass);
16468
16469                 return;
16470             }
16471             ns.push(s);
16472         },this);
16473         
16474         this.selections= ns;
16475         this.fireEvent("selectionchange", this, this.selections);
16476     },
16477
16478     /**
16479      * Gets a template node.
16480      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16481      * @return {HTMLElement} The node or null if it wasn't found
16482      */
16483     getNode : function(nodeInfo){
16484         if(typeof nodeInfo == "string"){
16485             return document.getElementById(nodeInfo);
16486         }else if(typeof nodeInfo == "number"){
16487             return this.nodes[nodeInfo];
16488         }
16489         return nodeInfo;
16490     },
16491
16492     /**
16493      * Gets a range template nodes.
16494      * @param {Number} startIndex
16495      * @param {Number} endIndex
16496      * @return {Array} An array of nodes
16497      */
16498     getNodes : function(start, end){
16499         var ns = this.nodes;
16500         start = start || 0;
16501         end = typeof end == "undefined" ? ns.length - 1 : end;
16502         var nodes = [];
16503         if(start <= end){
16504             for(var i = start; i <= end; i++){
16505                 nodes.push(ns[i]);
16506             }
16507         } else{
16508             for(var i = start; i >= end; i--){
16509                 nodes.push(ns[i]);
16510             }
16511         }
16512         return nodes;
16513     },
16514
16515     /**
16516      * Finds the index of the passed node
16517      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16518      * @return {Number} The index of the node or -1
16519      */
16520     indexOf : function(node){
16521         node = this.getNode(node);
16522         if(typeof node.nodeIndex == "number"){
16523             return node.nodeIndex;
16524         }
16525         var ns = this.nodes;
16526         for(var i = 0, len = ns.length; i < len; i++){
16527             if(ns[i] == node){
16528                 return i;
16529             }
16530         }
16531         return -1;
16532     }
16533 });
16534 /*
16535  * - LGPL
16536  *
16537  * based on jquery fullcalendar
16538  * 
16539  */
16540
16541 Roo.bootstrap = Roo.bootstrap || {};
16542 /**
16543  * @class Roo.bootstrap.Calendar
16544  * @extends Roo.bootstrap.Component
16545  * Bootstrap Calendar class
16546  * @cfg {Boolean} loadMask (true|false) default false
16547  * @cfg {Object} header generate the user specific header of the calendar, default false
16548
16549  * @constructor
16550  * Create a new Container
16551  * @param {Object} config The config object
16552  */
16553
16554
16555
16556 Roo.bootstrap.Calendar = function(config){
16557     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16558      this.addEvents({
16559         /**
16560              * @event select
16561              * Fires when a date is selected
16562              * @param {DatePicker} this
16563              * @param {Date} date The selected date
16564              */
16565         'select': true,
16566         /**
16567              * @event monthchange
16568              * Fires when the displayed month changes 
16569              * @param {DatePicker} this
16570              * @param {Date} date The selected month
16571              */
16572         'monthchange': true,
16573         /**
16574              * @event evententer
16575              * Fires when mouse over an event
16576              * @param {Calendar} this
16577              * @param {event} Event
16578              */
16579         'evententer': true,
16580         /**
16581              * @event eventleave
16582              * Fires when the mouse leaves an
16583              * @param {Calendar} this
16584              * @param {event}
16585              */
16586         'eventleave': true,
16587         /**
16588              * @event eventclick
16589              * Fires when the mouse click an
16590              * @param {Calendar} this
16591              * @param {event}
16592              */
16593         'eventclick': true
16594         
16595     });
16596
16597 };
16598
16599 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16600     
16601      /**
16602      * @cfg {Number} startDay
16603      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16604      */
16605     startDay : 0,
16606     
16607     loadMask : false,
16608     
16609     header : false,
16610       
16611     getAutoCreate : function(){
16612         
16613         
16614         var fc_button = function(name, corner, style, content ) {
16615             return Roo.apply({},{
16616                 tag : 'span',
16617                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16618                          (corner.length ?
16619                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16620                             ''
16621                         ),
16622                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16623                 unselectable: 'on'
16624             });
16625         };
16626         
16627         var header = {};
16628         
16629         if(!this.header){
16630             header = {
16631                 tag : 'table',
16632                 cls : 'fc-header',
16633                 style : 'width:100%',
16634                 cn : [
16635                     {
16636                         tag: 'tr',
16637                         cn : [
16638                             {
16639                                 tag : 'td',
16640                                 cls : 'fc-header-left',
16641                                 cn : [
16642                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16643                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16644                                     { tag: 'span', cls: 'fc-header-space' },
16645                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16646
16647
16648                                 ]
16649                             },
16650
16651                             {
16652                                 tag : 'td',
16653                                 cls : 'fc-header-center',
16654                                 cn : [
16655                                     {
16656                                         tag: 'span',
16657                                         cls: 'fc-header-title',
16658                                         cn : {
16659                                             tag: 'H2',
16660                                             html : 'month / year'
16661                                         }
16662                                     }
16663
16664                                 ]
16665                             },
16666                             {
16667                                 tag : 'td',
16668                                 cls : 'fc-header-right',
16669                                 cn : [
16670                               /*      fc_button('month', 'left', '', 'month' ),
16671                                     fc_button('week', '', '', 'week' ),
16672                                     fc_button('day', 'right', '', 'day' )
16673                                 */    
16674
16675                                 ]
16676                             }
16677
16678                         ]
16679                     }
16680                 ]
16681             };
16682         }
16683         
16684         header = this.header;
16685         
16686        
16687         var cal_heads = function() {
16688             var ret = [];
16689             // fixme - handle this.
16690             
16691             for (var i =0; i < Date.dayNames.length; i++) {
16692                 var d = Date.dayNames[i];
16693                 ret.push({
16694                     tag: 'th',
16695                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16696                     html : d.substring(0,3)
16697                 });
16698                 
16699             }
16700             ret[0].cls += ' fc-first';
16701             ret[6].cls += ' fc-last';
16702             return ret;
16703         };
16704         var cal_cell = function(n) {
16705             return  {
16706                 tag: 'td',
16707                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16708                 cn : [
16709                     {
16710                         cn : [
16711                             {
16712                                 cls: 'fc-day-number',
16713                                 html: 'D'
16714                             },
16715                             {
16716                                 cls: 'fc-day-content',
16717                              
16718                                 cn : [
16719                                      {
16720                                         style: 'position: relative;' // height: 17px;
16721                                     }
16722                                 ]
16723                             }
16724                             
16725                             
16726                         ]
16727                     }
16728                 ]
16729                 
16730             }
16731         };
16732         var cal_rows = function() {
16733             
16734             var ret = [];
16735             for (var r = 0; r < 6; r++) {
16736                 var row= {
16737                     tag : 'tr',
16738                     cls : 'fc-week',
16739                     cn : []
16740                 };
16741                 
16742                 for (var i =0; i < Date.dayNames.length; i++) {
16743                     var d = Date.dayNames[i];
16744                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16745
16746                 }
16747                 row.cn[0].cls+=' fc-first';
16748                 row.cn[0].cn[0].style = 'min-height:90px';
16749                 row.cn[6].cls+=' fc-last';
16750                 ret.push(row);
16751                 
16752             }
16753             ret[0].cls += ' fc-first';
16754             ret[4].cls += ' fc-prev-last';
16755             ret[5].cls += ' fc-last';
16756             return ret;
16757             
16758         };
16759         
16760         var cal_table = {
16761             tag: 'table',
16762             cls: 'fc-border-separate',
16763             style : 'width:100%',
16764             cellspacing  : 0,
16765             cn : [
16766                 { 
16767                     tag: 'thead',
16768                     cn : [
16769                         { 
16770                             tag: 'tr',
16771                             cls : 'fc-first fc-last',
16772                             cn : cal_heads()
16773                         }
16774                     ]
16775                 },
16776                 { 
16777                     tag: 'tbody',
16778                     cn : cal_rows()
16779                 }
16780                   
16781             ]
16782         };
16783          
16784          var cfg = {
16785             cls : 'fc fc-ltr',
16786             cn : [
16787                 header,
16788                 {
16789                     cls : 'fc-content',
16790                     style : "position: relative;",
16791                     cn : [
16792                         {
16793                             cls : 'fc-view fc-view-month fc-grid',
16794                             style : 'position: relative',
16795                             unselectable : 'on',
16796                             cn : [
16797                                 {
16798                                     cls : 'fc-event-container',
16799                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16800                                 },
16801                                 cal_table
16802                             ]
16803                         }
16804                     ]
16805     
16806                 }
16807            ] 
16808             
16809         };
16810         
16811          
16812         
16813         return cfg;
16814     },
16815     
16816     
16817     initEvents : function()
16818     {
16819         if(!this.store){
16820             throw "can not find store for calendar";
16821         }
16822         
16823         var mark = {
16824             tag: "div",
16825             cls:"x-dlg-mask",
16826             style: "text-align:center",
16827             cn: [
16828                 {
16829                     tag: "div",
16830                     style: "background-color:white;width:50%;margin:250 auto",
16831                     cn: [
16832                         {
16833                             tag: "img",
16834                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16835                         },
16836                         {
16837                             tag: "span",
16838                             html: "Loading"
16839                         }
16840                         
16841                     ]
16842                 }
16843             ]
16844         };
16845         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16846         
16847         var size = this.el.select('.fc-content', true).first().getSize();
16848         this.maskEl.setSize(size.width, size.height);
16849         this.maskEl.enableDisplayMode("block");
16850         if(!this.loadMask){
16851             this.maskEl.hide();
16852         }
16853         
16854         this.store = Roo.factory(this.store, Roo.data);
16855         this.store.on('load', this.onLoad, this);
16856         this.store.on('beforeload', this.onBeforeLoad, this);
16857         
16858         this.resize();
16859         
16860         this.cells = this.el.select('.fc-day',true);
16861         //Roo.log(this.cells);
16862         this.textNodes = this.el.query('.fc-day-number');
16863         this.cells.addClassOnOver('fc-state-hover');
16864         
16865         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16866         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16867         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16868         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16869         
16870         this.on('monthchange', this.onMonthChange, this);
16871         
16872         this.update(new Date().clearTime());
16873     },
16874     
16875     resize : function() {
16876         var sz  = this.el.getSize();
16877         
16878         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16879         this.el.select('.fc-day-content div',true).setHeight(34);
16880     },
16881     
16882     
16883     // private
16884     showPrevMonth : function(e){
16885         this.update(this.activeDate.add("mo", -1));
16886     },
16887     showToday : function(e){
16888         this.update(new Date().clearTime());
16889     },
16890     // private
16891     showNextMonth : function(e){
16892         this.update(this.activeDate.add("mo", 1));
16893     },
16894
16895     // private
16896     showPrevYear : function(){
16897         this.update(this.activeDate.add("y", -1));
16898     },
16899
16900     // private
16901     showNextYear : function(){
16902         this.update(this.activeDate.add("y", 1));
16903     },
16904
16905     
16906    // private
16907     update : function(date)
16908     {
16909         var vd = this.activeDate;
16910         this.activeDate = date;
16911 //        if(vd && this.el){
16912 //            var t = date.getTime();
16913 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16914 //                Roo.log('using add remove');
16915 //                
16916 //                this.fireEvent('monthchange', this, date);
16917 //                
16918 //                this.cells.removeClass("fc-state-highlight");
16919 //                this.cells.each(function(c){
16920 //                   if(c.dateValue == t){
16921 //                       c.addClass("fc-state-highlight");
16922 //                       setTimeout(function(){
16923 //                            try{c.dom.firstChild.focus();}catch(e){}
16924 //                       }, 50);
16925 //                       return false;
16926 //                   }
16927 //                   return true;
16928 //                });
16929 //                return;
16930 //            }
16931 //        }
16932         
16933         var days = date.getDaysInMonth();
16934         
16935         var firstOfMonth = date.getFirstDateOfMonth();
16936         var startingPos = firstOfMonth.getDay()-this.startDay;
16937         
16938         if(startingPos < this.startDay){
16939             startingPos += 7;
16940         }
16941         
16942         var pm = date.add(Date.MONTH, -1);
16943         var prevStart = pm.getDaysInMonth()-startingPos;
16944 //        
16945         this.cells = this.el.select('.fc-day',true);
16946         this.textNodes = this.el.query('.fc-day-number');
16947         this.cells.addClassOnOver('fc-state-hover');
16948         
16949         var cells = this.cells.elements;
16950         var textEls = this.textNodes;
16951         
16952         Roo.each(cells, function(cell){
16953             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16954         });
16955         
16956         days += startingPos;
16957
16958         // convert everything to numbers so it's fast
16959         var day = 86400000;
16960         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16961         //Roo.log(d);
16962         //Roo.log(pm);
16963         //Roo.log(prevStart);
16964         
16965         var today = new Date().clearTime().getTime();
16966         var sel = date.clearTime().getTime();
16967         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16968         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16969         var ddMatch = this.disabledDatesRE;
16970         var ddText = this.disabledDatesText;
16971         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16972         var ddaysText = this.disabledDaysText;
16973         var format = this.format;
16974         
16975         var setCellClass = function(cal, cell){
16976             cell.row = 0;
16977             cell.events = [];
16978             cell.more = [];
16979             //Roo.log('set Cell Class');
16980             cell.title = "";
16981             var t = d.getTime();
16982             
16983             //Roo.log(d);
16984             
16985             cell.dateValue = t;
16986             if(t == today){
16987                 cell.className += " fc-today";
16988                 cell.className += " fc-state-highlight";
16989                 cell.title = cal.todayText;
16990             }
16991             if(t == sel){
16992                 // disable highlight in other month..
16993                 //cell.className += " fc-state-highlight";
16994                 
16995             }
16996             // disabling
16997             if(t < min) {
16998                 cell.className = " fc-state-disabled";
16999                 cell.title = cal.minText;
17000                 return;
17001             }
17002             if(t > max) {
17003                 cell.className = " fc-state-disabled";
17004                 cell.title = cal.maxText;
17005                 return;
17006             }
17007             if(ddays){
17008                 if(ddays.indexOf(d.getDay()) != -1){
17009                     cell.title = ddaysText;
17010                     cell.className = " fc-state-disabled";
17011                 }
17012             }
17013             if(ddMatch && format){
17014                 var fvalue = d.dateFormat(format);
17015                 if(ddMatch.test(fvalue)){
17016                     cell.title = ddText.replace("%0", fvalue);
17017                     cell.className = " fc-state-disabled";
17018                 }
17019             }
17020             
17021             if (!cell.initialClassName) {
17022                 cell.initialClassName = cell.dom.className;
17023             }
17024             
17025             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17026         };
17027
17028         var i = 0;
17029         
17030         for(; i < startingPos; i++) {
17031             textEls[i].innerHTML = (++prevStart);
17032             d.setDate(d.getDate()+1);
17033             
17034             cells[i].className = "fc-past fc-other-month";
17035             setCellClass(this, cells[i]);
17036         }
17037         
17038         var intDay = 0;
17039         
17040         for(; i < days; i++){
17041             intDay = i - startingPos + 1;
17042             textEls[i].innerHTML = (intDay);
17043             d.setDate(d.getDate()+1);
17044             
17045             cells[i].className = ''; // "x-date-active";
17046             setCellClass(this, cells[i]);
17047         }
17048         var extraDays = 0;
17049         
17050         for(; i < 42; i++) {
17051             textEls[i].innerHTML = (++extraDays);
17052             d.setDate(d.getDate()+1);
17053             
17054             cells[i].className = "fc-future fc-other-month";
17055             setCellClass(this, cells[i]);
17056         }
17057         
17058         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17059         
17060         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17061         
17062         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17063         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17064         
17065         if(totalRows != 6){
17066             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17067             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17068         }
17069         
17070         this.fireEvent('monthchange', this, date);
17071         
17072         
17073         /*
17074         if(!this.internalRender){
17075             var main = this.el.dom.firstChild;
17076             var w = main.offsetWidth;
17077             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17078             Roo.fly(main).setWidth(w);
17079             this.internalRender = true;
17080             // opera does not respect the auto grow header center column
17081             // then, after it gets a width opera refuses to recalculate
17082             // without a second pass
17083             if(Roo.isOpera && !this.secondPass){
17084                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17085                 this.secondPass = true;
17086                 this.update.defer(10, this, [date]);
17087             }
17088         }
17089         */
17090         
17091     },
17092     
17093     findCell : function(dt) {
17094         dt = dt.clearTime().getTime();
17095         var ret = false;
17096         this.cells.each(function(c){
17097             //Roo.log("check " +c.dateValue + '?=' + dt);
17098             if(c.dateValue == dt){
17099                 ret = c;
17100                 return false;
17101             }
17102             return true;
17103         });
17104         
17105         return ret;
17106     },
17107     
17108     findCells : function(ev) {
17109         var s = ev.start.clone().clearTime().getTime();
17110        // Roo.log(s);
17111         var e= ev.end.clone().clearTime().getTime();
17112        // Roo.log(e);
17113         var ret = [];
17114         this.cells.each(function(c){
17115              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17116             
17117             if(c.dateValue > e){
17118                 return ;
17119             }
17120             if(c.dateValue < s){
17121                 return ;
17122             }
17123             ret.push(c);
17124         });
17125         
17126         return ret;    
17127     },
17128     
17129 //    findBestRow: function(cells)
17130 //    {
17131 //        var ret = 0;
17132 //        
17133 //        for (var i =0 ; i < cells.length;i++) {
17134 //            ret  = Math.max(cells[i].rows || 0,ret);
17135 //        }
17136 //        return ret;
17137 //        
17138 //    },
17139     
17140     
17141     addItem : function(ev)
17142     {
17143         // look for vertical location slot in
17144         var cells = this.findCells(ev);
17145         
17146 //        ev.row = this.findBestRow(cells);
17147         
17148         // work out the location.
17149         
17150         var crow = false;
17151         var rows = [];
17152         for(var i =0; i < cells.length; i++) {
17153             
17154             cells[i].row = cells[0].row;
17155             
17156             if(i == 0){
17157                 cells[i].row = cells[i].row + 1;
17158             }
17159             
17160             if (!crow) {
17161                 crow = {
17162                     start : cells[i],
17163                     end :  cells[i]
17164                 };
17165                 continue;
17166             }
17167             if (crow.start.getY() == cells[i].getY()) {
17168                 // on same row.
17169                 crow.end = cells[i];
17170                 continue;
17171             }
17172             // different row.
17173             rows.push(crow);
17174             crow = {
17175                 start: cells[i],
17176                 end : cells[i]
17177             };
17178             
17179         }
17180         
17181         rows.push(crow);
17182         ev.els = [];
17183         ev.rows = rows;
17184         ev.cells = cells;
17185         
17186         cells[0].events.push(ev);
17187         
17188         this.calevents.push(ev);
17189     },
17190     
17191     clearEvents: function() {
17192         
17193         if(!this.calevents){
17194             return;
17195         }
17196         
17197         Roo.each(this.cells.elements, function(c){
17198             c.row = 0;
17199             c.events = [];
17200             c.more = [];
17201         });
17202         
17203         Roo.each(this.calevents, function(e) {
17204             Roo.each(e.els, function(el) {
17205                 el.un('mouseenter' ,this.onEventEnter, this);
17206                 el.un('mouseleave' ,this.onEventLeave, this);
17207                 el.remove();
17208             },this);
17209         },this);
17210         
17211         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17212             e.remove();
17213         });
17214         
17215     },
17216     
17217     renderEvents: function()
17218     {   
17219         var _this = this;
17220         
17221         this.cells.each(function(c) {
17222             
17223             if(c.row < 5){
17224                 return;
17225             }
17226             
17227             var ev = c.events;
17228             
17229             var r = 4;
17230             if(c.row != c.events.length){
17231                 r = 4 - (4 - (c.row - c.events.length));
17232             }
17233             
17234             c.events = ev.slice(0, r);
17235             c.more = ev.slice(r);
17236             
17237             if(c.more.length && c.more.length == 1){
17238                 c.events.push(c.more.pop());
17239             }
17240             
17241             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17242             
17243         });
17244             
17245         this.cells.each(function(c) {
17246             
17247             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17248             
17249             
17250             for (var e = 0; e < c.events.length; e++){
17251                 var ev = c.events[e];
17252                 var rows = ev.rows;
17253                 
17254                 for(var i = 0; i < rows.length; i++) {
17255                 
17256                     // how many rows should it span..
17257
17258                     var  cfg = {
17259                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17260                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17261
17262                         unselectable : "on",
17263                         cn : [
17264                             {
17265                                 cls: 'fc-event-inner',
17266                                 cn : [
17267     //                                {
17268     //                                  tag:'span',
17269     //                                  cls: 'fc-event-time',
17270     //                                  html : cells.length > 1 ? '' : ev.time
17271     //                                },
17272                                     {
17273                                       tag:'span',
17274                                       cls: 'fc-event-title',
17275                                       html : String.format('{0}', ev.title)
17276                                     }
17277
17278
17279                                 ]
17280                             },
17281                             {
17282                                 cls: 'ui-resizable-handle ui-resizable-e',
17283                                 html : '&nbsp;&nbsp;&nbsp'
17284                             }
17285
17286                         ]
17287                     };
17288
17289                     if (i == 0) {
17290                         cfg.cls += ' fc-event-start';
17291                     }
17292                     if ((i+1) == rows.length) {
17293                         cfg.cls += ' fc-event-end';
17294                     }
17295
17296                     var ctr = _this.el.select('.fc-event-container',true).first();
17297                     var cg = ctr.createChild(cfg);
17298
17299                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17300                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17301
17302                     var r = (c.more.length) ? 1 : 0;
17303                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17304                     cg.setWidth(ebox.right - sbox.x -2);
17305
17306                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17307                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17308                     cg.on('click', _this.onEventClick, _this, ev);
17309
17310                     ev.els.push(cg);
17311                     
17312                 }
17313                 
17314             }
17315             
17316             
17317             if(c.more.length){
17318                 var  cfg = {
17319                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17320                     style : 'position: absolute',
17321                     unselectable : "on",
17322                     cn : [
17323                         {
17324                             cls: 'fc-event-inner',
17325                             cn : [
17326                                 {
17327                                   tag:'span',
17328                                   cls: 'fc-event-title',
17329                                   html : 'More'
17330                                 }
17331
17332
17333                             ]
17334                         },
17335                         {
17336                             cls: 'ui-resizable-handle ui-resizable-e',
17337                             html : '&nbsp;&nbsp;&nbsp'
17338                         }
17339
17340                     ]
17341                 };
17342
17343                 var ctr = _this.el.select('.fc-event-container',true).first();
17344                 var cg = ctr.createChild(cfg);
17345
17346                 var sbox = c.select('.fc-day-content',true).first().getBox();
17347                 var ebox = c.select('.fc-day-content',true).first().getBox();
17348                 //Roo.log(cg);
17349                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17350                 cg.setWidth(ebox.right - sbox.x -2);
17351
17352                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17353                 
17354             }
17355             
17356         });
17357         
17358         
17359         
17360     },
17361     
17362     onEventEnter: function (e, el,event,d) {
17363         this.fireEvent('evententer', this, el, event);
17364     },
17365     
17366     onEventLeave: function (e, el,event,d) {
17367         this.fireEvent('eventleave', this, el, event);
17368     },
17369     
17370     onEventClick: function (e, el,event,d) {
17371         this.fireEvent('eventclick', this, el, event);
17372     },
17373     
17374     onMonthChange: function () {
17375         this.store.load();
17376     },
17377     
17378     onMoreEventClick: function(e, el, more)
17379     {
17380         var _this = this;
17381         
17382         this.calpopover.placement = 'right';
17383         this.calpopover.setTitle('More');
17384         
17385         this.calpopover.setContent('');
17386         
17387         var ctr = this.calpopover.el.select('.popover-content', true).first();
17388         
17389         Roo.each(more, function(m){
17390             var cfg = {
17391                 cls : 'fc-event-hori fc-event-draggable',
17392                 html : m.title
17393             };
17394             var cg = ctr.createChild(cfg);
17395             
17396             cg.on('click', _this.onEventClick, _this, m);
17397         });
17398         
17399         this.calpopover.show(el);
17400         
17401         
17402     },
17403     
17404     onLoad: function () 
17405     {   
17406         this.calevents = [];
17407         var cal = this;
17408         
17409         if(this.store.getCount() > 0){
17410             this.store.data.each(function(d){
17411                cal.addItem({
17412                     id : d.data.id,
17413                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17414                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17415                     time : d.data.start_time,
17416                     title : d.data.title,
17417                     description : d.data.description,
17418                     venue : d.data.venue
17419                 });
17420             });
17421         }
17422         
17423         this.renderEvents();
17424         
17425         if(this.calevents.length && this.loadMask){
17426             this.maskEl.hide();
17427         }
17428     },
17429     
17430     onBeforeLoad: function()
17431     {
17432         this.clearEvents();
17433         if(this.loadMask){
17434             this.maskEl.show();
17435         }
17436     }
17437 });
17438
17439  
17440  /*
17441  * - LGPL
17442  *
17443  * element
17444  * 
17445  */
17446
17447 /**
17448  * @class Roo.bootstrap.Popover
17449  * @extends Roo.bootstrap.Component
17450  * Bootstrap Popover class
17451  * @cfg {String} html contents of the popover   (or false to use children..)
17452  * @cfg {String} title of popover (or false to hide)
17453  * @cfg {String} placement how it is placed
17454  * @cfg {String} trigger click || hover (or false to trigger manually)
17455  * @cfg {String} over what (parent or false to trigger manually.)
17456  * @cfg {Number} delay - delay before showing
17457  
17458  * @constructor
17459  * Create a new Popover
17460  * @param {Object} config The config object
17461  */
17462
17463 Roo.bootstrap.Popover = function(config){
17464     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17465     
17466     this.addEvents({
17467         // raw events
17468          /**
17469          * @event show
17470          * After the popover show
17471          * 
17472          * @param {Roo.bootstrap.Popover} this
17473          */
17474         "show" : true,
17475         /**
17476          * @event hide
17477          * After the popover hide
17478          * 
17479          * @param {Roo.bootstrap.Popover} this
17480          */
17481         "hide" : true
17482     });
17483 };
17484
17485 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17486     
17487     title: 'Fill in a title',
17488     html: false,
17489     
17490     placement : 'right',
17491     trigger : 'hover', // hover
17492     
17493     delay : 0,
17494     
17495     over: 'parent',
17496     
17497     can_build_overlaid : false,
17498     
17499     getChildContainer : function()
17500     {
17501         return this.el.select('.popover-content',true).first();
17502     },
17503     
17504     getAutoCreate : function(){
17505          
17506         var cfg = {
17507            cls : 'popover roo-dynamic',
17508            style: 'display:block',
17509            cn : [
17510                 {
17511                     cls : 'arrow'
17512                 },
17513                 {
17514                     cls : 'popover-inner',
17515                     cn : [
17516                         {
17517                             tag: 'h3',
17518                             cls: 'popover-title',
17519                             html : this.title
17520                         },
17521                         {
17522                             cls : 'popover-content',
17523                             html : this.html
17524                         }
17525                     ]
17526                     
17527                 }
17528            ]
17529         };
17530         
17531         return cfg;
17532     },
17533     setTitle: function(str)
17534     {
17535         this.title = str;
17536         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17537     },
17538     setContent: function(str)
17539     {
17540         this.html = str;
17541         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17542     },
17543     // as it get's added to the bottom of the page.
17544     onRender : function(ct, position)
17545     {
17546         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17547         if(!this.el){
17548             var cfg = Roo.apply({},  this.getAutoCreate());
17549             cfg.id = Roo.id();
17550             
17551             if (this.cls) {
17552                 cfg.cls += ' ' + this.cls;
17553             }
17554             if (this.style) {
17555                 cfg.style = this.style;
17556             }
17557             //Roo.log("adding to ");
17558             this.el = Roo.get(document.body).createChild(cfg, position);
17559 //            Roo.log(this.el);
17560         }
17561         this.initEvents();
17562     },
17563     
17564     initEvents : function()
17565     {
17566         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17567         this.el.enableDisplayMode('block');
17568         this.el.hide();
17569         if (this.over === false) {
17570             return; 
17571         }
17572         if (this.triggers === false) {
17573             return;
17574         }
17575         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17576         var triggers = this.trigger ? this.trigger.split(' ') : [];
17577         Roo.each(triggers, function(trigger) {
17578         
17579             if (trigger == 'click') {
17580                 on_el.on('click', this.toggle, this);
17581             } else if (trigger != 'manual') {
17582                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17583                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17584       
17585                 on_el.on(eventIn  ,this.enter, this);
17586                 on_el.on(eventOut, this.leave, this);
17587             }
17588         }, this);
17589         
17590     },
17591     
17592     
17593     // private
17594     timeout : null,
17595     hoverState : null,
17596     
17597     toggle : function () {
17598         this.hoverState == 'in' ? this.leave() : this.enter();
17599     },
17600     
17601     enter : function () {
17602         
17603         clearTimeout(this.timeout);
17604     
17605         this.hoverState = 'in';
17606     
17607         if (!this.delay || !this.delay.show) {
17608             this.show();
17609             return;
17610         }
17611         var _t = this;
17612         this.timeout = setTimeout(function () {
17613             if (_t.hoverState == 'in') {
17614                 _t.show();
17615             }
17616         }, this.delay.show)
17617     },
17618     
17619     leave : function() {
17620         clearTimeout(this.timeout);
17621     
17622         this.hoverState = 'out';
17623     
17624         if (!this.delay || !this.delay.hide) {
17625             this.hide();
17626             return;
17627         }
17628         var _t = this;
17629         this.timeout = setTimeout(function () {
17630             if (_t.hoverState == 'out') {
17631                 _t.hide();
17632             }
17633         }, this.delay.hide)
17634     },
17635     
17636     show : function (on_el)
17637     {
17638         if (!on_el) {
17639             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17640         }
17641         
17642         // set content.
17643         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17644         if (this.html !== false) {
17645             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17646         }
17647         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17648         if (!this.title.length) {
17649             this.el.select('.popover-title',true).hide();
17650         }
17651         
17652         var placement = typeof this.placement == 'function' ?
17653             this.placement.call(this, this.el, on_el) :
17654             this.placement;
17655             
17656         var autoToken = /\s?auto?\s?/i;
17657         var autoPlace = autoToken.test(placement);
17658         if (autoPlace) {
17659             placement = placement.replace(autoToken, '') || 'top';
17660         }
17661         
17662         //this.el.detach()
17663         //this.el.setXY([0,0]);
17664         this.el.show();
17665         this.el.dom.style.display='block';
17666         this.el.addClass(placement);
17667         
17668         //this.el.appendTo(on_el);
17669         
17670         var p = this.getPosition();
17671         var box = this.el.getBox();
17672         
17673         if (autoPlace) {
17674             // fixme..
17675         }
17676         var align = Roo.bootstrap.Popover.alignment[placement];
17677         
17678 //        Roo.log(align);
17679         this.el.alignTo(on_el, align[0],align[1]);
17680         //var arrow = this.el.select('.arrow',true).first();
17681         //arrow.set(align[2], 
17682         
17683         this.el.addClass('in');
17684         
17685         
17686         if (this.el.hasClass('fade')) {
17687             // fade it?
17688         }
17689         
17690         this.hoverState = 'in';
17691         
17692         this.fireEvent('show', this);
17693         
17694     },
17695     hide : function()
17696     {
17697         this.el.setXY([0,0]);
17698         this.el.removeClass('in');
17699         this.el.hide();
17700         this.hoverState = null;
17701         
17702         this.fireEvent('hide', this);
17703     }
17704     
17705 });
17706
17707 Roo.bootstrap.Popover.alignment = {
17708     'left' : ['r-l', [-10,0], 'right'],
17709     'right' : ['l-r', [10,0], 'left'],
17710     'bottom' : ['t-b', [0,10], 'top'],
17711     'top' : [ 'b-t', [0,-10], 'bottom']
17712 };
17713
17714  /*
17715  * - LGPL
17716  *
17717  * Progress
17718  * 
17719  */
17720
17721 /**
17722  * @class Roo.bootstrap.Progress
17723  * @extends Roo.bootstrap.Component
17724  * Bootstrap Progress class
17725  * @cfg {Boolean} striped striped of the progress bar
17726  * @cfg {Boolean} active animated of the progress bar
17727  * 
17728  * 
17729  * @constructor
17730  * Create a new Progress
17731  * @param {Object} config The config object
17732  */
17733
17734 Roo.bootstrap.Progress = function(config){
17735     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17736 };
17737
17738 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17739     
17740     striped : false,
17741     active: false,
17742     
17743     getAutoCreate : function(){
17744         var cfg = {
17745             tag: 'div',
17746             cls: 'progress'
17747         };
17748         
17749         
17750         if(this.striped){
17751             cfg.cls += ' progress-striped';
17752         }
17753       
17754         if(this.active){
17755             cfg.cls += ' active';
17756         }
17757         
17758         
17759         return cfg;
17760     }
17761    
17762 });
17763
17764  
17765
17766  /*
17767  * - LGPL
17768  *
17769  * ProgressBar
17770  * 
17771  */
17772
17773 /**
17774  * @class Roo.bootstrap.ProgressBar
17775  * @extends Roo.bootstrap.Component
17776  * Bootstrap ProgressBar class
17777  * @cfg {Number} aria_valuenow aria-value now
17778  * @cfg {Number} aria_valuemin aria-value min
17779  * @cfg {Number} aria_valuemax aria-value max
17780  * @cfg {String} label label for the progress bar
17781  * @cfg {String} panel (success | info | warning | danger )
17782  * @cfg {String} role role of the progress bar
17783  * @cfg {String} sr_only text
17784  * 
17785  * 
17786  * @constructor
17787  * Create a new ProgressBar
17788  * @param {Object} config The config object
17789  */
17790
17791 Roo.bootstrap.ProgressBar = function(config){
17792     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17793 };
17794
17795 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17796     
17797     aria_valuenow : 0,
17798     aria_valuemin : 0,
17799     aria_valuemax : 100,
17800     label : false,
17801     panel : false,
17802     role : false,
17803     sr_only: false,
17804     
17805     getAutoCreate : function()
17806     {
17807         
17808         var cfg = {
17809             tag: 'div',
17810             cls: 'progress-bar',
17811             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17812         };
17813         
17814         if(this.sr_only){
17815             cfg.cn = {
17816                 tag: 'span',
17817                 cls: 'sr-only',
17818                 html: this.sr_only
17819             }
17820         }
17821         
17822         if(this.role){
17823             cfg.role = this.role;
17824         }
17825         
17826         if(this.aria_valuenow){
17827             cfg['aria-valuenow'] = this.aria_valuenow;
17828         }
17829         
17830         if(this.aria_valuemin){
17831             cfg['aria-valuemin'] = this.aria_valuemin;
17832         }
17833         
17834         if(this.aria_valuemax){
17835             cfg['aria-valuemax'] = this.aria_valuemax;
17836         }
17837         
17838         if(this.label && !this.sr_only){
17839             cfg.html = this.label;
17840         }
17841         
17842         if(this.panel){
17843             cfg.cls += ' progress-bar-' + this.panel;
17844         }
17845         
17846         return cfg;
17847     },
17848     
17849     update : function(aria_valuenow)
17850     {
17851         this.aria_valuenow = aria_valuenow;
17852         
17853         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17854     }
17855    
17856 });
17857
17858  
17859
17860  /*
17861  * - LGPL
17862  *
17863  * column
17864  * 
17865  */
17866
17867 /**
17868  * @class Roo.bootstrap.TabGroup
17869  * @extends Roo.bootstrap.Column
17870  * Bootstrap Column class
17871  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17872  * @cfg {Boolean} carousel true to make the group behave like a carousel
17873  * @cfg {Boolean} bullets show bullets for the panels
17874  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17875  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17876  * @cfg {Boolean} showarrow (true|false) show arrow default true
17877  * 
17878  * @constructor
17879  * Create a new TabGroup
17880  * @param {Object} config The config object
17881  */
17882
17883 Roo.bootstrap.TabGroup = function(config){
17884     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17885     if (!this.navId) {
17886         this.navId = Roo.id();
17887     }
17888     this.tabs = [];
17889     Roo.bootstrap.TabGroup.register(this);
17890     
17891 };
17892
17893 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17894     
17895     carousel : false,
17896     transition : false,
17897     bullets : 0,
17898     timer : 0,
17899     autoslide : false,
17900     slideFn : false,
17901     slideOnTouch : false,
17902     showarrow : true,
17903     
17904     getAutoCreate : function()
17905     {
17906         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17907         
17908         cfg.cls += ' tab-content';
17909         
17910         if (this.carousel) {
17911             cfg.cls += ' carousel slide';
17912             
17913             cfg.cn = [{
17914                cls : 'carousel-inner',
17915                cn : []
17916             }];
17917         
17918             if(this.bullets  && !Roo.isTouch){
17919                 
17920                 var bullets = {
17921                     cls : 'carousel-bullets',
17922                     cn : []
17923                 };
17924                
17925                 if(this.bullets_cls){
17926                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17927                 }
17928                 
17929                 bullets.cn.push({
17930                     cls : 'clear'
17931                 });
17932                 
17933                 cfg.cn[0].cn.push(bullets);
17934             }
17935             
17936             if(this.showarrow){
17937                 cfg.cn[0].cn.push({
17938                     tag : 'div',
17939                     class : 'carousel-arrow',
17940                     cn : [
17941                         {
17942                             tag : 'div',
17943                             class : 'carousel-prev',
17944                             cn : [
17945                                 {
17946                                     tag : 'i',
17947                                     class : 'fa fa-chevron-left'
17948                                 }
17949                             ]
17950                         },
17951                         {
17952                             tag : 'div',
17953                             class : 'carousel-next',
17954                             cn : [
17955                                 {
17956                                     tag : 'i',
17957                                     class : 'fa fa-chevron-right'
17958                                 }
17959                             ]
17960                         }
17961                     ]
17962                 });
17963             }
17964             
17965         }
17966         
17967         return cfg;
17968     },
17969     
17970     initEvents:  function()
17971     {
17972 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17973 //            this.el.on("touchstart", this.onTouchStart, this);
17974 //        }
17975         
17976         if(this.autoslide){
17977             var _this = this;
17978             
17979             this.slideFn = window.setInterval(function() {
17980                 _this.showPanelNext();
17981             }, this.timer);
17982         }
17983         
17984         if(this.showarrow){
17985             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17986             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17987         }
17988         
17989         
17990     },
17991     
17992 //    onTouchStart : function(e, el, o)
17993 //    {
17994 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17995 //            return;
17996 //        }
17997 //        
17998 //        this.showPanelNext();
17999 //    },
18000     
18001     
18002     getChildContainer : function()
18003     {
18004         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18005     },
18006     
18007     /**
18008     * register a Navigation item
18009     * @param {Roo.bootstrap.NavItem} the navitem to add
18010     */
18011     register : function(item)
18012     {
18013         this.tabs.push( item);
18014         item.navId = this.navId; // not really needed..
18015         this.addBullet();
18016     
18017     },
18018     
18019     getActivePanel : function()
18020     {
18021         var r = false;
18022         Roo.each(this.tabs, function(t) {
18023             if (t.active) {
18024                 r = t;
18025                 return false;
18026             }
18027             return null;
18028         });
18029         return r;
18030         
18031     },
18032     getPanelByName : function(n)
18033     {
18034         var r = false;
18035         Roo.each(this.tabs, function(t) {
18036             if (t.tabId == n) {
18037                 r = t;
18038                 return false;
18039             }
18040             return null;
18041         });
18042         return r;
18043     },
18044     indexOfPanel : function(p)
18045     {
18046         var r = false;
18047         Roo.each(this.tabs, function(t,i) {
18048             if (t.tabId == p.tabId) {
18049                 r = i;
18050                 return false;
18051             }
18052             return null;
18053         });
18054         return r;
18055     },
18056     /**
18057      * show a specific panel
18058      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18059      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18060      */
18061     showPanel : function (pan)
18062     {
18063         if(this.transition || typeof(pan) == 'undefined'){
18064             Roo.log("waiting for the transitionend");
18065             return;
18066         }
18067         
18068         if (typeof(pan) == 'number') {
18069             pan = this.tabs[pan];
18070         }
18071         
18072         if (typeof(pan) == 'string') {
18073             pan = this.getPanelByName(pan);
18074         }
18075         
18076         var cur = this.getActivePanel();
18077         
18078         if(!pan || !cur){
18079             Roo.log('pan or acitve pan is undefined');
18080             return false;
18081         }
18082         
18083         if (pan.tabId == this.getActivePanel().tabId) {
18084             return true;
18085         }
18086         
18087         if (false === cur.fireEvent('beforedeactivate')) {
18088             return false;
18089         }
18090         
18091         if(this.bullets > 0 && !Roo.isTouch){
18092             this.setActiveBullet(this.indexOfPanel(pan));
18093         }
18094         
18095         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18096             
18097             this.transition = true;
18098             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18099             var lr = dir == 'next' ? 'left' : 'right';
18100             pan.el.addClass(dir); // or prev
18101             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18102             cur.el.addClass(lr); // or right
18103             pan.el.addClass(lr);
18104             
18105             var _this = this;
18106             cur.el.on('transitionend', function() {
18107                 Roo.log("trans end?");
18108                 
18109                 pan.el.removeClass([lr,dir]);
18110                 pan.setActive(true);
18111                 
18112                 cur.el.removeClass([lr]);
18113                 cur.setActive(false);
18114                 
18115                 _this.transition = false;
18116                 
18117             }, this, { single:  true } );
18118             
18119             return true;
18120         }
18121         
18122         cur.setActive(false);
18123         pan.setActive(true);
18124         
18125         return true;
18126         
18127     },
18128     showPanelNext : function()
18129     {
18130         var i = this.indexOfPanel(this.getActivePanel());
18131         
18132         if (i >= this.tabs.length - 1 && !this.autoslide) {
18133             return;
18134         }
18135         
18136         if (i >= this.tabs.length - 1 && this.autoslide) {
18137             i = -1;
18138         }
18139         
18140         this.showPanel(this.tabs[i+1]);
18141     },
18142     
18143     showPanelPrev : function()
18144     {
18145         var i = this.indexOfPanel(this.getActivePanel());
18146         
18147         if (i  < 1 && !this.autoslide) {
18148             return;
18149         }
18150         
18151         if (i < 1 && this.autoslide) {
18152             i = this.tabs.length;
18153         }
18154         
18155         this.showPanel(this.tabs[i-1]);
18156     },
18157     
18158     
18159     addBullet: function()
18160     {
18161         if(!this.bullets || Roo.isTouch){
18162             return;
18163         }
18164         var ctr = this.el.select('.carousel-bullets',true).first();
18165         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18166         var bullet = ctr.createChild({
18167             cls : 'bullet bullet-' + i
18168         },ctr.dom.lastChild);
18169         
18170         
18171         var _this = this;
18172         
18173         bullet.on('click', (function(e, el, o, ii, t){
18174
18175             e.preventDefault();
18176
18177             this.showPanel(ii);
18178
18179             if(this.autoslide && this.slideFn){
18180                 clearInterval(this.slideFn);
18181                 this.slideFn = window.setInterval(function() {
18182                     _this.showPanelNext();
18183                 }, this.timer);
18184             }
18185
18186         }).createDelegate(this, [i, bullet], true));
18187                 
18188         
18189     },
18190      
18191     setActiveBullet : function(i)
18192     {
18193         if(Roo.isTouch){
18194             return;
18195         }
18196         
18197         Roo.each(this.el.select('.bullet', true).elements, function(el){
18198             el.removeClass('selected');
18199         });
18200
18201         var bullet = this.el.select('.bullet-' + i, true).first();
18202         
18203         if(!bullet){
18204             return;
18205         }
18206         
18207         bullet.addClass('selected');
18208     }
18209     
18210     
18211   
18212 });
18213
18214  
18215
18216  
18217  
18218 Roo.apply(Roo.bootstrap.TabGroup, {
18219     
18220     groups: {},
18221      /**
18222     * register a Navigation Group
18223     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18224     */
18225     register : function(navgrp)
18226     {
18227         this.groups[navgrp.navId] = navgrp;
18228         
18229     },
18230     /**
18231     * fetch a Navigation Group based on the navigation ID
18232     * if one does not exist , it will get created.
18233     * @param {string} the navgroup to add
18234     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18235     */
18236     get: function(navId) {
18237         if (typeof(this.groups[navId]) == 'undefined') {
18238             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18239         }
18240         return this.groups[navId] ;
18241     }
18242     
18243     
18244     
18245 });
18246
18247  /*
18248  * - LGPL
18249  *
18250  * TabPanel
18251  * 
18252  */
18253
18254 /**
18255  * @class Roo.bootstrap.TabPanel
18256  * @extends Roo.bootstrap.Component
18257  * Bootstrap TabPanel class
18258  * @cfg {Boolean} active panel active
18259  * @cfg {String} html panel content
18260  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18261  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18262  * @cfg {String} href click to link..
18263  * 
18264  * 
18265  * @constructor
18266  * Create a new TabPanel
18267  * @param {Object} config The config object
18268  */
18269
18270 Roo.bootstrap.TabPanel = function(config){
18271     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18272     this.addEvents({
18273         /**
18274              * @event changed
18275              * Fires when the active status changes
18276              * @param {Roo.bootstrap.TabPanel} this
18277              * @param {Boolean} state the new state
18278             
18279          */
18280         'changed': true,
18281         /**
18282              * @event beforedeactivate
18283              * Fires before a tab is de-activated - can be used to do validation on a form.
18284              * @param {Roo.bootstrap.TabPanel} this
18285              * @return {Boolean} false if there is an error
18286             
18287          */
18288         'beforedeactivate': true
18289      });
18290     
18291     this.tabId = this.tabId || Roo.id();
18292   
18293 };
18294
18295 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18296     
18297     active: false,
18298     html: false,
18299     tabId: false,
18300     navId : false,
18301     href : '',
18302     
18303     getAutoCreate : function(){
18304         var cfg = {
18305             tag: 'div',
18306             // item is needed for carousel - not sure if it has any effect otherwise
18307             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18308             html: this.html || ''
18309         };
18310         
18311         if(this.active){
18312             cfg.cls += ' active';
18313         }
18314         
18315         if(this.tabId){
18316             cfg.tabId = this.tabId;
18317         }
18318         
18319         
18320         return cfg;
18321     },
18322     
18323     initEvents:  function()
18324     {
18325         var p = this.parent();
18326         
18327         this.navId = this.navId || p.navId;
18328         
18329         if (typeof(this.navId) != 'undefined') {
18330             // not really needed.. but just in case.. parent should be a NavGroup.
18331             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18332             
18333             tg.register(this);
18334             
18335             var i = tg.tabs.length - 1;
18336             
18337             if(this.active && tg.bullets > 0 && i < tg.bullets){
18338                 tg.setActiveBullet(i);
18339             }
18340         }
18341         
18342         this.el.on('click', this.onClick, this);
18343         
18344         if(Roo.isTouch){
18345             this.el.on("touchstart", this.onTouchStart, this);
18346             this.el.on("touchmove", this.onTouchMove, this);
18347             this.el.on("touchend", this.onTouchEnd, this);
18348         }
18349         
18350     },
18351     
18352     onRender : function(ct, position)
18353     {
18354         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18355     },
18356     
18357     setActive : function(state)
18358     {
18359         Roo.log("panel - set active " + this.tabId + "=" + state);
18360         
18361         this.active = state;
18362         if (!state) {
18363             this.el.removeClass('active');
18364             
18365         } else  if (!this.el.hasClass('active')) {
18366             this.el.addClass('active');
18367         }
18368         
18369         this.fireEvent('changed', this, state);
18370     },
18371     
18372     onClick : function(e)
18373     {
18374         e.preventDefault();
18375         
18376         if(!this.href.length){
18377             return;
18378         }
18379         
18380         window.location.href = this.href;
18381     },
18382     
18383     startX : 0,
18384     startY : 0,
18385     endX : 0,
18386     endY : 0,
18387     swiping : false,
18388     
18389     onTouchStart : function(e)
18390     {
18391         this.swiping = false;
18392         
18393         this.startX = e.browserEvent.touches[0].clientX;
18394         this.startY = e.browserEvent.touches[0].clientY;
18395     },
18396     
18397     onTouchMove : function(e)
18398     {
18399         this.swiping = true;
18400         
18401         this.endX = e.browserEvent.touches[0].clientX;
18402         this.endY = e.browserEvent.touches[0].clientY;
18403     },
18404     
18405     onTouchEnd : function(e)
18406     {
18407         if(!this.swiping){
18408             this.onClick(e);
18409             return;
18410         }
18411         
18412         var tabGroup = this.parent();
18413         
18414         if(this.endX > this.startX){ // swiping right
18415             tabGroup.showPanelPrev();
18416             return;
18417         }
18418         
18419         if(this.startX > this.endX){ // swiping left
18420             tabGroup.showPanelNext();
18421             return;
18422         }
18423     }
18424     
18425     
18426 });
18427  
18428
18429  
18430
18431  /*
18432  * - LGPL
18433  *
18434  * DateField
18435  * 
18436  */
18437
18438 /**
18439  * @class Roo.bootstrap.DateField
18440  * @extends Roo.bootstrap.Input
18441  * Bootstrap DateField class
18442  * @cfg {Number} weekStart default 0
18443  * @cfg {String} viewMode default empty, (months|years)
18444  * @cfg {String} minViewMode default empty, (months|years)
18445  * @cfg {Number} startDate default -Infinity
18446  * @cfg {Number} endDate default Infinity
18447  * @cfg {Boolean} todayHighlight default false
18448  * @cfg {Boolean} todayBtn default false
18449  * @cfg {Boolean} calendarWeeks default false
18450  * @cfg {Object} daysOfWeekDisabled default empty
18451  * @cfg {Boolean} singleMode default false (true | false)
18452  * 
18453  * @cfg {Boolean} keyboardNavigation default true
18454  * @cfg {String} language default en
18455  * 
18456  * @constructor
18457  * Create a new DateField
18458  * @param {Object} config The config object
18459  */
18460
18461 Roo.bootstrap.DateField = function(config){
18462     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18463      this.addEvents({
18464             /**
18465              * @event show
18466              * Fires when this field show.
18467              * @param {Roo.bootstrap.DateField} this
18468              * @param {Mixed} date The date value
18469              */
18470             show : true,
18471             /**
18472              * @event show
18473              * Fires when this field hide.
18474              * @param {Roo.bootstrap.DateField} this
18475              * @param {Mixed} date The date value
18476              */
18477             hide : true,
18478             /**
18479              * @event select
18480              * Fires when select a date.
18481              * @param {Roo.bootstrap.DateField} this
18482              * @param {Mixed} date The date value
18483              */
18484             select : true,
18485             /**
18486              * @event beforeselect
18487              * Fires when before select a date.
18488              * @param {Roo.bootstrap.DateField} this
18489              * @param {Mixed} date The date value
18490              */
18491             beforeselect : true
18492         });
18493 };
18494
18495 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18496     
18497     /**
18498      * @cfg {String} format
18499      * The default date format string which can be overriden for localization support.  The format must be
18500      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18501      */
18502     format : "m/d/y",
18503     /**
18504      * @cfg {String} altFormats
18505      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18506      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18507      */
18508     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18509     
18510     weekStart : 0,
18511     
18512     viewMode : '',
18513     
18514     minViewMode : '',
18515     
18516     todayHighlight : false,
18517     
18518     todayBtn: false,
18519     
18520     language: 'en',
18521     
18522     keyboardNavigation: true,
18523     
18524     calendarWeeks: false,
18525     
18526     startDate: -Infinity,
18527     
18528     endDate: Infinity,
18529     
18530     daysOfWeekDisabled: [],
18531     
18532     _events: [],
18533     
18534     singleMode : false,
18535     
18536     UTCDate: function()
18537     {
18538         return new Date(Date.UTC.apply(Date, arguments));
18539     },
18540     
18541     UTCToday: function()
18542     {
18543         var today = new Date();
18544         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18545     },
18546     
18547     getDate: function() {
18548             var d = this.getUTCDate();
18549             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18550     },
18551     
18552     getUTCDate: function() {
18553             return this.date;
18554     },
18555     
18556     setDate: function(d) {
18557             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18558     },
18559     
18560     setUTCDate: function(d) {
18561             this.date = d;
18562             this.setValue(this.formatDate(this.date));
18563     },
18564         
18565     onRender: function(ct, position)
18566     {
18567         
18568         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18569         
18570         this.language = this.language || 'en';
18571         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18572         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18573         
18574         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18575         this.format = this.format || 'm/d/y';
18576         this.isInline = false;
18577         this.isInput = true;
18578         this.component = this.el.select('.add-on', true).first() || false;
18579         this.component = (this.component && this.component.length === 0) ? false : this.component;
18580         this.hasInput = this.component && this.inputEl().length;
18581         
18582         if (typeof(this.minViewMode === 'string')) {
18583             switch (this.minViewMode) {
18584                 case 'months':
18585                     this.minViewMode = 1;
18586                     break;
18587                 case 'years':
18588                     this.minViewMode = 2;
18589                     break;
18590                 default:
18591                     this.minViewMode = 0;
18592                     break;
18593             }
18594         }
18595         
18596         if (typeof(this.viewMode === 'string')) {
18597             switch (this.viewMode) {
18598                 case 'months':
18599                     this.viewMode = 1;
18600                     break;
18601                 case 'years':
18602                     this.viewMode = 2;
18603                     break;
18604                 default:
18605                     this.viewMode = 0;
18606                     break;
18607             }
18608         }
18609                 
18610         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18611         
18612 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18613         
18614         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18615         
18616         this.picker().on('mousedown', this.onMousedown, this);
18617         this.picker().on('click', this.onClick, this);
18618         
18619         this.picker().addClass('datepicker-dropdown');
18620         
18621         this.startViewMode = this.viewMode;
18622         
18623         if(this.singleMode){
18624             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18625                 v.setVisibilityMode(Roo.Element.DISPLAY);
18626                 v.hide();
18627             });
18628             
18629             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18630                 v.setStyle('width', '189px');
18631             });
18632         }
18633         
18634         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18635             if(!this.calendarWeeks){
18636                 v.remove();
18637                 return;
18638             }
18639             
18640             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18641             v.attr('colspan', function(i, val){
18642                 return parseInt(val) + 1;
18643             });
18644         });
18645                         
18646         
18647         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18648         
18649         this.setStartDate(this.startDate);
18650         this.setEndDate(this.endDate);
18651         
18652         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18653         
18654         this.fillDow();
18655         this.fillMonths();
18656         this.update();
18657         this.showMode();
18658         
18659         if(this.isInline) {
18660             this.showPopup();
18661         }
18662     },
18663     
18664     picker : function()
18665     {
18666         return this.pickerEl;
18667 //        return this.el.select('.datepicker', true).first();
18668     },
18669     
18670     fillDow: function()
18671     {
18672         var dowCnt = this.weekStart;
18673         
18674         var dow = {
18675             tag: 'tr',
18676             cn: [
18677                 
18678             ]
18679         };
18680         
18681         if(this.calendarWeeks){
18682             dow.cn.push({
18683                 tag: 'th',
18684                 cls: 'cw',
18685                 html: '&nbsp;'
18686             })
18687         }
18688         
18689         while (dowCnt < this.weekStart + 7) {
18690             dow.cn.push({
18691                 tag: 'th',
18692                 cls: 'dow',
18693                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18694             });
18695         }
18696         
18697         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18698     },
18699     
18700     fillMonths: function()
18701     {    
18702         var i = 0;
18703         var months = this.picker().select('>.datepicker-months td', true).first();
18704         
18705         months.dom.innerHTML = '';
18706         
18707         while (i < 12) {
18708             var month = {
18709                 tag: 'span',
18710                 cls: 'month',
18711                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18712             };
18713             
18714             months.createChild(month);
18715         }
18716         
18717     },
18718     
18719     update: function()
18720     {
18721         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;
18722         
18723         if (this.date < this.startDate) {
18724             this.viewDate = new Date(this.startDate);
18725         } else if (this.date > this.endDate) {
18726             this.viewDate = new Date(this.endDate);
18727         } else {
18728             this.viewDate = new Date(this.date);
18729         }
18730         
18731         this.fill();
18732     },
18733     
18734     fill: function() 
18735     {
18736         var d = new Date(this.viewDate),
18737                 year = d.getUTCFullYear(),
18738                 month = d.getUTCMonth(),
18739                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18740                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18741                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18742                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18743                 currentDate = this.date && this.date.valueOf(),
18744                 today = this.UTCToday();
18745         
18746         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18747         
18748 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18749         
18750 //        this.picker.select('>tfoot th.today').
18751 //                                              .text(dates[this.language].today)
18752 //                                              .toggle(this.todayBtn !== false);
18753     
18754         this.updateNavArrows();
18755         this.fillMonths();
18756                                                 
18757         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18758         
18759         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18760          
18761         prevMonth.setUTCDate(day);
18762         
18763         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18764         
18765         var nextMonth = new Date(prevMonth);
18766         
18767         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18768         
18769         nextMonth = nextMonth.valueOf();
18770         
18771         var fillMonths = false;
18772         
18773         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18774         
18775         while(prevMonth.valueOf() <= nextMonth) {
18776             var clsName = '';
18777             
18778             if (prevMonth.getUTCDay() === this.weekStart) {
18779                 if(fillMonths){
18780                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18781                 }
18782                     
18783                 fillMonths = {
18784                     tag: 'tr',
18785                     cn: []
18786                 };
18787                 
18788                 if(this.calendarWeeks){
18789                     // ISO 8601: First week contains first thursday.
18790                     // ISO also states week starts on Monday, but we can be more abstract here.
18791                     var
18792                     // Start of current week: based on weekstart/current date
18793                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18794                     // Thursday of this week
18795                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18796                     // First Thursday of year, year from thursday
18797                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18798                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18799                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18800                     
18801                     fillMonths.cn.push({
18802                         tag: 'td',
18803                         cls: 'cw',
18804                         html: calWeek
18805                     });
18806                 }
18807             }
18808             
18809             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18810                 clsName += ' old';
18811             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18812                 clsName += ' new';
18813             }
18814             if (this.todayHighlight &&
18815                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18816                 prevMonth.getUTCMonth() == today.getMonth() &&
18817                 prevMonth.getUTCDate() == today.getDate()) {
18818                 clsName += ' today';
18819             }
18820             
18821             if (currentDate && prevMonth.valueOf() === currentDate) {
18822                 clsName += ' active';
18823             }
18824             
18825             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18826                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18827                     clsName += ' disabled';
18828             }
18829             
18830             fillMonths.cn.push({
18831                 tag: 'td',
18832                 cls: 'day ' + clsName,
18833                 html: prevMonth.getDate()
18834             });
18835             
18836             prevMonth.setDate(prevMonth.getDate()+1);
18837         }
18838           
18839         var currentYear = this.date && this.date.getUTCFullYear();
18840         var currentMonth = this.date && this.date.getUTCMonth();
18841         
18842         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18843         
18844         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18845             v.removeClass('active');
18846             
18847             if(currentYear === year && k === currentMonth){
18848                 v.addClass('active');
18849             }
18850             
18851             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18852                 v.addClass('disabled');
18853             }
18854             
18855         });
18856         
18857         
18858         year = parseInt(year/10, 10) * 10;
18859         
18860         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18861         
18862         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18863         
18864         year -= 1;
18865         for (var i = -1; i < 11; i++) {
18866             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18867                 tag: 'span',
18868                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18869                 html: year
18870             });
18871             
18872             year += 1;
18873         }
18874     },
18875     
18876     showMode: function(dir) 
18877     {
18878         if (dir) {
18879             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18880         }
18881         
18882         Roo.each(this.picker().select('>div',true).elements, function(v){
18883             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884             v.hide();
18885         });
18886         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18887     },
18888     
18889     place: function()
18890     {
18891         if(this.isInline) {
18892             return;
18893         }
18894         
18895         this.picker().removeClass(['bottom', 'top']);
18896         
18897         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18898             /*
18899              * place to the top of element!
18900              *
18901              */
18902             
18903             this.picker().addClass('top');
18904             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18905             
18906             return;
18907         }
18908         
18909         this.picker().addClass('bottom');
18910         
18911         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18912     },
18913     
18914     parseDate : function(value)
18915     {
18916         if(!value || value instanceof Date){
18917             return value;
18918         }
18919         var v = Date.parseDate(value, this.format);
18920         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18921             v = Date.parseDate(value, 'Y-m-d');
18922         }
18923         if(!v && this.altFormats){
18924             if(!this.altFormatsArray){
18925                 this.altFormatsArray = this.altFormats.split("|");
18926             }
18927             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18928                 v = Date.parseDate(value, this.altFormatsArray[i]);
18929             }
18930         }
18931         return v;
18932     },
18933     
18934     formatDate : function(date, fmt)
18935     {   
18936         return (!date || !(date instanceof Date)) ?
18937         date : date.dateFormat(fmt || this.format);
18938     },
18939     
18940     onFocus : function()
18941     {
18942         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18943         this.showPopup();
18944     },
18945     
18946     onBlur : function()
18947     {
18948         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18949         
18950         var d = this.inputEl().getValue();
18951         
18952         this.setValue(d);
18953                 
18954         this.hidePopup();
18955     },
18956     
18957     showPopup : function()
18958     {
18959         this.picker().show();
18960         this.update();
18961         this.place();
18962         
18963         this.fireEvent('showpopup', this, this.date);
18964     },
18965     
18966     hidePopup : function()
18967     {
18968         if(this.isInline) {
18969             return;
18970         }
18971         this.picker().hide();
18972         this.viewMode = this.startViewMode;
18973         this.showMode();
18974         
18975         this.fireEvent('hidepopup', this, this.date);
18976         
18977     },
18978     
18979     onMousedown: function(e)
18980     {
18981         e.stopPropagation();
18982         e.preventDefault();
18983     },
18984     
18985     keyup: function(e)
18986     {
18987         Roo.bootstrap.DateField.superclass.keyup.call(this);
18988         this.update();
18989     },
18990
18991     setValue: function(v)
18992     {
18993         if(this.fireEvent('beforeselect', this, v) !== false){
18994             var d = new Date(this.parseDate(v) ).clearTime();
18995         
18996             if(isNaN(d.getTime())){
18997                 this.date = this.viewDate = '';
18998                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18999                 return;
19000             }
19001
19002             v = this.formatDate(d);
19003
19004             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19005
19006             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19007
19008             this.update();
19009
19010             this.fireEvent('select', this, this.date);
19011         }
19012     },
19013     
19014     getValue: function()
19015     {
19016         return this.formatDate(this.date);
19017     },
19018     
19019     fireKey: function(e)
19020     {
19021         if (!this.picker().isVisible()){
19022             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19023                 this.showPopup();
19024             }
19025             return;
19026         }
19027         
19028         var dateChanged = false,
19029         dir, day, month,
19030         newDate, newViewDate;
19031         
19032         switch(e.keyCode){
19033             case 27: // escape
19034                 this.hidePopup();
19035                 e.preventDefault();
19036                 break;
19037             case 37: // left
19038             case 39: // right
19039                 if (!this.keyboardNavigation) {
19040                     break;
19041                 }
19042                 dir = e.keyCode == 37 ? -1 : 1;
19043                 
19044                 if (e.ctrlKey){
19045                     newDate = this.moveYear(this.date, dir);
19046                     newViewDate = this.moveYear(this.viewDate, dir);
19047                 } else if (e.shiftKey){
19048                     newDate = this.moveMonth(this.date, dir);
19049                     newViewDate = this.moveMonth(this.viewDate, dir);
19050                 } else {
19051                     newDate = new Date(this.date);
19052                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19053                     newViewDate = new Date(this.viewDate);
19054                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19055                 }
19056                 if (this.dateWithinRange(newDate)){
19057                     this.date = newDate;
19058                     this.viewDate = newViewDate;
19059                     this.setValue(this.formatDate(this.date));
19060 //                    this.update();
19061                     e.preventDefault();
19062                     dateChanged = true;
19063                 }
19064                 break;
19065             case 38: // up
19066             case 40: // down
19067                 if (!this.keyboardNavigation) {
19068                     break;
19069                 }
19070                 dir = e.keyCode == 38 ? -1 : 1;
19071                 if (e.ctrlKey){
19072                     newDate = this.moveYear(this.date, dir);
19073                     newViewDate = this.moveYear(this.viewDate, dir);
19074                 } else if (e.shiftKey){
19075                     newDate = this.moveMonth(this.date, dir);
19076                     newViewDate = this.moveMonth(this.viewDate, dir);
19077                 } else {
19078                     newDate = new Date(this.date);
19079                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19080                     newViewDate = new Date(this.viewDate);
19081                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19082                 }
19083                 if (this.dateWithinRange(newDate)){
19084                     this.date = newDate;
19085                     this.viewDate = newViewDate;
19086                     this.setValue(this.formatDate(this.date));
19087 //                    this.update();
19088                     e.preventDefault();
19089                     dateChanged = true;
19090                 }
19091                 break;
19092             case 13: // enter
19093                 this.setValue(this.formatDate(this.date));
19094                 this.hidePopup();
19095                 e.preventDefault();
19096                 break;
19097             case 9: // tab
19098                 this.setValue(this.formatDate(this.date));
19099                 this.hidePopup();
19100                 break;
19101             case 16: // shift
19102             case 17: // ctrl
19103             case 18: // alt
19104                 break;
19105             default :
19106                 this.hide();
19107                 
19108         }
19109     },
19110     
19111     
19112     onClick: function(e) 
19113     {
19114         e.stopPropagation();
19115         e.preventDefault();
19116         
19117         var target = e.getTarget();
19118         
19119         if(target.nodeName.toLowerCase() === 'i'){
19120             target = Roo.get(target).dom.parentNode;
19121         }
19122         
19123         var nodeName = target.nodeName;
19124         var className = target.className;
19125         var html = target.innerHTML;
19126         //Roo.log(nodeName);
19127         
19128         switch(nodeName.toLowerCase()) {
19129             case 'th':
19130                 switch(className) {
19131                     case 'switch':
19132                         this.showMode(1);
19133                         break;
19134                     case 'prev':
19135                     case 'next':
19136                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19137                         switch(this.viewMode){
19138                                 case 0:
19139                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19140                                         break;
19141                                 case 1:
19142                                 case 2:
19143                                         this.viewDate = this.moveYear(this.viewDate, dir);
19144                                         break;
19145                         }
19146                         this.fill();
19147                         break;
19148                     case 'today':
19149                         var date = new Date();
19150                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19151 //                        this.fill()
19152                         this.setValue(this.formatDate(this.date));
19153                         
19154                         this.hidePopup();
19155                         break;
19156                 }
19157                 break;
19158             case 'span':
19159                 if (className.indexOf('disabled') < 0) {
19160                     this.viewDate.setUTCDate(1);
19161                     if (className.indexOf('month') > -1) {
19162                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19163                     } else {
19164                         var year = parseInt(html, 10) || 0;
19165                         this.viewDate.setUTCFullYear(year);
19166                         
19167                     }
19168                     
19169                     if(this.singleMode){
19170                         this.setValue(this.formatDate(this.viewDate));
19171                         this.hidePopup();
19172                         return;
19173                     }
19174                     
19175                     this.showMode(-1);
19176                     this.fill();
19177                 }
19178                 break;
19179                 
19180             case 'td':
19181                 //Roo.log(className);
19182                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19183                     var day = parseInt(html, 10) || 1;
19184                     var year = this.viewDate.getUTCFullYear(),
19185                         month = this.viewDate.getUTCMonth();
19186
19187                     if (className.indexOf('old') > -1) {
19188                         if(month === 0 ){
19189                             month = 11;
19190                             year -= 1;
19191                         }else{
19192                             month -= 1;
19193                         }
19194                     } else if (className.indexOf('new') > -1) {
19195                         if (month == 11) {
19196                             month = 0;
19197                             year += 1;
19198                         } else {
19199                             month += 1;
19200                         }
19201                     }
19202                     //Roo.log([year,month,day]);
19203                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19204                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19205 //                    this.fill();
19206                     //Roo.log(this.formatDate(this.date));
19207                     this.setValue(this.formatDate(this.date));
19208                     this.hidePopup();
19209                 }
19210                 break;
19211         }
19212     },
19213     
19214     setStartDate: function(startDate)
19215     {
19216         this.startDate = startDate || -Infinity;
19217         if (this.startDate !== -Infinity) {
19218             this.startDate = this.parseDate(this.startDate);
19219         }
19220         this.update();
19221         this.updateNavArrows();
19222     },
19223
19224     setEndDate: function(endDate)
19225     {
19226         this.endDate = endDate || Infinity;
19227         if (this.endDate !== Infinity) {
19228             this.endDate = this.parseDate(this.endDate);
19229         }
19230         this.update();
19231         this.updateNavArrows();
19232     },
19233     
19234     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19235     {
19236         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19237         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19238             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19239         }
19240         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19241             return parseInt(d, 10);
19242         });
19243         this.update();
19244         this.updateNavArrows();
19245     },
19246     
19247     updateNavArrows: function() 
19248     {
19249         if(this.singleMode){
19250             return;
19251         }
19252         
19253         var d = new Date(this.viewDate),
19254         year = d.getUTCFullYear(),
19255         month = d.getUTCMonth();
19256         
19257         Roo.each(this.picker().select('.prev', true).elements, function(v){
19258             v.show();
19259             switch (this.viewMode) {
19260                 case 0:
19261
19262                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19263                         v.hide();
19264                     }
19265                     break;
19266                 case 1:
19267                 case 2:
19268                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19269                         v.hide();
19270                     }
19271                     break;
19272             }
19273         });
19274         
19275         Roo.each(this.picker().select('.next', true).elements, function(v){
19276             v.show();
19277             switch (this.viewMode) {
19278                 case 0:
19279
19280                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19281                         v.hide();
19282                     }
19283                     break;
19284                 case 1:
19285                 case 2:
19286                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19287                         v.hide();
19288                     }
19289                     break;
19290             }
19291         })
19292     },
19293     
19294     moveMonth: function(date, dir)
19295     {
19296         if (!dir) {
19297             return date;
19298         }
19299         var new_date = new Date(date.valueOf()),
19300         day = new_date.getUTCDate(),
19301         month = new_date.getUTCMonth(),
19302         mag = Math.abs(dir),
19303         new_month, test;
19304         dir = dir > 0 ? 1 : -1;
19305         if (mag == 1){
19306             test = dir == -1
19307             // If going back one month, make sure month is not current month
19308             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19309             ? function(){
19310                 return new_date.getUTCMonth() == month;
19311             }
19312             // If going forward one month, make sure month is as expected
19313             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19314             : function(){
19315                 return new_date.getUTCMonth() != new_month;
19316             };
19317             new_month = month + dir;
19318             new_date.setUTCMonth(new_month);
19319             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19320             if (new_month < 0 || new_month > 11) {
19321                 new_month = (new_month + 12) % 12;
19322             }
19323         } else {
19324             // For magnitudes >1, move one month at a time...
19325             for (var i=0; i<mag; i++) {
19326                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19327                 new_date = this.moveMonth(new_date, dir);
19328             }
19329             // ...then reset the day, keeping it in the new month
19330             new_month = new_date.getUTCMonth();
19331             new_date.setUTCDate(day);
19332             test = function(){
19333                 return new_month != new_date.getUTCMonth();
19334             };
19335         }
19336         // Common date-resetting loop -- if date is beyond end of month, make it
19337         // end of month
19338         while (test()){
19339             new_date.setUTCDate(--day);
19340             new_date.setUTCMonth(new_month);
19341         }
19342         return new_date;
19343     },
19344
19345     moveYear: function(date, dir)
19346     {
19347         return this.moveMonth(date, dir*12);
19348     },
19349
19350     dateWithinRange: function(date)
19351     {
19352         return date >= this.startDate && date <= this.endDate;
19353     },
19354
19355     
19356     remove: function() 
19357     {
19358         this.picker().remove();
19359     },
19360     
19361     validateValue : function(value)
19362     {
19363         if(this.getVisibilityEl().hasClass('hidden')){
19364             return true;
19365         }
19366         
19367         if(value.length < 1)  {
19368             if(this.allowBlank){
19369                 return true;
19370             }
19371             return false;
19372         }
19373         
19374         if(value.length < this.minLength){
19375             return false;
19376         }
19377         if(value.length > this.maxLength){
19378             return false;
19379         }
19380         if(this.vtype){
19381             var vt = Roo.form.VTypes;
19382             if(!vt[this.vtype](value, this)){
19383                 return false;
19384             }
19385         }
19386         if(typeof this.validator == "function"){
19387             var msg = this.validator(value);
19388             if(msg !== true){
19389                 return false;
19390             }
19391         }
19392         
19393         if(this.regex && !this.regex.test(value)){
19394             return false;
19395         }
19396         
19397         if(typeof(this.parseDate(value)) == 'undefined'){
19398             return false;
19399         }
19400         
19401         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19402             return false;
19403         }      
19404         
19405         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19406             return false;
19407         } 
19408         
19409         
19410         return true;
19411     },
19412     
19413     reset : function()
19414     {
19415         this.date = this.viewDate = '';
19416         
19417         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19418     }
19419    
19420 });
19421
19422 Roo.apply(Roo.bootstrap.DateField,  {
19423     
19424     head : {
19425         tag: 'thead',
19426         cn: [
19427         {
19428             tag: 'tr',
19429             cn: [
19430             {
19431                 tag: 'th',
19432                 cls: 'prev',
19433                 html: '<i class="fa fa-arrow-left"/>'
19434             },
19435             {
19436                 tag: 'th',
19437                 cls: 'switch',
19438                 colspan: '5'
19439             },
19440             {
19441                 tag: 'th',
19442                 cls: 'next',
19443                 html: '<i class="fa fa-arrow-right"/>'
19444             }
19445
19446             ]
19447         }
19448         ]
19449     },
19450     
19451     content : {
19452         tag: 'tbody',
19453         cn: [
19454         {
19455             tag: 'tr',
19456             cn: [
19457             {
19458                 tag: 'td',
19459                 colspan: '7'
19460             }
19461             ]
19462         }
19463         ]
19464     },
19465     
19466     footer : {
19467         tag: 'tfoot',
19468         cn: [
19469         {
19470             tag: 'tr',
19471             cn: [
19472             {
19473                 tag: 'th',
19474                 colspan: '7',
19475                 cls: 'today'
19476             }
19477                     
19478             ]
19479         }
19480         ]
19481     },
19482     
19483     dates:{
19484         en: {
19485             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19486             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19487             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19488             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19489             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19490             today: "Today"
19491         }
19492     },
19493     
19494     modes: [
19495     {
19496         clsName: 'days',
19497         navFnc: 'Month',
19498         navStep: 1
19499     },
19500     {
19501         clsName: 'months',
19502         navFnc: 'FullYear',
19503         navStep: 1
19504     },
19505     {
19506         clsName: 'years',
19507         navFnc: 'FullYear',
19508         navStep: 10
19509     }]
19510 });
19511
19512 Roo.apply(Roo.bootstrap.DateField,  {
19513   
19514     template : {
19515         tag: 'div',
19516         cls: 'datepicker dropdown-menu roo-dynamic',
19517         cn: [
19518         {
19519             tag: 'div',
19520             cls: 'datepicker-days',
19521             cn: [
19522             {
19523                 tag: 'table',
19524                 cls: 'table-condensed',
19525                 cn:[
19526                 Roo.bootstrap.DateField.head,
19527                 {
19528                     tag: 'tbody'
19529                 },
19530                 Roo.bootstrap.DateField.footer
19531                 ]
19532             }
19533             ]
19534         },
19535         {
19536             tag: 'div',
19537             cls: 'datepicker-months',
19538             cn: [
19539             {
19540                 tag: 'table',
19541                 cls: 'table-condensed',
19542                 cn:[
19543                 Roo.bootstrap.DateField.head,
19544                 Roo.bootstrap.DateField.content,
19545                 Roo.bootstrap.DateField.footer
19546                 ]
19547             }
19548             ]
19549         },
19550         {
19551             tag: 'div',
19552             cls: 'datepicker-years',
19553             cn: [
19554             {
19555                 tag: 'table',
19556                 cls: 'table-condensed',
19557                 cn:[
19558                 Roo.bootstrap.DateField.head,
19559                 Roo.bootstrap.DateField.content,
19560                 Roo.bootstrap.DateField.footer
19561                 ]
19562             }
19563             ]
19564         }
19565         ]
19566     }
19567 });
19568
19569  
19570
19571  /*
19572  * - LGPL
19573  *
19574  * TimeField
19575  * 
19576  */
19577
19578 /**
19579  * @class Roo.bootstrap.TimeField
19580  * @extends Roo.bootstrap.Input
19581  * Bootstrap DateField class
19582  * 
19583  * 
19584  * @constructor
19585  * Create a new TimeField
19586  * @param {Object} config The config object
19587  */
19588
19589 Roo.bootstrap.TimeField = function(config){
19590     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19591     this.addEvents({
19592             /**
19593              * @event show
19594              * Fires when this field show.
19595              * @param {Roo.bootstrap.DateField} thisthis
19596              * @param {Mixed} date The date value
19597              */
19598             show : true,
19599             /**
19600              * @event show
19601              * Fires when this field hide.
19602              * @param {Roo.bootstrap.DateField} this
19603              * @param {Mixed} date The date value
19604              */
19605             hide : true,
19606             /**
19607              * @event select
19608              * Fires when select a date.
19609              * @param {Roo.bootstrap.DateField} this
19610              * @param {Mixed} date The date value
19611              */
19612             select : true
19613         });
19614 };
19615
19616 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19617     
19618     /**
19619      * @cfg {String} format
19620      * The default time format string which can be overriden for localization support.  The format must be
19621      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19622      */
19623     format : "H:i",
19624        
19625     onRender: function(ct, position)
19626     {
19627         
19628         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19629                 
19630         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19631         
19632         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19633         
19634         this.pop = this.picker().select('>.datepicker-time',true).first();
19635         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19636         
19637         this.picker().on('mousedown', this.onMousedown, this);
19638         this.picker().on('click', this.onClick, this);
19639         
19640         this.picker().addClass('datepicker-dropdown');
19641     
19642         this.fillTime();
19643         this.update();
19644             
19645         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19646         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19647         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19648         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19649         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19650         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19651
19652     },
19653     
19654     fireKey: function(e){
19655         if (!this.picker().isVisible()){
19656             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19657                 this.show();
19658             }
19659             return;
19660         }
19661
19662         e.preventDefault();
19663         
19664         switch(e.keyCode){
19665             case 27: // escape
19666                 this.hide();
19667                 break;
19668             case 37: // left
19669             case 39: // right
19670                 this.onTogglePeriod();
19671                 break;
19672             case 38: // up
19673                 this.onIncrementMinutes();
19674                 break;
19675             case 40: // down
19676                 this.onDecrementMinutes();
19677                 break;
19678             case 13: // enter
19679             case 9: // tab
19680                 this.setTime();
19681                 break;
19682         }
19683     },
19684     
19685     onClick: function(e) {
19686         e.stopPropagation();
19687         e.preventDefault();
19688     },
19689     
19690     picker : function()
19691     {
19692         return this.el.select('.datepicker', true).first();
19693     },
19694     
19695     fillTime: function()
19696     {    
19697         var time = this.pop.select('tbody', true).first();
19698         
19699         time.dom.innerHTML = '';
19700         
19701         time.createChild({
19702             tag: 'tr',
19703             cn: [
19704                 {
19705                     tag: 'td',
19706                     cn: [
19707                         {
19708                             tag: 'a',
19709                             href: '#',
19710                             cls: 'btn',
19711                             cn: [
19712                                 {
19713                                     tag: 'span',
19714                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19715                                 }
19716                             ]
19717                         } 
19718                     ]
19719                 },
19720                 {
19721                     tag: 'td',
19722                     cls: 'separator'
19723                 },
19724                 {
19725                     tag: 'td',
19726                     cn: [
19727                         {
19728                             tag: 'a',
19729                             href: '#',
19730                             cls: 'btn',
19731                             cn: [
19732                                 {
19733                                     tag: 'span',
19734                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19735                                 }
19736                             ]
19737                         }
19738                     ]
19739                 },
19740                 {
19741                     tag: 'td',
19742                     cls: 'separator'
19743                 }
19744             ]
19745         });
19746         
19747         time.createChild({
19748             tag: 'tr',
19749             cn: [
19750                 {
19751                     tag: 'td',
19752                     cn: [
19753                         {
19754                             tag: 'span',
19755                             cls: 'timepicker-hour',
19756                             html: '00'
19757                         }  
19758                     ]
19759                 },
19760                 {
19761                     tag: 'td',
19762                     cls: 'separator',
19763                     html: ':'
19764                 },
19765                 {
19766                     tag: 'td',
19767                     cn: [
19768                         {
19769                             tag: 'span',
19770                             cls: 'timepicker-minute',
19771                             html: '00'
19772                         }  
19773                     ]
19774                 },
19775                 {
19776                     tag: 'td',
19777                     cls: 'separator'
19778                 },
19779                 {
19780                     tag: 'td',
19781                     cn: [
19782                         {
19783                             tag: 'button',
19784                             type: 'button',
19785                             cls: 'btn btn-primary period',
19786                             html: 'AM'
19787                             
19788                         }
19789                     ]
19790                 }
19791             ]
19792         });
19793         
19794         time.createChild({
19795             tag: 'tr',
19796             cn: [
19797                 {
19798                     tag: 'td',
19799                     cn: [
19800                         {
19801                             tag: 'a',
19802                             href: '#',
19803                             cls: 'btn',
19804                             cn: [
19805                                 {
19806                                     tag: 'span',
19807                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19808                                 }
19809                             ]
19810                         }
19811                     ]
19812                 },
19813                 {
19814                     tag: 'td',
19815                     cls: 'separator'
19816                 },
19817                 {
19818                     tag: 'td',
19819                     cn: [
19820                         {
19821                             tag: 'a',
19822                             href: '#',
19823                             cls: 'btn',
19824                             cn: [
19825                                 {
19826                                     tag: 'span',
19827                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19828                                 }
19829                             ]
19830                         }
19831                     ]
19832                 },
19833                 {
19834                     tag: 'td',
19835                     cls: 'separator'
19836                 }
19837             ]
19838         });
19839         
19840     },
19841     
19842     update: function()
19843     {
19844         
19845         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19846         
19847         this.fill();
19848     },
19849     
19850     fill: function() 
19851     {
19852         var hours = this.time.getHours();
19853         var minutes = this.time.getMinutes();
19854         var period = 'AM';
19855         
19856         if(hours > 11){
19857             period = 'PM';
19858         }
19859         
19860         if(hours == 0){
19861             hours = 12;
19862         }
19863         
19864         
19865         if(hours > 12){
19866             hours = hours - 12;
19867         }
19868         
19869         if(hours < 10){
19870             hours = '0' + hours;
19871         }
19872         
19873         if(minutes < 10){
19874             minutes = '0' + minutes;
19875         }
19876         
19877         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19878         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19879         this.pop.select('button', true).first().dom.innerHTML = period;
19880         
19881     },
19882     
19883     place: function()
19884     {   
19885         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19886         
19887         var cls = ['bottom'];
19888         
19889         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19890             cls.pop();
19891             cls.push('top');
19892         }
19893         
19894         cls.push('right');
19895         
19896         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19897             cls.pop();
19898             cls.push('left');
19899         }
19900         
19901         this.picker().addClass(cls.join('-'));
19902         
19903         var _this = this;
19904         
19905         Roo.each(cls, function(c){
19906             if(c == 'bottom'){
19907                 _this.picker().setTop(_this.inputEl().getHeight());
19908                 return;
19909             }
19910             if(c == 'top'){
19911                 _this.picker().setTop(0 - _this.picker().getHeight());
19912                 return;
19913             }
19914             
19915             if(c == 'left'){
19916                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19917                 return;
19918             }
19919             if(c == 'right'){
19920                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19921                 return;
19922             }
19923         });
19924         
19925     },
19926   
19927     onFocus : function()
19928     {
19929         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19930         this.show();
19931     },
19932     
19933     onBlur : function()
19934     {
19935         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19936         this.hide();
19937     },
19938     
19939     show : function()
19940     {
19941         this.picker().show();
19942         this.pop.show();
19943         this.update();
19944         this.place();
19945         
19946         this.fireEvent('show', this, this.date);
19947     },
19948     
19949     hide : function()
19950     {
19951         this.picker().hide();
19952         this.pop.hide();
19953         
19954         this.fireEvent('hide', this, this.date);
19955     },
19956     
19957     setTime : function()
19958     {
19959         this.hide();
19960         this.setValue(this.time.format(this.format));
19961         
19962         this.fireEvent('select', this, this.date);
19963         
19964         
19965     },
19966     
19967     onMousedown: function(e){
19968         e.stopPropagation();
19969         e.preventDefault();
19970     },
19971     
19972     onIncrementHours: function()
19973     {
19974         Roo.log('onIncrementHours');
19975         this.time = this.time.add(Date.HOUR, 1);
19976         this.update();
19977         
19978     },
19979     
19980     onDecrementHours: function()
19981     {
19982         Roo.log('onDecrementHours');
19983         this.time = this.time.add(Date.HOUR, -1);
19984         this.update();
19985     },
19986     
19987     onIncrementMinutes: function()
19988     {
19989         Roo.log('onIncrementMinutes');
19990         this.time = this.time.add(Date.MINUTE, 1);
19991         this.update();
19992     },
19993     
19994     onDecrementMinutes: function()
19995     {
19996         Roo.log('onDecrementMinutes');
19997         this.time = this.time.add(Date.MINUTE, -1);
19998         this.update();
19999     },
20000     
20001     onTogglePeriod: function()
20002     {
20003         Roo.log('onTogglePeriod');
20004         this.time = this.time.add(Date.HOUR, 12);
20005         this.update();
20006     }
20007     
20008    
20009 });
20010
20011 Roo.apply(Roo.bootstrap.TimeField,  {
20012     
20013     content : {
20014         tag: 'tbody',
20015         cn: [
20016             {
20017                 tag: 'tr',
20018                 cn: [
20019                 {
20020                     tag: 'td',
20021                     colspan: '7'
20022                 }
20023                 ]
20024             }
20025         ]
20026     },
20027     
20028     footer : {
20029         tag: 'tfoot',
20030         cn: [
20031             {
20032                 tag: 'tr',
20033                 cn: [
20034                 {
20035                     tag: 'th',
20036                     colspan: '7',
20037                     cls: '',
20038                     cn: [
20039                         {
20040                             tag: 'button',
20041                             cls: 'btn btn-info ok',
20042                             html: 'OK'
20043                         }
20044                     ]
20045                 }
20046
20047                 ]
20048             }
20049         ]
20050     }
20051 });
20052
20053 Roo.apply(Roo.bootstrap.TimeField,  {
20054   
20055     template : {
20056         tag: 'div',
20057         cls: 'datepicker dropdown-menu',
20058         cn: [
20059             {
20060                 tag: 'div',
20061                 cls: 'datepicker-time',
20062                 cn: [
20063                 {
20064                     tag: 'table',
20065                     cls: 'table-condensed',
20066                     cn:[
20067                     Roo.bootstrap.TimeField.content,
20068                     Roo.bootstrap.TimeField.footer
20069                     ]
20070                 }
20071                 ]
20072             }
20073         ]
20074     }
20075 });
20076
20077  
20078
20079  /*
20080  * - LGPL
20081  *
20082  * MonthField
20083  * 
20084  */
20085
20086 /**
20087  * @class Roo.bootstrap.MonthField
20088  * @extends Roo.bootstrap.Input
20089  * Bootstrap MonthField class
20090  * 
20091  * @cfg {String} language default en
20092  * 
20093  * @constructor
20094  * Create a new MonthField
20095  * @param {Object} config The config object
20096  */
20097
20098 Roo.bootstrap.MonthField = function(config){
20099     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20100     
20101     this.addEvents({
20102         /**
20103          * @event show
20104          * Fires when this field show.
20105          * @param {Roo.bootstrap.MonthField} this
20106          * @param {Mixed} date The date value
20107          */
20108         show : true,
20109         /**
20110          * @event show
20111          * Fires when this field hide.
20112          * @param {Roo.bootstrap.MonthField} this
20113          * @param {Mixed} date The date value
20114          */
20115         hide : true,
20116         /**
20117          * @event select
20118          * Fires when select a date.
20119          * @param {Roo.bootstrap.MonthField} this
20120          * @param {String} oldvalue The old value
20121          * @param {String} newvalue The new value
20122          */
20123         select : true
20124     });
20125 };
20126
20127 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20128     
20129     onRender: function(ct, position)
20130     {
20131         
20132         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20133         
20134         this.language = this.language || 'en';
20135         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20136         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20137         
20138         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20139         this.isInline = false;
20140         this.isInput = true;
20141         this.component = this.el.select('.add-on', true).first() || false;
20142         this.component = (this.component && this.component.length === 0) ? false : this.component;
20143         this.hasInput = this.component && this.inputEL().length;
20144         
20145         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20146         
20147         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20148         
20149         this.picker().on('mousedown', this.onMousedown, this);
20150         this.picker().on('click', this.onClick, this);
20151         
20152         this.picker().addClass('datepicker-dropdown');
20153         
20154         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20155             v.setStyle('width', '189px');
20156         });
20157         
20158         this.fillMonths();
20159         
20160         this.update();
20161         
20162         if(this.isInline) {
20163             this.show();
20164         }
20165         
20166     },
20167     
20168     setValue: function(v, suppressEvent)
20169     {   
20170         var o = this.getValue();
20171         
20172         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20173         
20174         this.update();
20175
20176         if(suppressEvent !== true){
20177             this.fireEvent('select', this, o, v);
20178         }
20179         
20180     },
20181     
20182     getValue: function()
20183     {
20184         return this.value;
20185     },
20186     
20187     onClick: function(e) 
20188     {
20189         e.stopPropagation();
20190         e.preventDefault();
20191         
20192         var target = e.getTarget();
20193         
20194         if(target.nodeName.toLowerCase() === 'i'){
20195             target = Roo.get(target).dom.parentNode;
20196         }
20197         
20198         var nodeName = target.nodeName;
20199         var className = target.className;
20200         var html = target.innerHTML;
20201         
20202         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20203             return;
20204         }
20205         
20206         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20207         
20208         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20209         
20210         this.hide();
20211                         
20212     },
20213     
20214     picker : function()
20215     {
20216         return this.pickerEl;
20217     },
20218     
20219     fillMonths: function()
20220     {    
20221         var i = 0;
20222         var months = this.picker().select('>.datepicker-months td', true).first();
20223         
20224         months.dom.innerHTML = '';
20225         
20226         while (i < 12) {
20227             var month = {
20228                 tag: 'span',
20229                 cls: 'month',
20230                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20231             };
20232             
20233             months.createChild(month);
20234         }
20235         
20236     },
20237     
20238     update: function()
20239     {
20240         var _this = this;
20241         
20242         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20243             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20244         }
20245         
20246         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20247             e.removeClass('active');
20248             
20249             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20250                 e.addClass('active');
20251             }
20252         })
20253     },
20254     
20255     place: function()
20256     {
20257         if(this.isInline) {
20258             return;
20259         }
20260         
20261         this.picker().removeClass(['bottom', 'top']);
20262         
20263         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20264             /*
20265              * place to the top of element!
20266              *
20267              */
20268             
20269             this.picker().addClass('top');
20270             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20271             
20272             return;
20273         }
20274         
20275         this.picker().addClass('bottom');
20276         
20277         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20278     },
20279     
20280     onFocus : function()
20281     {
20282         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20283         this.show();
20284     },
20285     
20286     onBlur : function()
20287     {
20288         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20289         
20290         var d = this.inputEl().getValue();
20291         
20292         this.setValue(d);
20293                 
20294         this.hide();
20295     },
20296     
20297     show : function()
20298     {
20299         this.picker().show();
20300         this.picker().select('>.datepicker-months', true).first().show();
20301         this.update();
20302         this.place();
20303         
20304         this.fireEvent('show', this, this.date);
20305     },
20306     
20307     hide : function()
20308     {
20309         if(this.isInline) {
20310             return;
20311         }
20312         this.picker().hide();
20313         this.fireEvent('hide', this, this.date);
20314         
20315     },
20316     
20317     onMousedown: function(e)
20318     {
20319         e.stopPropagation();
20320         e.preventDefault();
20321     },
20322     
20323     keyup: function(e)
20324     {
20325         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20326         this.update();
20327     },
20328
20329     fireKey: function(e)
20330     {
20331         if (!this.picker().isVisible()){
20332             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20333                 this.show();
20334             }
20335             return;
20336         }
20337         
20338         var dir;
20339         
20340         switch(e.keyCode){
20341             case 27: // escape
20342                 this.hide();
20343                 e.preventDefault();
20344                 break;
20345             case 37: // left
20346             case 39: // right
20347                 dir = e.keyCode == 37 ? -1 : 1;
20348                 
20349                 this.vIndex = this.vIndex + dir;
20350                 
20351                 if(this.vIndex < 0){
20352                     this.vIndex = 0;
20353                 }
20354                 
20355                 if(this.vIndex > 11){
20356                     this.vIndex = 11;
20357                 }
20358                 
20359                 if(isNaN(this.vIndex)){
20360                     this.vIndex = 0;
20361                 }
20362                 
20363                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20364                 
20365                 break;
20366             case 38: // up
20367             case 40: // down
20368                 
20369                 dir = e.keyCode == 38 ? -1 : 1;
20370                 
20371                 this.vIndex = this.vIndex + dir * 4;
20372                 
20373                 if(this.vIndex < 0){
20374                     this.vIndex = 0;
20375                 }
20376                 
20377                 if(this.vIndex > 11){
20378                     this.vIndex = 11;
20379                 }
20380                 
20381                 if(isNaN(this.vIndex)){
20382                     this.vIndex = 0;
20383                 }
20384                 
20385                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20386                 break;
20387                 
20388             case 13: // enter
20389                 
20390                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20391                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20392                 }
20393                 
20394                 this.hide();
20395                 e.preventDefault();
20396                 break;
20397             case 9: // tab
20398                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20399                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20400                 }
20401                 this.hide();
20402                 break;
20403             case 16: // shift
20404             case 17: // ctrl
20405             case 18: // alt
20406                 break;
20407             default :
20408                 this.hide();
20409                 
20410         }
20411     },
20412     
20413     remove: function() 
20414     {
20415         this.picker().remove();
20416     }
20417    
20418 });
20419
20420 Roo.apply(Roo.bootstrap.MonthField,  {
20421     
20422     content : {
20423         tag: 'tbody',
20424         cn: [
20425         {
20426             tag: 'tr',
20427             cn: [
20428             {
20429                 tag: 'td',
20430                 colspan: '7'
20431             }
20432             ]
20433         }
20434         ]
20435     },
20436     
20437     dates:{
20438         en: {
20439             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20440             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20441         }
20442     }
20443 });
20444
20445 Roo.apply(Roo.bootstrap.MonthField,  {
20446   
20447     template : {
20448         tag: 'div',
20449         cls: 'datepicker dropdown-menu roo-dynamic',
20450         cn: [
20451             {
20452                 tag: 'div',
20453                 cls: 'datepicker-months',
20454                 cn: [
20455                 {
20456                     tag: 'table',
20457                     cls: 'table-condensed',
20458                     cn:[
20459                         Roo.bootstrap.DateField.content
20460                     ]
20461                 }
20462                 ]
20463             }
20464         ]
20465     }
20466 });
20467
20468  
20469
20470  
20471  /*
20472  * - LGPL
20473  *
20474  * CheckBox
20475  * 
20476  */
20477
20478 /**
20479  * @class Roo.bootstrap.CheckBox
20480  * @extends Roo.bootstrap.Input
20481  * Bootstrap CheckBox class
20482  * 
20483  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20484  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20485  * @cfg {String} boxLabel The text that appears beside the checkbox
20486  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20487  * @cfg {Boolean} checked initnal the element
20488  * @cfg {Boolean} inline inline the element (default false)
20489  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20490  * @cfg {String} tooltip label tooltip
20491  * 
20492  * @constructor
20493  * Create a new CheckBox
20494  * @param {Object} config The config object
20495  */
20496
20497 Roo.bootstrap.CheckBox = function(config){
20498     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20499    
20500     this.addEvents({
20501         /**
20502         * @event check
20503         * Fires when the element is checked or unchecked.
20504         * @param {Roo.bootstrap.CheckBox} this This input
20505         * @param {Boolean} checked The new checked value
20506         */
20507        check : true,
20508        /**
20509         * @event click
20510         * Fires when the element is click.
20511         * @param {Roo.bootstrap.CheckBox} this This input
20512         */
20513        click : true
20514     });
20515     
20516 };
20517
20518 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20519   
20520     inputType: 'checkbox',
20521     inputValue: 1,
20522     valueOff: 0,
20523     boxLabel: false,
20524     checked: false,
20525     weight : false,
20526     inline: false,
20527     tooltip : '',
20528     
20529     getAutoCreate : function()
20530     {
20531         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20532         
20533         var id = Roo.id();
20534         
20535         var cfg = {};
20536         
20537         cfg.cls = 'form-group ' + this.inputType; //input-group
20538         
20539         if(this.inline){
20540             cfg.cls += ' ' + this.inputType + '-inline';
20541         }
20542         
20543         var input =  {
20544             tag: 'input',
20545             id : id,
20546             type : this.inputType,
20547             value : this.inputValue,
20548             cls : 'roo-' + this.inputType, //'form-box',
20549             placeholder : this.placeholder || ''
20550             
20551         };
20552         
20553         if(this.inputType != 'radio'){
20554             var hidden =  {
20555                 tag: 'input',
20556                 type : 'hidden',
20557                 cls : 'roo-hidden-value',
20558                 value : this.checked ? this.inputValue : this.valueOff
20559             };
20560         }
20561         
20562             
20563         if (this.weight) { // Validity check?
20564             cfg.cls += " " + this.inputType + "-" + this.weight;
20565         }
20566         
20567         if (this.disabled) {
20568             input.disabled=true;
20569         }
20570         
20571         if(this.checked){
20572             input.checked = this.checked;
20573         }
20574         
20575         if (this.name) {
20576             
20577             input.name = this.name;
20578             
20579             if(this.inputType != 'radio'){
20580                 hidden.name = this.name;
20581                 input.name = '_hidden_' + this.name;
20582             }
20583         }
20584         
20585         if (this.size) {
20586             input.cls += ' input-' + this.size;
20587         }
20588         
20589         var settings=this;
20590         
20591         ['xs','sm','md','lg'].map(function(size){
20592             if (settings[size]) {
20593                 cfg.cls += ' col-' + size + '-' + settings[size];
20594             }
20595         });
20596         
20597         var inputblock = input;
20598          
20599         if (this.before || this.after) {
20600             
20601             inputblock = {
20602                 cls : 'input-group',
20603                 cn :  [] 
20604             };
20605             
20606             if (this.before) {
20607                 inputblock.cn.push({
20608                     tag :'span',
20609                     cls : 'input-group-addon',
20610                     html : this.before
20611                 });
20612             }
20613             
20614             inputblock.cn.push(input);
20615             
20616             if(this.inputType != 'radio'){
20617                 inputblock.cn.push(hidden);
20618             }
20619             
20620             if (this.after) {
20621                 inputblock.cn.push({
20622                     tag :'span',
20623                     cls : 'input-group-addon',
20624                     html : this.after
20625                 });
20626             }
20627             
20628         }
20629         
20630         if (align ==='left' && this.fieldLabel.length) {
20631 //                Roo.log("left and has label");
20632             cfg.cn = [
20633                 {
20634                     tag: 'label',
20635                     'for' :  id,
20636                     cls : 'control-label',
20637                     html : this.fieldLabel
20638                 },
20639                 {
20640                     cls : "", 
20641                     cn: [
20642                         inputblock
20643                     ]
20644                 }
20645             ];
20646             
20647             if(this.labelWidth > 12){
20648                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20649             }
20650             
20651             if(this.labelWidth < 13 && this.labelmd == 0){
20652                 this.labelmd = this.labelWidth;
20653             }
20654             
20655             if(this.labellg > 0){
20656                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20657                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20658             }
20659             
20660             if(this.labelmd > 0){
20661                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20662                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20663             }
20664             
20665             if(this.labelsm > 0){
20666                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20667                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20668             }
20669             
20670             if(this.labelxs > 0){
20671                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20672                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20673             }
20674             
20675         } else if ( this.fieldLabel.length) {
20676 //                Roo.log(" label");
20677                 cfg.cn = [
20678                    
20679                     {
20680                         tag: this.boxLabel ? 'span' : 'label',
20681                         'for': id,
20682                         cls: 'control-label box-input-label',
20683                         //cls : 'input-group-addon',
20684                         html : this.fieldLabel
20685                     },
20686                     
20687                     inputblock
20688                     
20689                 ];
20690
20691         } else {
20692             
20693 //                Roo.log(" no label && no align");
20694                 cfg.cn = [  inputblock ] ;
20695                 
20696                 
20697         }
20698         
20699         if(this.boxLabel){
20700              var boxLabelCfg = {
20701                 tag: 'label',
20702                 //'for': id, // box label is handled by onclick - so no for...
20703                 cls: 'box-label',
20704                 html: this.boxLabel
20705             };
20706             
20707             if(this.tooltip){
20708                 boxLabelCfg.tooltip = this.tooltip;
20709             }
20710              
20711             cfg.cn.push(boxLabelCfg);
20712         }
20713         
20714         if(this.inputType != 'radio'){
20715             cfg.cn.push(hidden);
20716         }
20717         
20718         return cfg;
20719         
20720     },
20721     
20722     /**
20723      * return the real input element.
20724      */
20725     inputEl: function ()
20726     {
20727         return this.el.select('input.roo-' + this.inputType,true).first();
20728     },
20729     hiddenEl: function ()
20730     {
20731         return this.el.select('input.roo-hidden-value',true).first();
20732     },
20733     
20734     labelEl: function()
20735     {
20736         return this.el.select('label.control-label',true).first();
20737     },
20738     /* depricated... */
20739     
20740     label: function()
20741     {
20742         return this.labelEl();
20743     },
20744     
20745     boxLabelEl: function()
20746     {
20747         return this.el.select('label.box-label',true).first();
20748     },
20749     
20750     initEvents : function()
20751     {
20752 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20753         
20754         this.inputEl().on('click', this.onClick,  this);
20755         
20756         if (this.boxLabel) { 
20757             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20758         }
20759         
20760         this.startValue = this.getValue();
20761         
20762         if(this.groupId){
20763             Roo.bootstrap.CheckBox.register(this);
20764         }
20765     },
20766     
20767     onClick : function(e)
20768     {   
20769         if(this.fireEvent('click', this, e) !== false){
20770             this.setChecked(!this.checked);
20771         }
20772         
20773     },
20774     
20775     setChecked : function(state,suppressEvent)
20776     {
20777         this.startValue = this.getValue();
20778
20779         if(this.inputType == 'radio'){
20780             
20781             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20782                 e.dom.checked = false;
20783             });
20784             
20785             this.inputEl().dom.checked = true;
20786             
20787             this.inputEl().dom.value = this.inputValue;
20788             
20789             if(suppressEvent !== true){
20790                 this.fireEvent('check', this, true);
20791             }
20792             
20793             this.validate();
20794             
20795             return;
20796         }
20797         
20798         this.checked = state;
20799         
20800         this.inputEl().dom.checked = state;
20801         
20802         
20803         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20804         
20805         if(suppressEvent !== true){
20806             this.fireEvent('check', this, state);
20807         }
20808         
20809         this.validate();
20810     },
20811     
20812     getValue : function()
20813     {
20814         if(this.inputType == 'radio'){
20815             return this.getGroupValue();
20816         }
20817         
20818         return this.hiddenEl().dom.value;
20819         
20820     },
20821     
20822     getGroupValue : function()
20823     {
20824         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20825             return '';
20826         }
20827         
20828         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20829     },
20830     
20831     setValue : function(v,suppressEvent)
20832     {
20833         if(this.inputType == 'radio'){
20834             this.setGroupValue(v, suppressEvent);
20835             return;
20836         }
20837         
20838         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20839         
20840         this.validate();
20841     },
20842     
20843     setGroupValue : function(v, suppressEvent)
20844     {
20845         this.startValue = this.getValue();
20846         
20847         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20848             e.dom.checked = false;
20849             
20850             if(e.dom.value == v){
20851                 e.dom.checked = true;
20852             }
20853         });
20854         
20855         if(suppressEvent !== true){
20856             this.fireEvent('check', this, true);
20857         }
20858
20859         this.validate();
20860         
20861         return;
20862     },
20863     
20864     validate : function()
20865     {
20866         if(this.getVisibilityEl().hasClass('hidden')){
20867             return true;
20868         }
20869         
20870         if(
20871                 this.disabled || 
20872                 (this.inputType == 'radio' && this.validateRadio()) ||
20873                 (this.inputType == 'checkbox' && this.validateCheckbox())
20874         ){
20875             this.markValid();
20876             return true;
20877         }
20878         
20879         this.markInvalid();
20880         return false;
20881     },
20882     
20883     validateRadio : function()
20884     {
20885         if(this.getVisibilityEl().hasClass('hidden')){
20886             return true;
20887         }
20888         
20889         if(this.allowBlank){
20890             return true;
20891         }
20892         
20893         var valid = false;
20894         
20895         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20896             if(!e.dom.checked){
20897                 return;
20898             }
20899             
20900             valid = true;
20901             
20902             return false;
20903         });
20904         
20905         return valid;
20906     },
20907     
20908     validateCheckbox : function()
20909     {
20910         if(!this.groupId){
20911             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20912             //return (this.getValue() == this.inputValue) ? true : false;
20913         }
20914         
20915         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20916         
20917         if(!group){
20918             return false;
20919         }
20920         
20921         var r = false;
20922         
20923         for(var i in group){
20924             if(group[i].el.isVisible(true)){
20925                 r = false;
20926                 break;
20927             }
20928             
20929             r = true;
20930         }
20931         
20932         for(var i in group){
20933             if(r){
20934                 break;
20935             }
20936             
20937             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20938         }
20939         
20940         return r;
20941     },
20942     
20943     /**
20944      * Mark this field as valid
20945      */
20946     markValid : function()
20947     {
20948         var _this = this;
20949         
20950         this.fireEvent('valid', this);
20951         
20952         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20953         
20954         if(this.groupId){
20955             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20956         }
20957         
20958         if(label){
20959             label.markValid();
20960         }
20961
20962         if(this.inputType == 'radio'){
20963             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20964                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20965                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20966             });
20967             
20968             return;
20969         }
20970
20971         if(!this.groupId){
20972             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20973             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20974             return;
20975         }
20976         
20977         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20978         
20979         if(!group){
20980             return;
20981         }
20982         
20983         for(var i in group){
20984             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20985             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20986         }
20987     },
20988     
20989      /**
20990      * Mark this field as invalid
20991      * @param {String} msg The validation message
20992      */
20993     markInvalid : function(msg)
20994     {
20995         if(this.allowBlank){
20996             return;
20997         }
20998         
20999         var _this = this;
21000         
21001         this.fireEvent('invalid', this, msg);
21002         
21003         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21004         
21005         if(this.groupId){
21006             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21007         }
21008         
21009         if(label){
21010             label.markInvalid();
21011         }
21012             
21013         if(this.inputType == 'radio'){
21014             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21015                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21016                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21017             });
21018             
21019             return;
21020         }
21021         
21022         if(!this.groupId){
21023             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21024             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21025             return;
21026         }
21027         
21028         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21029         
21030         if(!group){
21031             return;
21032         }
21033         
21034         for(var i in group){
21035             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21036             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21037         }
21038         
21039     },
21040     
21041     clearInvalid : function()
21042     {
21043         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21044         
21045         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21046         
21047         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21048         
21049         if (label && label.iconEl) {
21050             label.iconEl.removeClass(label.validClass);
21051             label.iconEl.removeClass(label.invalidClass);
21052         }
21053     },
21054     
21055     disable : function()
21056     {
21057         if(this.inputType != 'radio'){
21058             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21059             return;
21060         }
21061         
21062         var _this = this;
21063         
21064         if(this.rendered){
21065             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21066                 _this.getActionEl().addClass(this.disabledClass);
21067                 e.dom.disabled = true;
21068             });
21069         }
21070         
21071         this.disabled = true;
21072         this.fireEvent("disable", this);
21073         return this;
21074     },
21075
21076     enable : function()
21077     {
21078         if(this.inputType != 'radio'){
21079             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21080             return;
21081         }
21082         
21083         var _this = this;
21084         
21085         if(this.rendered){
21086             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21087                 _this.getActionEl().removeClass(this.disabledClass);
21088                 e.dom.disabled = false;
21089             });
21090         }
21091         
21092         this.disabled = false;
21093         this.fireEvent("enable", this);
21094         return this;
21095     },
21096     
21097     setBoxLabel : function(v)
21098     {
21099         this.boxLabel = v;
21100         
21101         if(this.rendered){
21102             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21103         }
21104     }
21105
21106 });
21107
21108 Roo.apply(Roo.bootstrap.CheckBox, {
21109     
21110     groups: {},
21111     
21112      /**
21113     * register a CheckBox Group
21114     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21115     */
21116     register : function(checkbox)
21117     {
21118         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21119             this.groups[checkbox.groupId] = {};
21120         }
21121         
21122         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21123             return;
21124         }
21125         
21126         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21127         
21128     },
21129     /**
21130     * fetch a CheckBox Group based on the group ID
21131     * @param {string} the group ID
21132     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21133     */
21134     get: function(groupId) {
21135         if (typeof(this.groups[groupId]) == 'undefined') {
21136             return false;
21137         }
21138         
21139         return this.groups[groupId] ;
21140     }
21141     
21142     
21143 });
21144 /*
21145  * - LGPL
21146  *
21147  * RadioItem
21148  * 
21149  */
21150
21151 /**
21152  * @class Roo.bootstrap.Radio
21153  * @extends Roo.bootstrap.Component
21154  * Bootstrap Radio class
21155  * @cfg {String} boxLabel - the label associated
21156  * @cfg {String} value - the value of radio
21157  * 
21158  * @constructor
21159  * Create a new Radio
21160  * @param {Object} config The config object
21161  */
21162 Roo.bootstrap.Radio = function(config){
21163     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21164     
21165 };
21166
21167 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21168     
21169     boxLabel : '',
21170     
21171     value : '',
21172     
21173     getAutoCreate : function()
21174     {
21175         var cfg = {
21176             tag : 'div',
21177             cls : 'form-group radio',
21178             cn : [
21179                 {
21180                     tag : 'label',
21181                     cls : 'box-label',
21182                     html : this.boxLabel
21183                 }
21184             ]
21185         };
21186         
21187         return cfg;
21188     },
21189     
21190     initEvents : function() 
21191     {
21192         this.parent().register(this);
21193         
21194         this.el.on('click', this.onClick, this);
21195         
21196     },
21197     
21198     onClick : function(e)
21199     {
21200         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21201             this.setChecked(true);
21202         }
21203     },
21204     
21205     setChecked : function(state, suppressEvent)
21206     {
21207         this.parent().setValue(this.value, suppressEvent);
21208         
21209     },
21210     
21211     setBoxLabel : function(v)
21212     {
21213         this.boxLabel = v;
21214         
21215         if(this.rendered){
21216             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21217         }
21218     }
21219     
21220 });
21221  
21222
21223  /*
21224  * - LGPL
21225  *
21226  * Input
21227  * 
21228  */
21229
21230 /**
21231  * @class Roo.bootstrap.SecurePass
21232  * @extends Roo.bootstrap.Input
21233  * Bootstrap SecurePass class
21234  *
21235  * 
21236  * @constructor
21237  * Create a new SecurePass
21238  * @param {Object} config The config object
21239  */
21240  
21241 Roo.bootstrap.SecurePass = function (config) {
21242     // these go here, so the translation tool can replace them..
21243     this.errors = {
21244         PwdEmpty: "Please type a password, and then retype it to confirm.",
21245         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21246         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21247         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21248         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21249         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21250         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21251         TooWeak: "Your password is Too Weak."
21252     },
21253     this.meterLabel = "Password strength:";
21254     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21255     this.meterClass = [
21256         "roo-password-meter-tooweak", 
21257         "roo-password-meter-weak", 
21258         "roo-password-meter-medium", 
21259         "roo-password-meter-strong", 
21260         "roo-password-meter-grey"
21261     ];
21262     
21263     this.errors = {};
21264     
21265     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21266 }
21267
21268 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21269     /**
21270      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21271      * {
21272      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21273      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21274      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21275      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21276      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21277      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21278      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21279      * })
21280      */
21281     // private
21282     
21283     meterWidth: 300,
21284     errorMsg :'',    
21285     errors: false,
21286     imageRoot: '/',
21287     /**
21288      * @cfg {String/Object} Label for the strength meter (defaults to
21289      * 'Password strength:')
21290      */
21291     // private
21292     meterLabel: '',
21293     /**
21294      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21295      * ['Weak', 'Medium', 'Strong'])
21296      */
21297     // private    
21298     pwdStrengths: false,    
21299     // private
21300     strength: 0,
21301     // private
21302     _lastPwd: null,
21303     // private
21304     kCapitalLetter: 0,
21305     kSmallLetter: 1,
21306     kDigit: 2,
21307     kPunctuation: 3,
21308     
21309     insecure: false,
21310     // private
21311     initEvents: function ()
21312     {
21313         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21314
21315         if (this.el.is('input[type=password]') && Roo.isSafari) {
21316             this.el.on('keydown', this.SafariOnKeyDown, this);
21317         }
21318
21319         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21320     },
21321     // private
21322     onRender: function (ct, position)
21323     {
21324         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21325         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21326         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21327
21328         this.trigger.createChild({
21329                    cn: [
21330                     {
21331                     //id: 'PwdMeter',
21332                     tag: 'div',
21333                     cls: 'roo-password-meter-grey col-xs-12',
21334                     style: {
21335                         //width: 0,
21336                         //width: this.meterWidth + 'px'                                                
21337                         }
21338                     },
21339                     {                            
21340                          cls: 'roo-password-meter-text'                          
21341                     }
21342                 ]            
21343         });
21344
21345          
21346         if (this.hideTrigger) {
21347             this.trigger.setDisplayed(false);
21348         }
21349         this.setSize(this.width || '', this.height || '');
21350     },
21351     // private
21352     onDestroy: function ()
21353     {
21354         if (this.trigger) {
21355             this.trigger.removeAllListeners();
21356             this.trigger.remove();
21357         }
21358         if (this.wrap) {
21359             this.wrap.remove();
21360         }
21361         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21362     },
21363     // private
21364     checkStrength: function ()
21365     {
21366         var pwd = this.inputEl().getValue();
21367         if (pwd == this._lastPwd) {
21368             return;
21369         }
21370
21371         var strength;
21372         if (this.ClientSideStrongPassword(pwd)) {
21373             strength = 3;
21374         } else if (this.ClientSideMediumPassword(pwd)) {
21375             strength = 2;
21376         } else if (this.ClientSideWeakPassword(pwd)) {
21377             strength = 1;
21378         } else {
21379             strength = 0;
21380         }
21381         
21382         Roo.log('strength1: ' + strength);
21383         
21384         //var pm = this.trigger.child('div/div/div').dom;
21385         var pm = this.trigger.child('div/div');
21386         pm.removeClass(this.meterClass);
21387         pm.addClass(this.meterClass[strength]);
21388                 
21389         
21390         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21391                 
21392         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21393         
21394         this._lastPwd = pwd;
21395     },
21396     reset: function ()
21397     {
21398         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21399         
21400         this._lastPwd = '';
21401         
21402         var pm = this.trigger.child('div/div');
21403         pm.removeClass(this.meterClass);
21404         pm.addClass('roo-password-meter-grey');        
21405         
21406         
21407         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21408         
21409         pt.innerHTML = '';
21410         this.inputEl().dom.type='password';
21411     },
21412     // private
21413     validateValue: function (value)
21414     {
21415         
21416         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21417             return false;
21418         }
21419         if (value.length == 0) {
21420             if (this.allowBlank) {
21421                 this.clearInvalid();
21422                 return true;
21423             }
21424
21425             this.markInvalid(this.errors.PwdEmpty);
21426             this.errorMsg = this.errors.PwdEmpty;
21427             return false;
21428         }
21429         
21430         if(this.insecure){
21431             return true;
21432         }
21433         
21434         if ('[\x21-\x7e]*'.match(value)) {
21435             this.markInvalid(this.errors.PwdBadChar);
21436             this.errorMsg = this.errors.PwdBadChar;
21437             return false;
21438         }
21439         if (value.length < 6) {
21440             this.markInvalid(this.errors.PwdShort);
21441             this.errorMsg = this.errors.PwdShort;
21442             return false;
21443         }
21444         if (value.length > 16) {
21445             this.markInvalid(this.errors.PwdLong);
21446             this.errorMsg = this.errors.PwdLong;
21447             return false;
21448         }
21449         var strength;
21450         if (this.ClientSideStrongPassword(value)) {
21451             strength = 3;
21452         } else if (this.ClientSideMediumPassword(value)) {
21453             strength = 2;
21454         } else if (this.ClientSideWeakPassword(value)) {
21455             strength = 1;
21456         } else {
21457             strength = 0;
21458         }
21459
21460         
21461         if (strength < 2) {
21462             //this.markInvalid(this.errors.TooWeak);
21463             this.errorMsg = this.errors.TooWeak;
21464             //return false;
21465         }
21466         
21467         
21468         console.log('strength2: ' + strength);
21469         
21470         //var pm = this.trigger.child('div/div/div').dom;
21471         
21472         var pm = this.trigger.child('div/div');
21473         pm.removeClass(this.meterClass);
21474         pm.addClass(this.meterClass[strength]);
21475                 
21476         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21477                 
21478         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21479         
21480         this.errorMsg = ''; 
21481         return true;
21482     },
21483     // private
21484     CharacterSetChecks: function (type)
21485     {
21486         this.type = type;
21487         this.fResult = false;
21488     },
21489     // private
21490     isctype: function (character, type)
21491     {
21492         switch (type) {  
21493             case this.kCapitalLetter:
21494                 if (character >= 'A' && character <= 'Z') {
21495                     return true;
21496                 }
21497                 break;
21498             
21499             case this.kSmallLetter:
21500                 if (character >= 'a' && character <= 'z') {
21501                     return true;
21502                 }
21503                 break;
21504             
21505             case this.kDigit:
21506                 if (character >= '0' && character <= '9') {
21507                     return true;
21508                 }
21509                 break;
21510             
21511             case this.kPunctuation:
21512                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21513                     return true;
21514                 }
21515                 break;
21516             
21517             default:
21518                 return false;
21519         }
21520
21521     },
21522     // private
21523     IsLongEnough: function (pwd, size)
21524     {
21525         return !(pwd == null || isNaN(size) || pwd.length < size);
21526     },
21527     // private
21528     SpansEnoughCharacterSets: function (word, nb)
21529     {
21530         if (!this.IsLongEnough(word, nb))
21531         {
21532             return false;
21533         }
21534
21535         var characterSetChecks = new Array(
21536             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21537             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21538         );
21539         
21540         for (var index = 0; index < word.length; ++index) {
21541             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21542                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21543                     characterSetChecks[nCharSet].fResult = true;
21544                     break;
21545                 }
21546             }
21547         }
21548
21549         var nCharSets = 0;
21550         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21551             if (characterSetChecks[nCharSet].fResult) {
21552                 ++nCharSets;
21553             }
21554         }
21555
21556         if (nCharSets < nb) {
21557             return false;
21558         }
21559         return true;
21560     },
21561     // private
21562     ClientSideStrongPassword: function (pwd)
21563     {
21564         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21565     },
21566     // private
21567     ClientSideMediumPassword: function (pwd)
21568     {
21569         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21570     },
21571     // private
21572     ClientSideWeakPassword: function (pwd)
21573     {
21574         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21575     }
21576           
21577 })//<script type="text/javascript">
21578
21579 /*
21580  * Based  Ext JS Library 1.1.1
21581  * Copyright(c) 2006-2007, Ext JS, LLC.
21582  * LGPL
21583  *
21584  */
21585  
21586 /**
21587  * @class Roo.HtmlEditorCore
21588  * @extends Roo.Component
21589  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21590  *
21591  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21592  */
21593
21594 Roo.HtmlEditorCore = function(config){
21595     
21596     
21597     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21598     
21599     
21600     this.addEvents({
21601         /**
21602          * @event initialize
21603          * Fires when the editor is fully initialized (including the iframe)
21604          * @param {Roo.HtmlEditorCore} this
21605          */
21606         initialize: true,
21607         /**
21608          * @event activate
21609          * Fires when the editor is first receives the focus. Any insertion must wait
21610          * until after this event.
21611          * @param {Roo.HtmlEditorCore} this
21612          */
21613         activate: true,
21614          /**
21615          * @event beforesync
21616          * Fires before the textarea is updated with content from the editor iframe. Return false
21617          * to cancel the sync.
21618          * @param {Roo.HtmlEditorCore} this
21619          * @param {String} html
21620          */
21621         beforesync: true,
21622          /**
21623          * @event beforepush
21624          * Fires before the iframe editor is updated with content from the textarea. Return false
21625          * to cancel the push.
21626          * @param {Roo.HtmlEditorCore} this
21627          * @param {String} html
21628          */
21629         beforepush: true,
21630          /**
21631          * @event sync
21632          * Fires when the textarea is updated with content from the editor iframe.
21633          * @param {Roo.HtmlEditorCore} this
21634          * @param {String} html
21635          */
21636         sync: true,
21637          /**
21638          * @event push
21639          * Fires when the iframe editor is updated with content from the textarea.
21640          * @param {Roo.HtmlEditorCore} this
21641          * @param {String} html
21642          */
21643         push: true,
21644         
21645         /**
21646          * @event editorevent
21647          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21648          * @param {Roo.HtmlEditorCore} this
21649          */
21650         editorevent: true
21651         
21652     });
21653     
21654     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21655     
21656     // defaults : white / black...
21657     this.applyBlacklists();
21658     
21659     
21660     
21661 };
21662
21663
21664 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21665
21666
21667      /**
21668      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21669      */
21670     
21671     owner : false,
21672     
21673      /**
21674      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21675      *                        Roo.resizable.
21676      */
21677     resizable : false,
21678      /**
21679      * @cfg {Number} height (in pixels)
21680      */   
21681     height: 300,
21682    /**
21683      * @cfg {Number} width (in pixels)
21684      */   
21685     width: 500,
21686     
21687     /**
21688      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21689      * 
21690      */
21691     stylesheets: false,
21692     
21693     // id of frame..
21694     frameId: false,
21695     
21696     // private properties
21697     validationEvent : false,
21698     deferHeight: true,
21699     initialized : false,
21700     activated : false,
21701     sourceEditMode : false,
21702     onFocus : Roo.emptyFn,
21703     iframePad:3,
21704     hideMode:'offsets',
21705     
21706     clearUp: true,
21707     
21708     // blacklist + whitelisted elements..
21709     black: false,
21710     white: false,
21711      
21712     bodyCls : '',
21713
21714     /**
21715      * Protected method that will not generally be called directly. It
21716      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21717      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21718      */
21719     getDocMarkup : function(){
21720         // body styles..
21721         var st = '';
21722         
21723         // inherit styels from page...?? 
21724         if (this.stylesheets === false) {
21725             
21726             Roo.get(document.head).select('style').each(function(node) {
21727                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21728             });
21729             
21730             Roo.get(document.head).select('link').each(function(node) { 
21731                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21732             });
21733             
21734         } else if (!this.stylesheets.length) {
21735                 // simple..
21736                 st = '<style type="text/css">' +
21737                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21738                    '</style>';
21739         } else { 
21740             st = '<style type="text/css">' +
21741                     this.stylesheets +
21742                 '</style>';
21743         }
21744         
21745         st +=  '<style type="text/css">' +
21746             'IMG { cursor: pointer } ' +
21747         '</style>';
21748
21749         var cls = 'roo-htmleditor-body';
21750         
21751         if(this.bodyCls.length){
21752             cls += ' ' + this.bodyCls;
21753         }
21754         
21755         return '<html><head>' + st  +
21756             //<style type="text/css">' +
21757             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21758             //'</style>' +
21759             ' </head><body class="' +  cls + '"></body></html>';
21760     },
21761
21762     // private
21763     onRender : function(ct, position)
21764     {
21765         var _t = this;
21766         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21767         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21768         
21769         
21770         this.el.dom.style.border = '0 none';
21771         this.el.dom.setAttribute('tabIndex', -1);
21772         this.el.addClass('x-hidden hide');
21773         
21774         
21775         
21776         if(Roo.isIE){ // fix IE 1px bogus margin
21777             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21778         }
21779        
21780         
21781         this.frameId = Roo.id();
21782         
21783          
21784         
21785         var iframe = this.owner.wrap.createChild({
21786             tag: 'iframe',
21787             cls: 'form-control', // bootstrap..
21788             id: this.frameId,
21789             name: this.frameId,
21790             frameBorder : 'no',
21791             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21792         }, this.el
21793         );
21794         
21795         
21796         this.iframe = iframe.dom;
21797
21798          this.assignDocWin();
21799         
21800         this.doc.designMode = 'on';
21801        
21802         this.doc.open();
21803         this.doc.write(this.getDocMarkup());
21804         this.doc.close();
21805
21806         
21807         var task = { // must defer to wait for browser to be ready
21808             run : function(){
21809                 //console.log("run task?" + this.doc.readyState);
21810                 this.assignDocWin();
21811                 if(this.doc.body || this.doc.readyState == 'complete'){
21812                     try {
21813                         this.doc.designMode="on";
21814                     } catch (e) {
21815                         return;
21816                     }
21817                     Roo.TaskMgr.stop(task);
21818                     this.initEditor.defer(10, this);
21819                 }
21820             },
21821             interval : 10,
21822             duration: 10000,
21823             scope: this
21824         };
21825         Roo.TaskMgr.start(task);
21826
21827     },
21828
21829     // private
21830     onResize : function(w, h)
21831     {
21832          Roo.log('resize: ' +w + ',' + h );
21833         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21834         if(!this.iframe){
21835             return;
21836         }
21837         if(typeof w == 'number'){
21838             
21839             this.iframe.style.width = w + 'px';
21840         }
21841         if(typeof h == 'number'){
21842             
21843             this.iframe.style.height = h + 'px';
21844             if(this.doc){
21845                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21846             }
21847         }
21848         
21849     },
21850
21851     /**
21852      * Toggles the editor between standard and source edit mode.
21853      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21854      */
21855     toggleSourceEdit : function(sourceEditMode){
21856         
21857         this.sourceEditMode = sourceEditMode === true;
21858         
21859         if(this.sourceEditMode){
21860  
21861             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21862             
21863         }else{
21864             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21865             //this.iframe.className = '';
21866             this.deferFocus();
21867         }
21868         //this.setSize(this.owner.wrap.getSize());
21869         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21870     },
21871
21872     
21873   
21874
21875     /**
21876      * Protected method that will not generally be called directly. If you need/want
21877      * custom HTML cleanup, this is the method you should override.
21878      * @param {String} html The HTML to be cleaned
21879      * return {String} The cleaned HTML
21880      */
21881     cleanHtml : function(html){
21882         html = String(html);
21883         if(html.length > 5){
21884             if(Roo.isSafari){ // strip safari nonsense
21885                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21886             }
21887         }
21888         if(html == '&nbsp;'){
21889             html = '';
21890         }
21891         return html;
21892     },
21893
21894     /**
21895      * HTML Editor -> Textarea
21896      * Protected method that will not generally be called directly. Syncs the contents
21897      * of the editor iframe with the textarea.
21898      */
21899     syncValue : function(){
21900         if(this.initialized){
21901             var bd = (this.doc.body || this.doc.documentElement);
21902             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21903             var html = bd.innerHTML;
21904             if(Roo.isSafari){
21905                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21906                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21907                 if(m && m[1]){
21908                     html = '<div style="'+m[0]+'">' + html + '</div>';
21909                 }
21910             }
21911             html = this.cleanHtml(html);
21912             // fix up the special chars.. normaly like back quotes in word...
21913             // however we do not want to do this with chinese..
21914             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21915                 var cc = b.charCodeAt();
21916                 if (
21917                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21918                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21919                     (cc >= 0xf900 && cc < 0xfb00 )
21920                 ) {
21921                         return b;
21922                 }
21923                 return "&#"+cc+";" 
21924             });
21925             if(this.owner.fireEvent('beforesync', this, html) !== false){
21926                 this.el.dom.value = html;
21927                 this.owner.fireEvent('sync', this, html);
21928             }
21929         }
21930     },
21931
21932     /**
21933      * Protected method that will not generally be called directly. Pushes the value of the textarea
21934      * into the iframe editor.
21935      */
21936     pushValue : function(){
21937         if(this.initialized){
21938             var v = this.el.dom.value.trim();
21939             
21940 //            if(v.length < 1){
21941 //                v = '&#160;';
21942 //            }
21943             
21944             if(this.owner.fireEvent('beforepush', this, v) !== false){
21945                 var d = (this.doc.body || this.doc.documentElement);
21946                 d.innerHTML = v;
21947                 this.cleanUpPaste();
21948                 this.el.dom.value = d.innerHTML;
21949                 this.owner.fireEvent('push', this, v);
21950             }
21951         }
21952     },
21953
21954     // private
21955     deferFocus : function(){
21956         this.focus.defer(10, this);
21957     },
21958
21959     // doc'ed in Field
21960     focus : function(){
21961         if(this.win && !this.sourceEditMode){
21962             this.win.focus();
21963         }else{
21964             this.el.focus();
21965         }
21966     },
21967     
21968     assignDocWin: function()
21969     {
21970         var iframe = this.iframe;
21971         
21972          if(Roo.isIE){
21973             this.doc = iframe.contentWindow.document;
21974             this.win = iframe.contentWindow;
21975         } else {
21976 //            if (!Roo.get(this.frameId)) {
21977 //                return;
21978 //            }
21979 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21980 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21981             
21982             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21983                 return;
21984             }
21985             
21986             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21987             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21988         }
21989     },
21990     
21991     // private
21992     initEditor : function(){
21993         //console.log("INIT EDITOR");
21994         this.assignDocWin();
21995         
21996         
21997         
21998         this.doc.designMode="on";
21999         this.doc.open();
22000         this.doc.write(this.getDocMarkup());
22001         this.doc.close();
22002         
22003         var dbody = (this.doc.body || this.doc.documentElement);
22004         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22005         // this copies styles from the containing element into thsi one..
22006         // not sure why we need all of this..
22007         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22008         
22009         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22010         //ss['background-attachment'] = 'fixed'; // w3c
22011         dbody.bgProperties = 'fixed'; // ie
22012         //Roo.DomHelper.applyStyles(dbody, ss);
22013         Roo.EventManager.on(this.doc, {
22014             //'mousedown': this.onEditorEvent,
22015             'mouseup': this.onEditorEvent,
22016             'dblclick': this.onEditorEvent,
22017             'click': this.onEditorEvent,
22018             'keyup': this.onEditorEvent,
22019             buffer:100,
22020             scope: this
22021         });
22022         if(Roo.isGecko){
22023             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22024         }
22025         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22026             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22027         }
22028         this.initialized = true;
22029
22030         this.owner.fireEvent('initialize', this);
22031         this.pushValue();
22032     },
22033
22034     // private
22035     onDestroy : function(){
22036         
22037         
22038         
22039         if(this.rendered){
22040             
22041             //for (var i =0; i < this.toolbars.length;i++) {
22042             //    // fixme - ask toolbars for heights?
22043             //    this.toolbars[i].onDestroy();
22044            // }
22045             
22046             //this.wrap.dom.innerHTML = '';
22047             //this.wrap.remove();
22048         }
22049     },
22050
22051     // private
22052     onFirstFocus : function(){
22053         
22054         this.assignDocWin();
22055         
22056         
22057         this.activated = true;
22058          
22059     
22060         if(Roo.isGecko){ // prevent silly gecko errors
22061             this.win.focus();
22062             var s = this.win.getSelection();
22063             if(!s.focusNode || s.focusNode.nodeType != 3){
22064                 var r = s.getRangeAt(0);
22065                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22066                 r.collapse(true);
22067                 this.deferFocus();
22068             }
22069             try{
22070                 this.execCmd('useCSS', true);
22071                 this.execCmd('styleWithCSS', false);
22072             }catch(e){}
22073         }
22074         this.owner.fireEvent('activate', this);
22075     },
22076
22077     // private
22078     adjustFont: function(btn){
22079         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22080         //if(Roo.isSafari){ // safari
22081         //    adjust *= 2;
22082        // }
22083         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22084         if(Roo.isSafari){ // safari
22085             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22086             v =  (v < 10) ? 10 : v;
22087             v =  (v > 48) ? 48 : v;
22088             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22089             
22090         }
22091         
22092         
22093         v = Math.max(1, v+adjust);
22094         
22095         this.execCmd('FontSize', v  );
22096     },
22097
22098     onEditorEvent : function(e)
22099     {
22100         this.owner.fireEvent('editorevent', this, e);
22101       //  this.updateToolbar();
22102         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22103     },
22104
22105     insertTag : function(tg)
22106     {
22107         // could be a bit smarter... -> wrap the current selected tRoo..
22108         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22109             
22110             range = this.createRange(this.getSelection());
22111             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22112             wrappingNode.appendChild(range.extractContents());
22113             range.insertNode(wrappingNode);
22114
22115             return;
22116             
22117             
22118             
22119         }
22120         this.execCmd("formatblock",   tg);
22121         
22122     },
22123     
22124     insertText : function(txt)
22125     {
22126         
22127         
22128         var range = this.createRange();
22129         range.deleteContents();
22130                //alert(Sender.getAttribute('label'));
22131                
22132         range.insertNode(this.doc.createTextNode(txt));
22133     } ,
22134     
22135      
22136
22137     /**
22138      * Executes a Midas editor command on the editor document and performs necessary focus and
22139      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22140      * @param {String} cmd The Midas command
22141      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22142      */
22143     relayCmd : function(cmd, value){
22144         this.win.focus();
22145         this.execCmd(cmd, value);
22146         this.owner.fireEvent('editorevent', this);
22147         //this.updateToolbar();
22148         this.owner.deferFocus();
22149     },
22150
22151     /**
22152      * Executes a Midas editor command directly on the editor document.
22153      * For visual commands, you should use {@link #relayCmd} instead.
22154      * <b>This should only be called after the editor is initialized.</b>
22155      * @param {String} cmd The Midas command
22156      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22157      */
22158     execCmd : function(cmd, value){
22159         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22160         this.syncValue();
22161     },
22162  
22163  
22164    
22165     /**
22166      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22167      * to insert tRoo.
22168      * @param {String} text | dom node.. 
22169      */
22170     insertAtCursor : function(text)
22171     {
22172         
22173         if(!this.activated){
22174             return;
22175         }
22176         /*
22177         if(Roo.isIE){
22178             this.win.focus();
22179             var r = this.doc.selection.createRange();
22180             if(r){
22181                 r.collapse(true);
22182                 r.pasteHTML(text);
22183                 this.syncValue();
22184                 this.deferFocus();
22185             
22186             }
22187             return;
22188         }
22189         */
22190         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22191             this.win.focus();
22192             
22193             
22194             // from jquery ui (MIT licenced)
22195             var range, node;
22196             var win = this.win;
22197             
22198             if (win.getSelection && win.getSelection().getRangeAt) {
22199                 range = win.getSelection().getRangeAt(0);
22200                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22201                 range.insertNode(node);
22202             } else if (win.document.selection && win.document.selection.createRange) {
22203                 // no firefox support
22204                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22205                 win.document.selection.createRange().pasteHTML(txt);
22206             } else {
22207                 // no firefox support
22208                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22209                 this.execCmd('InsertHTML', txt);
22210             } 
22211             
22212             this.syncValue();
22213             
22214             this.deferFocus();
22215         }
22216     },
22217  // private
22218     mozKeyPress : function(e){
22219         if(e.ctrlKey){
22220             var c = e.getCharCode(), cmd;
22221           
22222             if(c > 0){
22223                 c = String.fromCharCode(c).toLowerCase();
22224                 switch(c){
22225                     case 'b':
22226                         cmd = 'bold';
22227                         break;
22228                     case 'i':
22229                         cmd = 'italic';
22230                         break;
22231                     
22232                     case 'u':
22233                         cmd = 'underline';
22234                         break;
22235                     
22236                     case 'v':
22237                         this.cleanUpPaste.defer(100, this);
22238                         return;
22239                         
22240                 }
22241                 if(cmd){
22242                     this.win.focus();
22243                     this.execCmd(cmd);
22244                     this.deferFocus();
22245                     e.preventDefault();
22246                 }
22247                 
22248             }
22249         }
22250     },
22251
22252     // private
22253     fixKeys : function(){ // load time branching for fastest keydown performance
22254         if(Roo.isIE){
22255             return function(e){
22256                 var k = e.getKey(), r;
22257                 if(k == e.TAB){
22258                     e.stopEvent();
22259                     r = this.doc.selection.createRange();
22260                     if(r){
22261                         r.collapse(true);
22262                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22263                         this.deferFocus();
22264                     }
22265                     return;
22266                 }
22267                 
22268                 if(k == e.ENTER){
22269                     r = this.doc.selection.createRange();
22270                     if(r){
22271                         var target = r.parentElement();
22272                         if(!target || target.tagName.toLowerCase() != 'li'){
22273                             e.stopEvent();
22274                             r.pasteHTML('<br />');
22275                             r.collapse(false);
22276                             r.select();
22277                         }
22278                     }
22279                 }
22280                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22281                     this.cleanUpPaste.defer(100, this);
22282                     return;
22283                 }
22284                 
22285                 
22286             };
22287         }else if(Roo.isOpera){
22288             return function(e){
22289                 var k = e.getKey();
22290                 if(k == e.TAB){
22291                     e.stopEvent();
22292                     this.win.focus();
22293                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22294                     this.deferFocus();
22295                 }
22296                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22297                     this.cleanUpPaste.defer(100, this);
22298                     return;
22299                 }
22300                 
22301             };
22302         }else if(Roo.isSafari){
22303             return function(e){
22304                 var k = e.getKey();
22305                 
22306                 if(k == e.TAB){
22307                     e.stopEvent();
22308                     this.execCmd('InsertText','\t');
22309                     this.deferFocus();
22310                     return;
22311                 }
22312                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22313                     this.cleanUpPaste.defer(100, this);
22314                     return;
22315                 }
22316                 
22317              };
22318         }
22319     }(),
22320     
22321     getAllAncestors: function()
22322     {
22323         var p = this.getSelectedNode();
22324         var a = [];
22325         if (!p) {
22326             a.push(p); // push blank onto stack..
22327             p = this.getParentElement();
22328         }
22329         
22330         
22331         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22332             a.push(p);
22333             p = p.parentNode;
22334         }
22335         a.push(this.doc.body);
22336         return a;
22337     },
22338     lastSel : false,
22339     lastSelNode : false,
22340     
22341     
22342     getSelection : function() 
22343     {
22344         this.assignDocWin();
22345         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22346     },
22347     
22348     getSelectedNode: function() 
22349     {
22350         // this may only work on Gecko!!!
22351         
22352         // should we cache this!!!!
22353         
22354         
22355         
22356          
22357         var range = this.createRange(this.getSelection()).cloneRange();
22358         
22359         if (Roo.isIE) {
22360             var parent = range.parentElement();
22361             while (true) {
22362                 var testRange = range.duplicate();
22363                 testRange.moveToElementText(parent);
22364                 if (testRange.inRange(range)) {
22365                     break;
22366                 }
22367                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22368                     break;
22369                 }
22370                 parent = parent.parentElement;
22371             }
22372             return parent;
22373         }
22374         
22375         // is ancestor a text element.
22376         var ac =  range.commonAncestorContainer;
22377         if (ac.nodeType == 3) {
22378             ac = ac.parentNode;
22379         }
22380         
22381         var ar = ac.childNodes;
22382          
22383         var nodes = [];
22384         var other_nodes = [];
22385         var has_other_nodes = false;
22386         for (var i=0;i<ar.length;i++) {
22387             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22388                 continue;
22389             }
22390             // fullly contained node.
22391             
22392             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22393                 nodes.push(ar[i]);
22394                 continue;
22395             }
22396             
22397             // probably selected..
22398             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22399                 other_nodes.push(ar[i]);
22400                 continue;
22401             }
22402             // outer..
22403             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22404                 continue;
22405             }
22406             
22407             
22408             has_other_nodes = true;
22409         }
22410         if (!nodes.length && other_nodes.length) {
22411             nodes= other_nodes;
22412         }
22413         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22414             return false;
22415         }
22416         
22417         return nodes[0];
22418     },
22419     createRange: function(sel)
22420     {
22421         // this has strange effects when using with 
22422         // top toolbar - not sure if it's a great idea.
22423         //this.editor.contentWindow.focus();
22424         if (typeof sel != "undefined") {
22425             try {
22426                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22427             } catch(e) {
22428                 return this.doc.createRange();
22429             }
22430         } else {
22431             return this.doc.createRange();
22432         }
22433     },
22434     getParentElement: function()
22435     {
22436         
22437         this.assignDocWin();
22438         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22439         
22440         var range = this.createRange(sel);
22441          
22442         try {
22443             var p = range.commonAncestorContainer;
22444             while (p.nodeType == 3) { // text node
22445                 p = p.parentNode;
22446             }
22447             return p;
22448         } catch (e) {
22449             return null;
22450         }
22451     
22452     },
22453     /***
22454      *
22455      * Range intersection.. the hard stuff...
22456      *  '-1' = before
22457      *  '0' = hits..
22458      *  '1' = after.
22459      *         [ -- selected range --- ]
22460      *   [fail]                        [fail]
22461      *
22462      *    basically..
22463      *      if end is before start or  hits it. fail.
22464      *      if start is after end or hits it fail.
22465      *
22466      *   if either hits (but other is outside. - then it's not 
22467      *   
22468      *    
22469      **/
22470     
22471     
22472     // @see http://www.thismuchiknow.co.uk/?p=64.
22473     rangeIntersectsNode : function(range, node)
22474     {
22475         var nodeRange = node.ownerDocument.createRange();
22476         try {
22477             nodeRange.selectNode(node);
22478         } catch (e) {
22479             nodeRange.selectNodeContents(node);
22480         }
22481     
22482         var rangeStartRange = range.cloneRange();
22483         rangeStartRange.collapse(true);
22484     
22485         var rangeEndRange = range.cloneRange();
22486         rangeEndRange.collapse(false);
22487     
22488         var nodeStartRange = nodeRange.cloneRange();
22489         nodeStartRange.collapse(true);
22490     
22491         var nodeEndRange = nodeRange.cloneRange();
22492         nodeEndRange.collapse(false);
22493     
22494         return rangeStartRange.compareBoundaryPoints(
22495                  Range.START_TO_START, nodeEndRange) == -1 &&
22496                rangeEndRange.compareBoundaryPoints(
22497                  Range.START_TO_START, nodeStartRange) == 1;
22498         
22499          
22500     },
22501     rangeCompareNode : function(range, node)
22502     {
22503         var nodeRange = node.ownerDocument.createRange();
22504         try {
22505             nodeRange.selectNode(node);
22506         } catch (e) {
22507             nodeRange.selectNodeContents(node);
22508         }
22509         
22510         
22511         range.collapse(true);
22512     
22513         nodeRange.collapse(true);
22514      
22515         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22516         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22517          
22518         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22519         
22520         var nodeIsBefore   =  ss == 1;
22521         var nodeIsAfter    = ee == -1;
22522         
22523         if (nodeIsBefore && nodeIsAfter) {
22524             return 0; // outer
22525         }
22526         if (!nodeIsBefore && nodeIsAfter) {
22527             return 1; //right trailed.
22528         }
22529         
22530         if (nodeIsBefore && !nodeIsAfter) {
22531             return 2;  // left trailed.
22532         }
22533         // fully contined.
22534         return 3;
22535     },
22536
22537     // private? - in a new class?
22538     cleanUpPaste :  function()
22539     {
22540         // cleans up the whole document..
22541         Roo.log('cleanuppaste');
22542         
22543         this.cleanUpChildren(this.doc.body);
22544         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22545         if (clean != this.doc.body.innerHTML) {
22546             this.doc.body.innerHTML = clean;
22547         }
22548         
22549     },
22550     
22551     cleanWordChars : function(input) {// change the chars to hex code
22552         var he = Roo.HtmlEditorCore;
22553         
22554         var output = input;
22555         Roo.each(he.swapCodes, function(sw) { 
22556             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22557             
22558             output = output.replace(swapper, sw[1]);
22559         });
22560         
22561         return output;
22562     },
22563     
22564     
22565     cleanUpChildren : function (n)
22566     {
22567         if (!n.childNodes.length) {
22568             return;
22569         }
22570         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22571            this.cleanUpChild(n.childNodes[i]);
22572         }
22573     },
22574     
22575     
22576         
22577     
22578     cleanUpChild : function (node)
22579     {
22580         var ed = this;
22581         //console.log(node);
22582         if (node.nodeName == "#text") {
22583             // clean up silly Windows -- stuff?
22584             return; 
22585         }
22586         if (node.nodeName == "#comment") {
22587             node.parentNode.removeChild(node);
22588             // clean up silly Windows -- stuff?
22589             return; 
22590         }
22591         var lcname = node.tagName.toLowerCase();
22592         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22593         // whitelist of tags..
22594         
22595         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22596             // remove node.
22597             node.parentNode.removeChild(node);
22598             return;
22599             
22600         }
22601         
22602         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22603         
22604         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22605         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22606         
22607         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22608         //    remove_keep_children = true;
22609         //}
22610         
22611         if (remove_keep_children) {
22612             this.cleanUpChildren(node);
22613             // inserts everything just before this node...
22614             while (node.childNodes.length) {
22615                 var cn = node.childNodes[0];
22616                 node.removeChild(cn);
22617                 node.parentNode.insertBefore(cn, node);
22618             }
22619             node.parentNode.removeChild(node);
22620             return;
22621         }
22622         
22623         if (!node.attributes || !node.attributes.length) {
22624             this.cleanUpChildren(node);
22625             return;
22626         }
22627         
22628         function cleanAttr(n,v)
22629         {
22630             
22631             if (v.match(/^\./) || v.match(/^\//)) {
22632                 return;
22633             }
22634             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22635                 return;
22636             }
22637             if (v.match(/^#/)) {
22638                 return;
22639             }
22640 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22641             node.removeAttribute(n);
22642             
22643         }
22644         
22645         var cwhite = this.cwhite;
22646         var cblack = this.cblack;
22647             
22648         function cleanStyle(n,v)
22649         {
22650             if (v.match(/expression/)) { //XSS?? should we even bother..
22651                 node.removeAttribute(n);
22652                 return;
22653             }
22654             
22655             var parts = v.split(/;/);
22656             var clean = [];
22657             
22658             Roo.each(parts, function(p) {
22659                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22660                 if (!p.length) {
22661                     return true;
22662                 }
22663                 var l = p.split(':').shift().replace(/\s+/g,'');
22664                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22665                 
22666                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22667 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22668                     //node.removeAttribute(n);
22669                     return true;
22670                 }
22671                 //Roo.log()
22672                 // only allow 'c whitelisted system attributes'
22673                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22674 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22675                     //node.removeAttribute(n);
22676                     return true;
22677                 }
22678                 
22679                 
22680                  
22681                 
22682                 clean.push(p);
22683                 return true;
22684             });
22685             if (clean.length) { 
22686                 node.setAttribute(n, clean.join(';'));
22687             } else {
22688                 node.removeAttribute(n);
22689             }
22690             
22691         }
22692         
22693         
22694         for (var i = node.attributes.length-1; i > -1 ; i--) {
22695             var a = node.attributes[i];
22696             //console.log(a);
22697             
22698             if (a.name.toLowerCase().substr(0,2)=='on')  {
22699                 node.removeAttribute(a.name);
22700                 continue;
22701             }
22702             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22703                 node.removeAttribute(a.name);
22704                 continue;
22705             }
22706             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22707                 cleanAttr(a.name,a.value); // fixme..
22708                 continue;
22709             }
22710             if (a.name == 'style') {
22711                 cleanStyle(a.name,a.value);
22712                 continue;
22713             }
22714             /// clean up MS crap..
22715             // tecnically this should be a list of valid class'es..
22716             
22717             
22718             if (a.name == 'class') {
22719                 if (a.value.match(/^Mso/)) {
22720                     node.className = '';
22721                 }
22722                 
22723                 if (a.value.match(/^body$/)) {
22724                     node.className = '';
22725                 }
22726                 continue;
22727             }
22728             
22729             // style cleanup!?
22730             // class cleanup?
22731             
22732         }
22733         
22734         
22735         this.cleanUpChildren(node);
22736         
22737         
22738     },
22739     
22740     /**
22741      * Clean up MS wordisms...
22742      */
22743     cleanWord : function(node)
22744     {
22745         
22746         
22747         if (!node) {
22748             this.cleanWord(this.doc.body);
22749             return;
22750         }
22751         if (node.nodeName == "#text") {
22752             // clean up silly Windows -- stuff?
22753             return; 
22754         }
22755         if (node.nodeName == "#comment") {
22756             node.parentNode.removeChild(node);
22757             // clean up silly Windows -- stuff?
22758             return; 
22759         }
22760         
22761         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22762             node.parentNode.removeChild(node);
22763             return;
22764         }
22765         
22766         // remove - but keep children..
22767         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22768             while (node.childNodes.length) {
22769                 var cn = node.childNodes[0];
22770                 node.removeChild(cn);
22771                 node.parentNode.insertBefore(cn, node);
22772             }
22773             node.parentNode.removeChild(node);
22774             this.iterateChildren(node, this.cleanWord);
22775             return;
22776         }
22777         // clean styles
22778         if (node.className.length) {
22779             
22780             var cn = node.className.split(/\W+/);
22781             var cna = [];
22782             Roo.each(cn, function(cls) {
22783                 if (cls.match(/Mso[a-zA-Z]+/)) {
22784                     return;
22785                 }
22786                 cna.push(cls);
22787             });
22788             node.className = cna.length ? cna.join(' ') : '';
22789             if (!cna.length) {
22790                 node.removeAttribute("class");
22791             }
22792         }
22793         
22794         if (node.hasAttribute("lang")) {
22795             node.removeAttribute("lang");
22796         }
22797         
22798         if (node.hasAttribute("style")) {
22799             
22800             var styles = node.getAttribute("style").split(";");
22801             var nstyle = [];
22802             Roo.each(styles, function(s) {
22803                 if (!s.match(/:/)) {
22804                     return;
22805                 }
22806                 var kv = s.split(":");
22807                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22808                     return;
22809                 }
22810                 // what ever is left... we allow.
22811                 nstyle.push(s);
22812             });
22813             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22814             if (!nstyle.length) {
22815                 node.removeAttribute('style');
22816             }
22817         }
22818         this.iterateChildren(node, this.cleanWord);
22819         
22820         
22821         
22822     },
22823     /**
22824      * iterateChildren of a Node, calling fn each time, using this as the scole..
22825      * @param {DomNode} node node to iterate children of.
22826      * @param {Function} fn method of this class to call on each item.
22827      */
22828     iterateChildren : function(node, fn)
22829     {
22830         if (!node.childNodes.length) {
22831                 return;
22832         }
22833         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22834            fn.call(this, node.childNodes[i])
22835         }
22836     },
22837     
22838     
22839     /**
22840      * cleanTableWidths.
22841      *
22842      * Quite often pasting from word etc.. results in tables with column and widths.
22843      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22844      *
22845      */
22846     cleanTableWidths : function(node)
22847     {
22848          
22849          
22850         if (!node) {
22851             this.cleanTableWidths(this.doc.body);
22852             return;
22853         }
22854         
22855         // ignore list...
22856         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22857             return; 
22858         }
22859         Roo.log(node.tagName);
22860         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22861             this.iterateChildren(node, this.cleanTableWidths);
22862             return;
22863         }
22864         if (node.hasAttribute('width')) {
22865             node.removeAttribute('width');
22866         }
22867         
22868          
22869         if (node.hasAttribute("style")) {
22870             // pretty basic...
22871             
22872             var styles = node.getAttribute("style").split(";");
22873             var nstyle = [];
22874             Roo.each(styles, function(s) {
22875                 if (!s.match(/:/)) {
22876                     return;
22877                 }
22878                 var kv = s.split(":");
22879                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22880                     return;
22881                 }
22882                 // what ever is left... we allow.
22883                 nstyle.push(s);
22884             });
22885             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22886             if (!nstyle.length) {
22887                 node.removeAttribute('style');
22888             }
22889         }
22890         
22891         this.iterateChildren(node, this.cleanTableWidths);
22892         
22893         
22894     },
22895     
22896     
22897     
22898     
22899     domToHTML : function(currentElement, depth, nopadtext) {
22900         
22901         depth = depth || 0;
22902         nopadtext = nopadtext || false;
22903     
22904         if (!currentElement) {
22905             return this.domToHTML(this.doc.body);
22906         }
22907         
22908         //Roo.log(currentElement);
22909         var j;
22910         var allText = false;
22911         var nodeName = currentElement.nodeName;
22912         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22913         
22914         if  (nodeName == '#text') {
22915             
22916             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22917         }
22918         
22919         
22920         var ret = '';
22921         if (nodeName != 'BODY') {
22922              
22923             var i = 0;
22924             // Prints the node tagName, such as <A>, <IMG>, etc
22925             if (tagName) {
22926                 var attr = [];
22927                 for(i = 0; i < currentElement.attributes.length;i++) {
22928                     // quoting?
22929                     var aname = currentElement.attributes.item(i).name;
22930                     if (!currentElement.attributes.item(i).value.length) {
22931                         continue;
22932                     }
22933                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22934                 }
22935                 
22936                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22937             } 
22938             else {
22939                 
22940                 // eack
22941             }
22942         } else {
22943             tagName = false;
22944         }
22945         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22946             return ret;
22947         }
22948         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22949             nopadtext = true;
22950         }
22951         
22952         
22953         // Traverse the tree
22954         i = 0;
22955         var currentElementChild = currentElement.childNodes.item(i);
22956         var allText = true;
22957         var innerHTML  = '';
22958         lastnode = '';
22959         while (currentElementChild) {
22960             // Formatting code (indent the tree so it looks nice on the screen)
22961             var nopad = nopadtext;
22962             if (lastnode == 'SPAN') {
22963                 nopad  = true;
22964             }
22965             // text
22966             if  (currentElementChild.nodeName == '#text') {
22967                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22968                 toadd = nopadtext ? toadd : toadd.trim();
22969                 if (!nopad && toadd.length > 80) {
22970                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22971                 }
22972                 innerHTML  += toadd;
22973                 
22974                 i++;
22975                 currentElementChild = currentElement.childNodes.item(i);
22976                 lastNode = '';
22977                 continue;
22978             }
22979             allText = false;
22980             
22981             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22982                 
22983             // Recursively traverse the tree structure of the child node
22984             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22985             lastnode = currentElementChild.nodeName;
22986             i++;
22987             currentElementChild=currentElement.childNodes.item(i);
22988         }
22989         
22990         ret += innerHTML;
22991         
22992         if (!allText) {
22993                 // The remaining code is mostly for formatting the tree
22994             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22995         }
22996         
22997         
22998         if (tagName) {
22999             ret+= "</"+tagName+">";
23000         }
23001         return ret;
23002         
23003     },
23004         
23005     applyBlacklists : function()
23006     {
23007         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23008         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23009         
23010         this.white = [];
23011         this.black = [];
23012         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23013             if (b.indexOf(tag) > -1) {
23014                 return;
23015             }
23016             this.white.push(tag);
23017             
23018         }, this);
23019         
23020         Roo.each(w, function(tag) {
23021             if (b.indexOf(tag) > -1) {
23022                 return;
23023             }
23024             if (this.white.indexOf(tag) > -1) {
23025                 return;
23026             }
23027             this.white.push(tag);
23028             
23029         }, this);
23030         
23031         
23032         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23033             if (w.indexOf(tag) > -1) {
23034                 return;
23035             }
23036             this.black.push(tag);
23037             
23038         }, this);
23039         
23040         Roo.each(b, function(tag) {
23041             if (w.indexOf(tag) > -1) {
23042                 return;
23043             }
23044             if (this.black.indexOf(tag) > -1) {
23045                 return;
23046             }
23047             this.black.push(tag);
23048             
23049         }, this);
23050         
23051         
23052         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23053         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23054         
23055         this.cwhite = [];
23056         this.cblack = [];
23057         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23058             if (b.indexOf(tag) > -1) {
23059                 return;
23060             }
23061             this.cwhite.push(tag);
23062             
23063         }, this);
23064         
23065         Roo.each(w, function(tag) {
23066             if (b.indexOf(tag) > -1) {
23067                 return;
23068             }
23069             if (this.cwhite.indexOf(tag) > -1) {
23070                 return;
23071             }
23072             this.cwhite.push(tag);
23073             
23074         }, this);
23075         
23076         
23077         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23078             if (w.indexOf(tag) > -1) {
23079                 return;
23080             }
23081             this.cblack.push(tag);
23082             
23083         }, this);
23084         
23085         Roo.each(b, function(tag) {
23086             if (w.indexOf(tag) > -1) {
23087                 return;
23088             }
23089             if (this.cblack.indexOf(tag) > -1) {
23090                 return;
23091             }
23092             this.cblack.push(tag);
23093             
23094         }, this);
23095     },
23096     
23097     setStylesheets : function(stylesheets)
23098     {
23099         if(typeof(stylesheets) == 'string'){
23100             Roo.get(this.iframe.contentDocument.head).createChild({
23101                 tag : 'link',
23102                 rel : 'stylesheet',
23103                 type : 'text/css',
23104                 href : stylesheets
23105             });
23106             
23107             return;
23108         }
23109         var _this = this;
23110      
23111         Roo.each(stylesheets, function(s) {
23112             if(!s.length){
23113                 return;
23114             }
23115             
23116             Roo.get(_this.iframe.contentDocument.head).createChild({
23117                 tag : 'link',
23118                 rel : 'stylesheet',
23119                 type : 'text/css',
23120                 href : s
23121             });
23122         });
23123
23124         
23125     },
23126     
23127     removeStylesheets : function()
23128     {
23129         var _this = this;
23130         
23131         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23132             s.remove();
23133         });
23134     },
23135     
23136     setStyle : function(style)
23137     {
23138         Roo.get(this.iframe.contentDocument.head).createChild({
23139             tag : 'style',
23140             type : 'text/css',
23141             html : style
23142         });
23143
23144         return;
23145     }
23146     
23147     // hide stuff that is not compatible
23148     /**
23149      * @event blur
23150      * @hide
23151      */
23152     /**
23153      * @event change
23154      * @hide
23155      */
23156     /**
23157      * @event focus
23158      * @hide
23159      */
23160     /**
23161      * @event specialkey
23162      * @hide
23163      */
23164     /**
23165      * @cfg {String} fieldClass @hide
23166      */
23167     /**
23168      * @cfg {String} focusClass @hide
23169      */
23170     /**
23171      * @cfg {String} autoCreate @hide
23172      */
23173     /**
23174      * @cfg {String} inputType @hide
23175      */
23176     /**
23177      * @cfg {String} invalidClass @hide
23178      */
23179     /**
23180      * @cfg {String} invalidText @hide
23181      */
23182     /**
23183      * @cfg {String} msgFx @hide
23184      */
23185     /**
23186      * @cfg {String} validateOnBlur @hide
23187      */
23188 });
23189
23190 Roo.HtmlEditorCore.white = [
23191         'area', 'br', 'img', 'input', 'hr', 'wbr',
23192         
23193        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23194        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23195        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23196        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23197        'table',   'ul',         'xmp', 
23198        
23199        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23200       'thead',   'tr', 
23201      
23202       'dir', 'menu', 'ol', 'ul', 'dl',
23203        
23204       'embed',  'object'
23205 ];
23206
23207
23208 Roo.HtmlEditorCore.black = [
23209     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23210         'applet', // 
23211         'base',   'basefont', 'bgsound', 'blink',  'body', 
23212         'frame',  'frameset', 'head',    'html',   'ilayer', 
23213         'iframe', 'layer',  'link',     'meta',    'object',   
23214         'script', 'style' ,'title',  'xml' // clean later..
23215 ];
23216 Roo.HtmlEditorCore.clean = [
23217     'script', 'style', 'title', 'xml'
23218 ];
23219 Roo.HtmlEditorCore.remove = [
23220     'font'
23221 ];
23222 // attributes..
23223
23224 Roo.HtmlEditorCore.ablack = [
23225     'on'
23226 ];
23227     
23228 Roo.HtmlEditorCore.aclean = [ 
23229     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23230 ];
23231
23232 // protocols..
23233 Roo.HtmlEditorCore.pwhite= [
23234         'http',  'https',  'mailto'
23235 ];
23236
23237 // white listed style attributes.
23238 Roo.HtmlEditorCore.cwhite= [
23239       //  'text-align', /// default is to allow most things..
23240       
23241          
23242 //        'font-size'//??
23243 ];
23244
23245 // black listed style attributes.
23246 Roo.HtmlEditorCore.cblack= [
23247       //  'font-size' -- this can be set by the project 
23248 ];
23249
23250
23251 Roo.HtmlEditorCore.swapCodes   =[ 
23252     [    8211, "--" ], 
23253     [    8212, "--" ], 
23254     [    8216,  "'" ],  
23255     [    8217, "'" ],  
23256     [    8220, '"' ],  
23257     [    8221, '"' ],  
23258     [    8226, "*" ],  
23259     [    8230, "..." ]
23260 ]; 
23261
23262     /*
23263  * - LGPL
23264  *
23265  * HtmlEditor
23266  * 
23267  */
23268
23269 /**
23270  * @class Roo.bootstrap.HtmlEditor
23271  * @extends Roo.bootstrap.TextArea
23272  * Bootstrap HtmlEditor class
23273
23274  * @constructor
23275  * Create a new HtmlEditor
23276  * @param {Object} config The config object
23277  */
23278
23279 Roo.bootstrap.HtmlEditor = function(config){
23280     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23281     if (!this.toolbars) {
23282         this.toolbars = [];
23283     }
23284     
23285     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23286     this.addEvents({
23287             /**
23288              * @event initialize
23289              * Fires when the editor is fully initialized (including the iframe)
23290              * @param {HtmlEditor} this
23291              */
23292             initialize: true,
23293             /**
23294              * @event activate
23295              * Fires when the editor is first receives the focus. Any insertion must wait
23296              * until after this event.
23297              * @param {HtmlEditor} this
23298              */
23299             activate: true,
23300              /**
23301              * @event beforesync
23302              * Fires before the textarea is updated with content from the editor iframe. Return false
23303              * to cancel the sync.
23304              * @param {HtmlEditor} this
23305              * @param {String} html
23306              */
23307             beforesync: true,
23308              /**
23309              * @event beforepush
23310              * Fires before the iframe editor is updated with content from the textarea. Return false
23311              * to cancel the push.
23312              * @param {HtmlEditor} this
23313              * @param {String} html
23314              */
23315             beforepush: true,
23316              /**
23317              * @event sync
23318              * Fires when the textarea is updated with content from the editor iframe.
23319              * @param {HtmlEditor} this
23320              * @param {String} html
23321              */
23322             sync: true,
23323              /**
23324              * @event push
23325              * Fires when the iframe editor is updated with content from the textarea.
23326              * @param {HtmlEditor} this
23327              * @param {String} html
23328              */
23329             push: true,
23330              /**
23331              * @event editmodechange
23332              * Fires when the editor switches edit modes
23333              * @param {HtmlEditor} this
23334              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23335              */
23336             editmodechange: true,
23337             /**
23338              * @event editorevent
23339              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23340              * @param {HtmlEditor} this
23341              */
23342             editorevent: true,
23343             /**
23344              * @event firstfocus
23345              * Fires when on first focus - needed by toolbars..
23346              * @param {HtmlEditor} this
23347              */
23348             firstfocus: true,
23349             /**
23350              * @event autosave
23351              * Auto save the htmlEditor value as a file into Events
23352              * @param {HtmlEditor} this
23353              */
23354             autosave: true,
23355             /**
23356              * @event savedpreview
23357              * preview the saved version of htmlEditor
23358              * @param {HtmlEditor} this
23359              */
23360             savedpreview: true
23361         });
23362 };
23363
23364
23365 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23366     
23367     
23368       /**
23369      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23370      */
23371     toolbars : false,
23372     
23373      /**
23374     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23375     */
23376     btns : [],
23377    
23378      /**
23379      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23380      *                        Roo.resizable.
23381      */
23382     resizable : false,
23383      /**
23384      * @cfg {Number} height (in pixels)
23385      */   
23386     height: 300,
23387    /**
23388      * @cfg {Number} width (in pixels)
23389      */   
23390     width: false,
23391     
23392     /**
23393      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23394      * 
23395      */
23396     stylesheets: false,
23397     
23398     // id of frame..
23399     frameId: false,
23400     
23401     // private properties
23402     validationEvent : false,
23403     deferHeight: true,
23404     initialized : false,
23405     activated : false,
23406     
23407     onFocus : Roo.emptyFn,
23408     iframePad:3,
23409     hideMode:'offsets',
23410     
23411     tbContainer : false,
23412     
23413     bodyCls : '',
23414     
23415     toolbarContainer :function() {
23416         return this.wrap.select('.x-html-editor-tb',true).first();
23417     },
23418
23419     /**
23420      * Protected method that will not generally be called directly. It
23421      * is called when the editor creates its toolbar. Override this method if you need to
23422      * add custom toolbar buttons.
23423      * @param {HtmlEditor} editor
23424      */
23425     createToolbar : function(){
23426         Roo.log('renewing');
23427         Roo.log("create toolbars");
23428         
23429         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23430         this.toolbars[0].render(this.toolbarContainer());
23431         
23432         return;
23433         
23434 //        if (!editor.toolbars || !editor.toolbars.length) {
23435 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23436 //        }
23437 //        
23438 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23439 //            editor.toolbars[i] = Roo.factory(
23440 //                    typeof(editor.toolbars[i]) == 'string' ?
23441 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23442 //                Roo.bootstrap.HtmlEditor);
23443 //            editor.toolbars[i].init(editor);
23444 //        }
23445     },
23446
23447      
23448     // private
23449     onRender : function(ct, position)
23450     {
23451        // Roo.log("Call onRender: " + this.xtype);
23452         var _t = this;
23453         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23454       
23455         this.wrap = this.inputEl().wrap({
23456             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23457         });
23458         
23459         this.editorcore.onRender(ct, position);
23460          
23461         if (this.resizable) {
23462             this.resizeEl = new Roo.Resizable(this.wrap, {
23463                 pinned : true,
23464                 wrap: true,
23465                 dynamic : true,
23466                 minHeight : this.height,
23467                 height: this.height,
23468                 handles : this.resizable,
23469                 width: this.width,
23470                 listeners : {
23471                     resize : function(r, w, h) {
23472                         _t.onResize(w,h); // -something
23473                     }
23474                 }
23475             });
23476             
23477         }
23478         this.createToolbar(this);
23479        
23480         
23481         if(!this.width && this.resizable){
23482             this.setSize(this.wrap.getSize());
23483         }
23484         if (this.resizeEl) {
23485             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23486             // should trigger onReize..
23487         }
23488         
23489     },
23490
23491     // private
23492     onResize : function(w, h)
23493     {
23494         Roo.log('resize: ' +w + ',' + h );
23495         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23496         var ew = false;
23497         var eh = false;
23498         
23499         if(this.inputEl() ){
23500             if(typeof w == 'number'){
23501                 var aw = w - this.wrap.getFrameWidth('lr');
23502                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23503                 ew = aw;
23504             }
23505             if(typeof h == 'number'){
23506                  var tbh = -11;  // fixme it needs to tool bar size!
23507                 for (var i =0; i < this.toolbars.length;i++) {
23508                     // fixme - ask toolbars for heights?
23509                     tbh += this.toolbars[i].el.getHeight();
23510                     //if (this.toolbars[i].footer) {
23511                     //    tbh += this.toolbars[i].footer.el.getHeight();
23512                     //}
23513                 }
23514               
23515                 
23516                 
23517                 
23518                 
23519                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23520                 ah -= 5; // knock a few pixes off for look..
23521                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23522                 var eh = ah;
23523             }
23524         }
23525         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23526         this.editorcore.onResize(ew,eh);
23527         
23528     },
23529
23530     /**
23531      * Toggles the editor between standard and source edit mode.
23532      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23533      */
23534     toggleSourceEdit : function(sourceEditMode)
23535     {
23536         this.editorcore.toggleSourceEdit(sourceEditMode);
23537         
23538         if(this.editorcore.sourceEditMode){
23539             Roo.log('editor - showing textarea');
23540             
23541 //            Roo.log('in');
23542 //            Roo.log(this.syncValue());
23543             this.syncValue();
23544             this.inputEl().removeClass(['hide', 'x-hidden']);
23545             this.inputEl().dom.removeAttribute('tabIndex');
23546             this.inputEl().focus();
23547         }else{
23548             Roo.log('editor - hiding textarea');
23549 //            Roo.log('out')
23550 //            Roo.log(this.pushValue()); 
23551             this.pushValue();
23552             
23553             this.inputEl().addClass(['hide', 'x-hidden']);
23554             this.inputEl().dom.setAttribute('tabIndex', -1);
23555             //this.deferFocus();
23556         }
23557          
23558         if(this.resizable){
23559             this.setSize(this.wrap.getSize());
23560         }
23561         
23562         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23563     },
23564  
23565     // private (for BoxComponent)
23566     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23567
23568     // private (for BoxComponent)
23569     getResizeEl : function(){
23570         return this.wrap;
23571     },
23572
23573     // private (for BoxComponent)
23574     getPositionEl : function(){
23575         return this.wrap;
23576     },
23577
23578     // private
23579     initEvents : function(){
23580         this.originalValue = this.getValue();
23581     },
23582
23583 //    /**
23584 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23585 //     * @method
23586 //     */
23587 //    markInvalid : Roo.emptyFn,
23588 //    /**
23589 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23590 //     * @method
23591 //     */
23592 //    clearInvalid : Roo.emptyFn,
23593
23594     setValue : function(v){
23595         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23596         this.editorcore.pushValue();
23597     },
23598
23599      
23600     // private
23601     deferFocus : function(){
23602         this.focus.defer(10, this);
23603     },
23604
23605     // doc'ed in Field
23606     focus : function(){
23607         this.editorcore.focus();
23608         
23609     },
23610       
23611
23612     // private
23613     onDestroy : function(){
23614         
23615         
23616         
23617         if(this.rendered){
23618             
23619             for (var i =0; i < this.toolbars.length;i++) {
23620                 // fixme - ask toolbars for heights?
23621                 this.toolbars[i].onDestroy();
23622             }
23623             
23624             this.wrap.dom.innerHTML = '';
23625             this.wrap.remove();
23626         }
23627     },
23628
23629     // private
23630     onFirstFocus : function(){
23631         //Roo.log("onFirstFocus");
23632         this.editorcore.onFirstFocus();
23633          for (var i =0; i < this.toolbars.length;i++) {
23634             this.toolbars[i].onFirstFocus();
23635         }
23636         
23637     },
23638     
23639     // private
23640     syncValue : function()
23641     {   
23642         this.editorcore.syncValue();
23643     },
23644     
23645     pushValue : function()
23646     {   
23647         this.editorcore.pushValue();
23648     }
23649      
23650     
23651     // hide stuff that is not compatible
23652     /**
23653      * @event blur
23654      * @hide
23655      */
23656     /**
23657      * @event change
23658      * @hide
23659      */
23660     /**
23661      * @event focus
23662      * @hide
23663      */
23664     /**
23665      * @event specialkey
23666      * @hide
23667      */
23668     /**
23669      * @cfg {String} fieldClass @hide
23670      */
23671     /**
23672      * @cfg {String} focusClass @hide
23673      */
23674     /**
23675      * @cfg {String} autoCreate @hide
23676      */
23677     /**
23678      * @cfg {String} inputType @hide
23679      */
23680     /**
23681      * @cfg {String} invalidClass @hide
23682      */
23683     /**
23684      * @cfg {String} invalidText @hide
23685      */
23686     /**
23687      * @cfg {String} msgFx @hide
23688      */
23689     /**
23690      * @cfg {String} validateOnBlur @hide
23691      */
23692 });
23693  
23694     
23695    
23696    
23697    
23698       
23699 Roo.namespace('Roo.bootstrap.htmleditor');
23700 /**
23701  * @class Roo.bootstrap.HtmlEditorToolbar1
23702  * Basic Toolbar
23703  * 
23704  * Usage:
23705  *
23706  new Roo.bootstrap.HtmlEditor({
23707     ....
23708     toolbars : [
23709         new Roo.bootstrap.HtmlEditorToolbar1({
23710             disable : { fonts: 1 , format: 1, ..., ... , ...],
23711             btns : [ .... ]
23712         })
23713     }
23714      
23715  * 
23716  * @cfg {Object} disable List of elements to disable..
23717  * @cfg {Array} btns List of additional buttons.
23718  * 
23719  * 
23720  * NEEDS Extra CSS? 
23721  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23722  */
23723  
23724 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23725 {
23726     
23727     Roo.apply(this, config);
23728     
23729     // default disabled, based on 'good practice'..
23730     this.disable = this.disable || {};
23731     Roo.applyIf(this.disable, {
23732         fontSize : true,
23733         colors : true,
23734         specialElements : true
23735     });
23736     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23737     
23738     this.editor = config.editor;
23739     this.editorcore = config.editor.editorcore;
23740     
23741     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23742     
23743     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23744     // dont call parent... till later.
23745 }
23746 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23747      
23748     bar : true,
23749     
23750     editor : false,
23751     editorcore : false,
23752     
23753     
23754     formats : [
23755         "p" ,  
23756         "h1","h2","h3","h4","h5","h6", 
23757         "pre", "code", 
23758         "abbr", "acronym", "address", "cite", "samp", "var",
23759         'div','span'
23760     ],
23761     
23762     onRender : function(ct, position)
23763     {
23764        // Roo.log("Call onRender: " + this.xtype);
23765         
23766        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23767        Roo.log(this.el);
23768        this.el.dom.style.marginBottom = '0';
23769        var _this = this;
23770        var editorcore = this.editorcore;
23771        var editor= this.editor;
23772        
23773        var children = [];
23774        var btn = function(id,cmd , toggle, handler, html){
23775        
23776             var  event = toggle ? 'toggle' : 'click';
23777        
23778             var a = {
23779                 size : 'sm',
23780                 xtype: 'Button',
23781                 xns: Roo.bootstrap,
23782                 glyphicon : id,
23783                 cmd : id || cmd,
23784                 enableToggle:toggle !== false,
23785                 html : html || '',
23786                 pressed : toggle ? false : null,
23787                 listeners : {}
23788             };
23789             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23790                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23791             };
23792             children.push(a);
23793             return a;
23794        }
23795        
23796     //    var cb_box = function...
23797         
23798         var style = {
23799                 xtype: 'Button',
23800                 size : 'sm',
23801                 xns: Roo.bootstrap,
23802                 glyphicon : 'font',
23803                 //html : 'submit'
23804                 menu : {
23805                     xtype: 'Menu',
23806                     xns: Roo.bootstrap,
23807                     items:  []
23808                 }
23809         };
23810         Roo.each(this.formats, function(f) {
23811             style.menu.items.push({
23812                 xtype :'MenuItem',
23813                 xns: Roo.bootstrap,
23814                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23815                 tagname : f,
23816                 listeners : {
23817                     click : function()
23818                     {
23819                         editorcore.insertTag(this.tagname);
23820                         editor.focus();
23821                     }
23822                 }
23823                 
23824             });
23825         });
23826         children.push(style);   
23827         
23828         btn('bold',false,true);
23829         btn('italic',false,true);
23830         btn('align-left', 'justifyleft',true);
23831         btn('align-center', 'justifycenter',true);
23832         btn('align-right' , 'justifyright',true);
23833         btn('link', false, false, function(btn) {
23834             //Roo.log("create link?");
23835             var url = prompt(this.createLinkText, this.defaultLinkValue);
23836             if(url && url != 'http:/'+'/'){
23837                 this.editorcore.relayCmd('createlink', url);
23838             }
23839         }),
23840         btn('list','insertunorderedlist',true);
23841         btn('pencil', false,true, function(btn){
23842                 Roo.log(this);
23843                 this.toggleSourceEdit(btn.pressed);
23844         });
23845         
23846         if (this.editor.btns.length > 0) {
23847             for (var i = 0; i<this.editor.btns.length; i++) {
23848                 children.push(this.editor.btns[i]);
23849             }
23850         }
23851         
23852         /*
23853         var cog = {
23854                 xtype: 'Button',
23855                 size : 'sm',
23856                 xns: Roo.bootstrap,
23857                 glyphicon : 'cog',
23858                 //html : 'submit'
23859                 menu : {
23860                     xtype: 'Menu',
23861                     xns: Roo.bootstrap,
23862                     items:  []
23863                 }
23864         };
23865         
23866         cog.menu.items.push({
23867             xtype :'MenuItem',
23868             xns: Roo.bootstrap,
23869             html : Clean styles,
23870             tagname : f,
23871             listeners : {
23872                 click : function()
23873                 {
23874                     editorcore.insertTag(this.tagname);
23875                     editor.focus();
23876                 }
23877             }
23878             
23879         });
23880        */
23881         
23882          
23883        this.xtype = 'NavSimplebar';
23884         
23885         for(var i=0;i< children.length;i++) {
23886             
23887             this.buttons.add(this.addxtypeChild(children[i]));
23888             
23889         }
23890         
23891         editor.on('editorevent', this.updateToolbar, this);
23892     },
23893     onBtnClick : function(id)
23894     {
23895        this.editorcore.relayCmd(id);
23896        this.editorcore.focus();
23897     },
23898     
23899     /**
23900      * Protected method that will not generally be called directly. It triggers
23901      * a toolbar update by reading the markup state of the current selection in the editor.
23902      */
23903     updateToolbar: function(){
23904
23905         if(!this.editorcore.activated){
23906             this.editor.onFirstFocus(); // is this neeed?
23907             return;
23908         }
23909
23910         var btns = this.buttons; 
23911         var doc = this.editorcore.doc;
23912         btns.get('bold').setActive(doc.queryCommandState('bold'));
23913         btns.get('italic').setActive(doc.queryCommandState('italic'));
23914         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23915         
23916         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23917         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23918         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23919         
23920         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23921         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23922          /*
23923         
23924         var ans = this.editorcore.getAllAncestors();
23925         if (this.formatCombo) {
23926             
23927             
23928             var store = this.formatCombo.store;
23929             this.formatCombo.setValue("");
23930             for (var i =0; i < ans.length;i++) {
23931                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23932                     // select it..
23933                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23934                     break;
23935                 }
23936             }
23937         }
23938         
23939         
23940         
23941         // hides menus... - so this cant be on a menu...
23942         Roo.bootstrap.MenuMgr.hideAll();
23943         */
23944         Roo.bootstrap.MenuMgr.hideAll();
23945         //this.editorsyncValue();
23946     },
23947     onFirstFocus: function() {
23948         this.buttons.each(function(item){
23949            item.enable();
23950         });
23951     },
23952     toggleSourceEdit : function(sourceEditMode){
23953         
23954           
23955         if(sourceEditMode){
23956             Roo.log("disabling buttons");
23957            this.buttons.each( function(item){
23958                 if(item.cmd != 'pencil'){
23959                     item.disable();
23960                 }
23961             });
23962           
23963         }else{
23964             Roo.log("enabling buttons");
23965             if(this.editorcore.initialized){
23966                 this.buttons.each( function(item){
23967                     item.enable();
23968                 });
23969             }
23970             
23971         }
23972         Roo.log("calling toggole on editor");
23973         // tell the editor that it's been pressed..
23974         this.editor.toggleSourceEdit(sourceEditMode);
23975        
23976     }
23977 });
23978
23979
23980
23981
23982
23983 /**
23984  * @class Roo.bootstrap.Table.AbstractSelectionModel
23985  * @extends Roo.util.Observable
23986  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23987  * implemented by descendant classes.  This class should not be directly instantiated.
23988  * @constructor
23989  */
23990 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23991     this.locked = false;
23992     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23993 };
23994
23995
23996 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23997     /** @ignore Called by the grid automatically. Do not call directly. */
23998     init : function(grid){
23999         this.grid = grid;
24000         this.initEvents();
24001     },
24002
24003     /**
24004      * Locks the selections.
24005      */
24006     lock : function(){
24007         this.locked = true;
24008     },
24009
24010     /**
24011      * Unlocks the selections.
24012      */
24013     unlock : function(){
24014         this.locked = false;
24015     },
24016
24017     /**
24018      * Returns true if the selections are locked.
24019      * @return {Boolean}
24020      */
24021     isLocked : function(){
24022         return this.locked;
24023     }
24024 });
24025 /**
24026  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24027  * @class Roo.bootstrap.Table.RowSelectionModel
24028  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24029  * It supports multiple selections and keyboard selection/navigation. 
24030  * @constructor
24031  * @param {Object} config
24032  */
24033
24034 Roo.bootstrap.Table.RowSelectionModel = function(config){
24035     Roo.apply(this, config);
24036     this.selections = new Roo.util.MixedCollection(false, function(o){
24037         return o.id;
24038     });
24039
24040     this.last = false;
24041     this.lastActive = false;
24042
24043     this.addEvents({
24044         /**
24045              * @event selectionchange
24046              * Fires when the selection changes
24047              * @param {SelectionModel} this
24048              */
24049             "selectionchange" : true,
24050         /**
24051              * @event afterselectionchange
24052              * Fires after the selection changes (eg. by key press or clicking)
24053              * @param {SelectionModel} this
24054              */
24055             "afterselectionchange" : true,
24056         /**
24057              * @event beforerowselect
24058              * Fires when a row is selected being selected, return false to cancel.
24059              * @param {SelectionModel} this
24060              * @param {Number} rowIndex The selected index
24061              * @param {Boolean} keepExisting False if other selections will be cleared
24062              */
24063             "beforerowselect" : true,
24064         /**
24065              * @event rowselect
24066              * Fires when a row is selected.
24067              * @param {SelectionModel} this
24068              * @param {Number} rowIndex The selected index
24069              * @param {Roo.data.Record} r The record
24070              */
24071             "rowselect" : true,
24072         /**
24073              * @event rowdeselect
24074              * Fires when a row is deselected.
24075              * @param {SelectionModel} this
24076              * @param {Number} rowIndex The selected index
24077              */
24078         "rowdeselect" : true
24079     });
24080     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24081     this.locked = false;
24082  };
24083
24084 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24085     /**
24086      * @cfg {Boolean} singleSelect
24087      * True to allow selection of only one row at a time (defaults to false)
24088      */
24089     singleSelect : false,
24090
24091     // private
24092     initEvents : function()
24093     {
24094
24095         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24096         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24097         //}else{ // allow click to work like normal
24098          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24099         //}
24100         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24101         this.grid.on("rowclick", this.handleMouseDown, this);
24102         
24103         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24104             "up" : function(e){
24105                 if(!e.shiftKey){
24106                     this.selectPrevious(e.shiftKey);
24107                 }else if(this.last !== false && this.lastActive !== false){
24108                     var last = this.last;
24109                     this.selectRange(this.last,  this.lastActive-1);
24110                     this.grid.getView().focusRow(this.lastActive);
24111                     if(last !== false){
24112                         this.last = last;
24113                     }
24114                 }else{
24115                     this.selectFirstRow();
24116                 }
24117                 this.fireEvent("afterselectionchange", this);
24118             },
24119             "down" : function(e){
24120                 if(!e.shiftKey){
24121                     this.selectNext(e.shiftKey);
24122                 }else if(this.last !== false && this.lastActive !== false){
24123                     var last = this.last;
24124                     this.selectRange(this.last,  this.lastActive+1);
24125                     this.grid.getView().focusRow(this.lastActive);
24126                     if(last !== false){
24127                         this.last = last;
24128                     }
24129                 }else{
24130                     this.selectFirstRow();
24131                 }
24132                 this.fireEvent("afterselectionchange", this);
24133             },
24134             scope: this
24135         });
24136         this.grid.store.on('load', function(){
24137             this.selections.clear();
24138         },this);
24139         /*
24140         var view = this.grid.view;
24141         view.on("refresh", this.onRefresh, this);
24142         view.on("rowupdated", this.onRowUpdated, this);
24143         view.on("rowremoved", this.onRemove, this);
24144         */
24145     },
24146
24147     // private
24148     onRefresh : function()
24149     {
24150         var ds = this.grid.store, i, v = this.grid.view;
24151         var s = this.selections;
24152         s.each(function(r){
24153             if((i = ds.indexOfId(r.id)) != -1){
24154                 v.onRowSelect(i);
24155             }else{
24156                 s.remove(r);
24157             }
24158         });
24159     },
24160
24161     // private
24162     onRemove : function(v, index, r){
24163         this.selections.remove(r);
24164     },
24165
24166     // private
24167     onRowUpdated : function(v, index, r){
24168         if(this.isSelected(r)){
24169             v.onRowSelect(index);
24170         }
24171     },
24172
24173     /**
24174      * Select records.
24175      * @param {Array} records The records to select
24176      * @param {Boolean} keepExisting (optional) True to keep existing selections
24177      */
24178     selectRecords : function(records, keepExisting)
24179     {
24180         if(!keepExisting){
24181             this.clearSelections();
24182         }
24183             var ds = this.grid.store;
24184         for(var i = 0, len = records.length; i < len; i++){
24185             this.selectRow(ds.indexOf(records[i]), true);
24186         }
24187     },
24188
24189     /**
24190      * Gets the number of selected rows.
24191      * @return {Number}
24192      */
24193     getCount : function(){
24194         return this.selections.length;
24195     },
24196
24197     /**
24198      * Selects the first row in the grid.
24199      */
24200     selectFirstRow : function(){
24201         this.selectRow(0);
24202     },
24203
24204     /**
24205      * Select the last row.
24206      * @param {Boolean} keepExisting (optional) True to keep existing selections
24207      */
24208     selectLastRow : function(keepExisting){
24209         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24210         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24211     },
24212
24213     /**
24214      * Selects the row immediately following the last selected row.
24215      * @param {Boolean} keepExisting (optional) True to keep existing selections
24216      */
24217     selectNext : function(keepExisting)
24218     {
24219             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24220             this.selectRow(this.last+1, keepExisting);
24221             this.grid.getView().focusRow(this.last);
24222         }
24223     },
24224
24225     /**
24226      * Selects the row that precedes the last selected row.
24227      * @param {Boolean} keepExisting (optional) True to keep existing selections
24228      */
24229     selectPrevious : function(keepExisting){
24230         if(this.last){
24231             this.selectRow(this.last-1, keepExisting);
24232             this.grid.getView().focusRow(this.last);
24233         }
24234     },
24235
24236     /**
24237      * Returns the selected records
24238      * @return {Array} Array of selected records
24239      */
24240     getSelections : function(){
24241         return [].concat(this.selections.items);
24242     },
24243
24244     /**
24245      * Returns the first selected record.
24246      * @return {Record}
24247      */
24248     getSelected : function(){
24249         return this.selections.itemAt(0);
24250     },
24251
24252
24253     /**
24254      * Clears all selections.
24255      */
24256     clearSelections : function(fast)
24257     {
24258         if(this.locked) {
24259             return;
24260         }
24261         if(fast !== true){
24262                 var ds = this.grid.store;
24263             var s = this.selections;
24264             s.each(function(r){
24265                 this.deselectRow(ds.indexOfId(r.id));
24266             }, this);
24267             s.clear();
24268         }else{
24269             this.selections.clear();
24270         }
24271         this.last = false;
24272     },
24273
24274
24275     /**
24276      * Selects all rows.
24277      */
24278     selectAll : function(){
24279         if(this.locked) {
24280             return;
24281         }
24282         this.selections.clear();
24283         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24284             this.selectRow(i, true);
24285         }
24286     },
24287
24288     /**
24289      * Returns True if there is a selection.
24290      * @return {Boolean}
24291      */
24292     hasSelection : function(){
24293         return this.selections.length > 0;
24294     },
24295
24296     /**
24297      * Returns True if the specified row is selected.
24298      * @param {Number/Record} record The record or index of the record to check
24299      * @return {Boolean}
24300      */
24301     isSelected : function(index){
24302             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24303         return (r && this.selections.key(r.id) ? true : false);
24304     },
24305
24306     /**
24307      * Returns True if the specified record id is selected.
24308      * @param {String} id The id of record to check
24309      * @return {Boolean}
24310      */
24311     isIdSelected : function(id){
24312         return (this.selections.key(id) ? true : false);
24313     },
24314
24315
24316     // private
24317     handleMouseDBClick : function(e, t){
24318         
24319     },
24320     // private
24321     handleMouseDown : function(e, t)
24322     {
24323             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24324         if(this.isLocked() || rowIndex < 0 ){
24325             return;
24326         };
24327         if(e.shiftKey && this.last !== false){
24328             var last = this.last;
24329             this.selectRange(last, rowIndex, e.ctrlKey);
24330             this.last = last; // reset the last
24331             t.focus();
24332     
24333         }else{
24334             var isSelected = this.isSelected(rowIndex);
24335             //Roo.log("select row:" + rowIndex);
24336             if(isSelected){
24337                 this.deselectRow(rowIndex);
24338             } else {
24339                         this.selectRow(rowIndex, true);
24340             }
24341     
24342             /*
24343                 if(e.button !== 0 && isSelected){
24344                 alert('rowIndex 2: ' + rowIndex);
24345                     view.focusRow(rowIndex);
24346                 }else if(e.ctrlKey && isSelected){
24347                     this.deselectRow(rowIndex);
24348                 }else if(!isSelected){
24349                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24350                     view.focusRow(rowIndex);
24351                 }
24352             */
24353         }
24354         this.fireEvent("afterselectionchange", this);
24355     },
24356     // private
24357     handleDragableRowClick :  function(grid, rowIndex, e) 
24358     {
24359         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24360             this.selectRow(rowIndex, false);
24361             grid.view.focusRow(rowIndex);
24362              this.fireEvent("afterselectionchange", this);
24363         }
24364     },
24365     
24366     /**
24367      * Selects multiple rows.
24368      * @param {Array} rows Array of the indexes of the row to select
24369      * @param {Boolean} keepExisting (optional) True to keep existing selections
24370      */
24371     selectRows : function(rows, keepExisting){
24372         if(!keepExisting){
24373             this.clearSelections();
24374         }
24375         for(var i = 0, len = rows.length; i < len; i++){
24376             this.selectRow(rows[i], true);
24377         }
24378     },
24379
24380     /**
24381      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24382      * @param {Number} startRow The index of the first row in the range
24383      * @param {Number} endRow The index of the last row in the range
24384      * @param {Boolean} keepExisting (optional) True to retain existing selections
24385      */
24386     selectRange : function(startRow, endRow, keepExisting){
24387         if(this.locked) {
24388             return;
24389         }
24390         if(!keepExisting){
24391             this.clearSelections();
24392         }
24393         if(startRow <= endRow){
24394             for(var i = startRow; i <= endRow; i++){
24395                 this.selectRow(i, true);
24396             }
24397         }else{
24398             for(var i = startRow; i >= endRow; i--){
24399                 this.selectRow(i, true);
24400             }
24401         }
24402     },
24403
24404     /**
24405      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24406      * @param {Number} startRow The index of the first row in the range
24407      * @param {Number} endRow The index of the last row in the range
24408      */
24409     deselectRange : function(startRow, endRow, preventViewNotify){
24410         if(this.locked) {
24411             return;
24412         }
24413         for(var i = startRow; i <= endRow; i++){
24414             this.deselectRow(i, preventViewNotify);
24415         }
24416     },
24417
24418     /**
24419      * Selects a row.
24420      * @param {Number} row The index of the row to select
24421      * @param {Boolean} keepExisting (optional) True to keep existing selections
24422      */
24423     selectRow : function(index, keepExisting, preventViewNotify)
24424     {
24425             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24426             return;
24427         }
24428         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24429             if(!keepExisting || this.singleSelect){
24430                 this.clearSelections();
24431             }
24432             
24433             var r = this.grid.store.getAt(index);
24434             //console.log('selectRow - record id :' + r.id);
24435             
24436             this.selections.add(r);
24437             this.last = this.lastActive = index;
24438             if(!preventViewNotify){
24439                 var proxy = new Roo.Element(
24440                                 this.grid.getRowDom(index)
24441                 );
24442                 proxy.addClass('bg-info info');
24443             }
24444             this.fireEvent("rowselect", this, index, r);
24445             this.fireEvent("selectionchange", this);
24446         }
24447     },
24448
24449     /**
24450      * Deselects a row.
24451      * @param {Number} row The index of the row to deselect
24452      */
24453     deselectRow : function(index, preventViewNotify)
24454     {
24455         if(this.locked) {
24456             return;
24457         }
24458         if(this.last == index){
24459             this.last = false;
24460         }
24461         if(this.lastActive == index){
24462             this.lastActive = false;
24463         }
24464         
24465         var r = this.grid.store.getAt(index);
24466         if (!r) {
24467             return;
24468         }
24469         
24470         this.selections.remove(r);
24471         //.console.log('deselectRow - record id :' + r.id);
24472         if(!preventViewNotify){
24473         
24474             var proxy = new Roo.Element(
24475                 this.grid.getRowDom(index)
24476             );
24477             proxy.removeClass('bg-info info');
24478         }
24479         this.fireEvent("rowdeselect", this, index);
24480         this.fireEvent("selectionchange", this);
24481     },
24482
24483     // private
24484     restoreLast : function(){
24485         if(this._last){
24486             this.last = this._last;
24487         }
24488     },
24489
24490     // private
24491     acceptsNav : function(row, col, cm){
24492         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24493     },
24494
24495     // private
24496     onEditorKey : function(field, e){
24497         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24498         if(k == e.TAB){
24499             e.stopEvent();
24500             ed.completeEdit();
24501             if(e.shiftKey){
24502                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24503             }else{
24504                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24505             }
24506         }else if(k == e.ENTER && !e.ctrlKey){
24507             e.stopEvent();
24508             ed.completeEdit();
24509             if(e.shiftKey){
24510                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24511             }else{
24512                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24513             }
24514         }else if(k == e.ESC){
24515             ed.cancelEdit();
24516         }
24517         if(newCell){
24518             g.startEditing(newCell[0], newCell[1]);
24519         }
24520     }
24521 });
24522 /*
24523  * Based on:
24524  * Ext JS Library 1.1.1
24525  * Copyright(c) 2006-2007, Ext JS, LLC.
24526  *
24527  * Originally Released Under LGPL - original licence link has changed is not relivant.
24528  *
24529  * Fork - LGPL
24530  * <script type="text/javascript">
24531  */
24532  
24533 /**
24534  * @class Roo.bootstrap.PagingToolbar
24535  * @extends Roo.bootstrap.NavSimplebar
24536  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24537  * @constructor
24538  * Create a new PagingToolbar
24539  * @param {Object} config The config object
24540  * @param {Roo.data.Store} store
24541  */
24542 Roo.bootstrap.PagingToolbar = function(config)
24543 {
24544     // old args format still supported... - xtype is prefered..
24545         // created from xtype...
24546     
24547     this.ds = config.dataSource;
24548     
24549     if (config.store && !this.ds) {
24550         this.store= Roo.factory(config.store, Roo.data);
24551         this.ds = this.store;
24552         this.ds.xmodule = this.xmodule || false;
24553     }
24554     
24555     this.toolbarItems = [];
24556     if (config.items) {
24557         this.toolbarItems = config.items;
24558     }
24559     
24560     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24561     
24562     this.cursor = 0;
24563     
24564     if (this.ds) { 
24565         this.bind(this.ds);
24566     }
24567     
24568     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24569     
24570 };
24571
24572 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24573     /**
24574      * @cfg {Roo.data.Store} dataSource
24575      * The underlying data store providing the paged data
24576      */
24577     /**
24578      * @cfg {String/HTMLElement/Element} container
24579      * container The id or element that will contain the toolbar
24580      */
24581     /**
24582      * @cfg {Boolean} displayInfo
24583      * True to display the displayMsg (defaults to false)
24584      */
24585     /**
24586      * @cfg {Number} pageSize
24587      * The number of records to display per page (defaults to 20)
24588      */
24589     pageSize: 20,
24590     /**
24591      * @cfg {String} displayMsg
24592      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24593      */
24594     displayMsg : 'Displaying {0} - {1} of {2}',
24595     /**
24596      * @cfg {String} emptyMsg
24597      * The message to display when no records are found (defaults to "No data to display")
24598      */
24599     emptyMsg : 'No data to display',
24600     /**
24601      * Customizable piece of the default paging text (defaults to "Page")
24602      * @type String
24603      */
24604     beforePageText : "Page",
24605     /**
24606      * Customizable piece of the default paging text (defaults to "of %0")
24607      * @type String
24608      */
24609     afterPageText : "of {0}",
24610     /**
24611      * Customizable piece of the default paging text (defaults to "First Page")
24612      * @type String
24613      */
24614     firstText : "First Page",
24615     /**
24616      * Customizable piece of the default paging text (defaults to "Previous Page")
24617      * @type String
24618      */
24619     prevText : "Previous Page",
24620     /**
24621      * Customizable piece of the default paging text (defaults to "Next Page")
24622      * @type String
24623      */
24624     nextText : "Next Page",
24625     /**
24626      * Customizable piece of the default paging text (defaults to "Last Page")
24627      * @type String
24628      */
24629     lastText : "Last Page",
24630     /**
24631      * Customizable piece of the default paging text (defaults to "Refresh")
24632      * @type String
24633      */
24634     refreshText : "Refresh",
24635
24636     buttons : false,
24637     // private
24638     onRender : function(ct, position) 
24639     {
24640         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24641         this.navgroup.parentId = this.id;
24642         this.navgroup.onRender(this.el, null);
24643         // add the buttons to the navgroup
24644         
24645         if(this.displayInfo){
24646             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24647             this.displayEl = this.el.select('.x-paging-info', true).first();
24648 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24649 //            this.displayEl = navel.el.select('span',true).first();
24650         }
24651         
24652         var _this = this;
24653         
24654         if(this.buttons){
24655             Roo.each(_this.buttons, function(e){ // this might need to use render????
24656                Roo.factory(e).render(_this.el);
24657             });
24658         }
24659             
24660         Roo.each(_this.toolbarItems, function(e) {
24661             _this.navgroup.addItem(e);
24662         });
24663         
24664         
24665         this.first = this.navgroup.addItem({
24666             tooltip: this.firstText,
24667             cls: "prev",
24668             icon : 'fa fa-backward',
24669             disabled: true,
24670             preventDefault: true,
24671             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24672         });
24673         
24674         this.prev =  this.navgroup.addItem({
24675             tooltip: this.prevText,
24676             cls: "prev",
24677             icon : 'fa fa-step-backward',
24678             disabled: true,
24679             preventDefault: true,
24680             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24681         });
24682     //this.addSeparator();
24683         
24684         
24685         var field = this.navgroup.addItem( {
24686             tagtype : 'span',
24687             cls : 'x-paging-position',
24688             
24689             html : this.beforePageText  +
24690                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24691                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24692          } ); //?? escaped?
24693         
24694         this.field = field.el.select('input', true).first();
24695         this.field.on("keydown", this.onPagingKeydown, this);
24696         this.field.on("focus", function(){this.dom.select();});
24697     
24698     
24699         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24700         //this.field.setHeight(18);
24701         //this.addSeparator();
24702         this.next = this.navgroup.addItem({
24703             tooltip: this.nextText,
24704             cls: "next",
24705             html : ' <i class="fa fa-step-forward">',
24706             disabled: true,
24707             preventDefault: true,
24708             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24709         });
24710         this.last = this.navgroup.addItem({
24711             tooltip: this.lastText,
24712             icon : 'fa fa-forward',
24713             cls: "next",
24714             disabled: true,
24715             preventDefault: true,
24716             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24717         });
24718     //this.addSeparator();
24719         this.loading = this.navgroup.addItem({
24720             tooltip: this.refreshText,
24721             icon: 'fa fa-refresh',
24722             preventDefault: true,
24723             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24724         });
24725         
24726     },
24727
24728     // private
24729     updateInfo : function(){
24730         if(this.displayEl){
24731             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24732             var msg = count == 0 ?
24733                 this.emptyMsg :
24734                 String.format(
24735                     this.displayMsg,
24736                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24737                 );
24738             this.displayEl.update(msg);
24739         }
24740     },
24741
24742     // private
24743     onLoad : function(ds, r, o)
24744     {
24745         this.cursor = o.params.start ? o.params.start : 0;
24746         
24747         var d = this.getPageData(),
24748             ap = d.activePage,
24749             ps = d.pages;
24750         
24751         
24752         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24753         this.field.dom.value = ap;
24754         this.first.setDisabled(ap == 1);
24755         this.prev.setDisabled(ap == 1);
24756         this.next.setDisabled(ap == ps);
24757         this.last.setDisabled(ap == ps);
24758         this.loading.enable();
24759         this.updateInfo();
24760     },
24761
24762     // private
24763     getPageData : function(){
24764         var total = this.ds.getTotalCount();
24765         return {
24766             total : total,
24767             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24768             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24769         };
24770     },
24771
24772     // private
24773     onLoadError : function(){
24774         this.loading.enable();
24775     },
24776
24777     // private
24778     onPagingKeydown : function(e){
24779         var k = e.getKey();
24780         var d = this.getPageData();
24781         if(k == e.RETURN){
24782             var v = this.field.dom.value, pageNum;
24783             if(!v || isNaN(pageNum = parseInt(v, 10))){
24784                 this.field.dom.value = d.activePage;
24785                 return;
24786             }
24787             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24788             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24789             e.stopEvent();
24790         }
24791         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))
24792         {
24793           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24794           this.field.dom.value = pageNum;
24795           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24796           e.stopEvent();
24797         }
24798         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24799         {
24800           var v = this.field.dom.value, pageNum; 
24801           var increment = (e.shiftKey) ? 10 : 1;
24802           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24803                 increment *= -1;
24804           }
24805           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24806             this.field.dom.value = d.activePage;
24807             return;
24808           }
24809           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24810           {
24811             this.field.dom.value = parseInt(v, 10) + increment;
24812             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24813             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24814           }
24815           e.stopEvent();
24816         }
24817     },
24818
24819     // private
24820     beforeLoad : function(){
24821         if(this.loading){
24822             this.loading.disable();
24823         }
24824     },
24825
24826     // private
24827     onClick : function(which){
24828         
24829         var ds = this.ds;
24830         if (!ds) {
24831             return;
24832         }
24833         
24834         switch(which){
24835             case "first":
24836                 ds.load({params:{start: 0, limit: this.pageSize}});
24837             break;
24838             case "prev":
24839                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24840             break;
24841             case "next":
24842                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24843             break;
24844             case "last":
24845                 var total = ds.getTotalCount();
24846                 var extra = total % this.pageSize;
24847                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24848                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24849             break;
24850             case "refresh":
24851                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24852             break;
24853         }
24854     },
24855
24856     /**
24857      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24858      * @param {Roo.data.Store} store The data store to unbind
24859      */
24860     unbind : function(ds){
24861         ds.un("beforeload", this.beforeLoad, this);
24862         ds.un("load", this.onLoad, this);
24863         ds.un("loadexception", this.onLoadError, this);
24864         ds.un("remove", this.updateInfo, this);
24865         ds.un("add", this.updateInfo, this);
24866         this.ds = undefined;
24867     },
24868
24869     /**
24870      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24871      * @param {Roo.data.Store} store The data store to bind
24872      */
24873     bind : function(ds){
24874         ds.on("beforeload", this.beforeLoad, this);
24875         ds.on("load", this.onLoad, this);
24876         ds.on("loadexception", this.onLoadError, this);
24877         ds.on("remove", this.updateInfo, this);
24878         ds.on("add", this.updateInfo, this);
24879         this.ds = ds;
24880     }
24881 });/*
24882  * - LGPL
24883  *
24884  * element
24885  * 
24886  */
24887
24888 /**
24889  * @class Roo.bootstrap.MessageBar
24890  * @extends Roo.bootstrap.Component
24891  * Bootstrap MessageBar class
24892  * @cfg {String} html contents of the MessageBar
24893  * @cfg {String} weight (info | success | warning | danger) default info
24894  * @cfg {String} beforeClass insert the bar before the given class
24895  * @cfg {Boolean} closable (true | false) default false
24896  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24897  * 
24898  * @constructor
24899  * Create a new Element
24900  * @param {Object} config The config object
24901  */
24902
24903 Roo.bootstrap.MessageBar = function(config){
24904     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24905 };
24906
24907 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24908     
24909     html: '',
24910     weight: 'info',
24911     closable: false,
24912     fixed: false,
24913     beforeClass: 'bootstrap-sticky-wrap',
24914     
24915     getAutoCreate : function(){
24916         
24917         var cfg = {
24918             tag: 'div',
24919             cls: 'alert alert-dismissable alert-' + this.weight,
24920             cn: [
24921                 {
24922                     tag: 'span',
24923                     cls: 'message',
24924                     html: this.html || ''
24925                 }
24926             ]
24927         };
24928         
24929         if(this.fixed){
24930             cfg.cls += ' alert-messages-fixed';
24931         }
24932         
24933         if(this.closable){
24934             cfg.cn.push({
24935                 tag: 'button',
24936                 cls: 'close',
24937                 html: 'x'
24938             });
24939         }
24940         
24941         return cfg;
24942     },
24943     
24944     onRender : function(ct, position)
24945     {
24946         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24947         
24948         if(!this.el){
24949             var cfg = Roo.apply({},  this.getAutoCreate());
24950             cfg.id = Roo.id();
24951             
24952             if (this.cls) {
24953                 cfg.cls += ' ' + this.cls;
24954             }
24955             if (this.style) {
24956                 cfg.style = this.style;
24957             }
24958             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24959             
24960             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24961         }
24962         
24963         this.el.select('>button.close').on('click', this.hide, this);
24964         
24965     },
24966     
24967     show : function()
24968     {
24969         if (!this.rendered) {
24970             this.render();
24971         }
24972         
24973         this.el.show();
24974         
24975         this.fireEvent('show', this);
24976         
24977     },
24978     
24979     hide : function()
24980     {
24981         if (!this.rendered) {
24982             this.render();
24983         }
24984         
24985         this.el.hide();
24986         
24987         this.fireEvent('hide', this);
24988     },
24989     
24990     update : function()
24991     {
24992 //        var e = this.el.dom.firstChild;
24993 //        
24994 //        if(this.closable){
24995 //            e = e.nextSibling;
24996 //        }
24997 //        
24998 //        e.data = this.html || '';
24999
25000         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25001     }
25002    
25003 });
25004
25005  
25006
25007      /*
25008  * - LGPL
25009  *
25010  * Graph
25011  * 
25012  */
25013
25014
25015 /**
25016  * @class Roo.bootstrap.Graph
25017  * @extends Roo.bootstrap.Component
25018  * Bootstrap Graph class
25019 > Prameters
25020  -sm {number} sm 4
25021  -md {number} md 5
25022  @cfg {String} graphtype  bar | vbar | pie
25023  @cfg {number} g_x coodinator | centre x (pie)
25024  @cfg {number} g_y coodinator | centre y (pie)
25025  @cfg {number} g_r radius (pie)
25026  @cfg {number} g_height height of the chart (respected by all elements in the set)
25027  @cfg {number} g_width width of the chart (respected by all elements in the set)
25028  @cfg {Object} title The title of the chart
25029     
25030  -{Array}  values
25031  -opts (object) options for the chart 
25032      o {
25033      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25034      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25035      o vgutter (number)
25036      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.
25037      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25038      o to
25039      o stretch (boolean)
25040      o }
25041  -opts (object) options for the pie
25042      o{
25043      o cut
25044      o startAngle (number)
25045      o endAngle (number)
25046      } 
25047  *
25048  * @constructor
25049  * Create a new Input
25050  * @param {Object} config The config object
25051  */
25052
25053 Roo.bootstrap.Graph = function(config){
25054     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25055     
25056     this.addEvents({
25057         // img events
25058         /**
25059          * @event click
25060          * The img click event for the img.
25061          * @param {Roo.EventObject} e
25062          */
25063         "click" : true
25064     });
25065 };
25066
25067 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25068     
25069     sm: 4,
25070     md: 5,
25071     graphtype: 'bar',
25072     g_height: 250,
25073     g_width: 400,
25074     g_x: 50,
25075     g_y: 50,
25076     g_r: 30,
25077     opts:{
25078         //g_colors: this.colors,
25079         g_type: 'soft',
25080         g_gutter: '20%'
25081
25082     },
25083     title : false,
25084
25085     getAutoCreate : function(){
25086         
25087         var cfg = {
25088             tag: 'div',
25089             html : null
25090         };
25091         
25092         
25093         return  cfg;
25094     },
25095
25096     onRender : function(ct,position){
25097         
25098         
25099         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25100         
25101         if (typeof(Raphael) == 'undefined') {
25102             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25103             return;
25104         }
25105         
25106         this.raphael = Raphael(this.el.dom);
25107         
25108                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25109                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25110                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25111                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25112                 /*
25113                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25114                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25115                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25116                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25117                 
25118                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25119                 r.barchart(330, 10, 300, 220, data1);
25120                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25121                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25122                 */
25123                 
25124                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25125                 // r.barchart(30, 30, 560, 250,  xdata, {
25126                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25127                 //     axis : "0 0 1 1",
25128                 //     axisxlabels :  xdata
25129                 //     //yvalues : cols,
25130                    
25131                 // });
25132 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25133 //        
25134 //        this.load(null,xdata,{
25135 //                axis : "0 0 1 1",
25136 //                axisxlabels :  xdata
25137 //                });
25138
25139     },
25140
25141     load : function(graphtype,xdata,opts)
25142     {
25143         this.raphael.clear();
25144         if(!graphtype) {
25145             graphtype = this.graphtype;
25146         }
25147         if(!opts){
25148             opts = this.opts;
25149         }
25150         var r = this.raphael,
25151             fin = function () {
25152                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25153             },
25154             fout = function () {
25155                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25156             },
25157             pfin = function() {
25158                 this.sector.stop();
25159                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25160
25161                 if (this.label) {
25162                     this.label[0].stop();
25163                     this.label[0].attr({ r: 7.5 });
25164                     this.label[1].attr({ "font-weight": 800 });
25165                 }
25166             },
25167             pfout = function() {
25168                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25169
25170                 if (this.label) {
25171                     this.label[0].animate({ r: 5 }, 500, "bounce");
25172                     this.label[1].attr({ "font-weight": 400 });
25173                 }
25174             };
25175
25176         switch(graphtype){
25177             case 'bar':
25178                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25179                 break;
25180             case 'hbar':
25181                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25182                 break;
25183             case 'pie':
25184 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25185 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25186 //            
25187                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25188                 
25189                 break;
25190
25191         }
25192         
25193         if(this.title){
25194             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25195         }
25196         
25197     },
25198     
25199     setTitle: function(o)
25200     {
25201         this.title = o;
25202     },
25203     
25204     initEvents: function() {
25205         
25206         if(!this.href){
25207             this.el.on('click', this.onClick, this);
25208         }
25209     },
25210     
25211     onClick : function(e)
25212     {
25213         Roo.log('img onclick');
25214         this.fireEvent('click', this, e);
25215     }
25216    
25217 });
25218
25219  
25220 /*
25221  * - LGPL
25222  *
25223  * numberBox
25224  * 
25225  */
25226 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25227
25228 /**
25229  * @class Roo.bootstrap.dash.NumberBox
25230  * @extends Roo.bootstrap.Component
25231  * Bootstrap NumberBox class
25232  * @cfg {String} headline Box headline
25233  * @cfg {String} content Box content
25234  * @cfg {String} icon Box icon
25235  * @cfg {String} footer Footer text
25236  * @cfg {String} fhref Footer href
25237  * 
25238  * @constructor
25239  * Create a new NumberBox
25240  * @param {Object} config The config object
25241  */
25242
25243
25244 Roo.bootstrap.dash.NumberBox = function(config){
25245     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25246     
25247 };
25248
25249 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25250     
25251     headline : '',
25252     content : '',
25253     icon : '',
25254     footer : '',
25255     fhref : '',
25256     ficon : '',
25257     
25258     getAutoCreate : function(){
25259         
25260         var cfg = {
25261             tag : 'div',
25262             cls : 'small-box ',
25263             cn : [
25264                 {
25265                     tag : 'div',
25266                     cls : 'inner',
25267                     cn :[
25268                         {
25269                             tag : 'h3',
25270                             cls : 'roo-headline',
25271                             html : this.headline
25272                         },
25273                         {
25274                             tag : 'p',
25275                             cls : 'roo-content',
25276                             html : this.content
25277                         }
25278                     ]
25279                 }
25280             ]
25281         };
25282         
25283         if(this.icon){
25284             cfg.cn.push({
25285                 tag : 'div',
25286                 cls : 'icon',
25287                 cn :[
25288                     {
25289                         tag : 'i',
25290                         cls : 'ion ' + this.icon
25291                     }
25292                 ]
25293             });
25294         }
25295         
25296         if(this.footer){
25297             var footer = {
25298                 tag : 'a',
25299                 cls : 'small-box-footer',
25300                 href : this.fhref || '#',
25301                 html : this.footer
25302             };
25303             
25304             cfg.cn.push(footer);
25305             
25306         }
25307         
25308         return  cfg;
25309     },
25310
25311     onRender : function(ct,position){
25312         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25313
25314
25315        
25316                 
25317     },
25318
25319     setHeadline: function (value)
25320     {
25321         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25322     },
25323     
25324     setFooter: function (value, href)
25325     {
25326         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25327         
25328         if(href){
25329             this.el.select('a.small-box-footer',true).first().attr('href', href);
25330         }
25331         
25332     },
25333
25334     setContent: function (value)
25335     {
25336         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25337     },
25338
25339     initEvents: function() 
25340     {   
25341         
25342     }
25343     
25344 });
25345
25346  
25347 /*
25348  * - LGPL
25349  *
25350  * TabBox
25351  * 
25352  */
25353 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25354
25355 /**
25356  * @class Roo.bootstrap.dash.TabBox
25357  * @extends Roo.bootstrap.Component
25358  * Bootstrap TabBox class
25359  * @cfg {String} title Title of the TabBox
25360  * @cfg {String} icon Icon of the TabBox
25361  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25362  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25363  * 
25364  * @constructor
25365  * Create a new TabBox
25366  * @param {Object} config The config object
25367  */
25368
25369
25370 Roo.bootstrap.dash.TabBox = function(config){
25371     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25372     this.addEvents({
25373         // raw events
25374         /**
25375          * @event addpane
25376          * When a pane is added
25377          * @param {Roo.bootstrap.dash.TabPane} pane
25378          */
25379         "addpane" : true,
25380         /**
25381          * @event activatepane
25382          * When a pane is activated
25383          * @param {Roo.bootstrap.dash.TabPane} pane
25384          */
25385         "activatepane" : true
25386         
25387          
25388     });
25389     
25390     this.panes = [];
25391 };
25392
25393 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25394
25395     title : '',
25396     icon : false,
25397     showtabs : true,
25398     tabScrollable : false,
25399     
25400     getChildContainer : function()
25401     {
25402         return this.el.select('.tab-content', true).first();
25403     },
25404     
25405     getAutoCreate : function(){
25406         
25407         var header = {
25408             tag: 'li',
25409             cls: 'pull-left header',
25410             html: this.title,
25411             cn : []
25412         };
25413         
25414         if(this.icon){
25415             header.cn.push({
25416                 tag: 'i',
25417                 cls: 'fa ' + this.icon
25418             });
25419         }
25420         
25421         var h = {
25422             tag: 'ul',
25423             cls: 'nav nav-tabs pull-right',
25424             cn: [
25425                 header
25426             ]
25427         };
25428         
25429         if(this.tabScrollable){
25430             h = {
25431                 tag: 'div',
25432                 cls: 'tab-header',
25433                 cn: [
25434                     {
25435                         tag: 'ul',
25436                         cls: 'nav nav-tabs pull-right',
25437                         cn: [
25438                             header
25439                         ]
25440                     }
25441                 ]
25442             };
25443         }
25444         
25445         var cfg = {
25446             tag: 'div',
25447             cls: 'nav-tabs-custom',
25448             cn: [
25449                 h,
25450                 {
25451                     tag: 'div',
25452                     cls: 'tab-content no-padding',
25453                     cn: []
25454                 }
25455             ]
25456         };
25457
25458         return  cfg;
25459     },
25460     initEvents : function()
25461     {
25462         //Roo.log('add add pane handler');
25463         this.on('addpane', this.onAddPane, this);
25464     },
25465      /**
25466      * Updates the box title
25467      * @param {String} html to set the title to.
25468      */
25469     setTitle : function(value)
25470     {
25471         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25472     },
25473     onAddPane : function(pane)
25474     {
25475         this.panes.push(pane);
25476         //Roo.log('addpane');
25477         //Roo.log(pane);
25478         // tabs are rendere left to right..
25479         if(!this.showtabs){
25480             return;
25481         }
25482         
25483         var ctr = this.el.select('.nav-tabs', true).first();
25484          
25485          
25486         var existing = ctr.select('.nav-tab',true);
25487         var qty = existing.getCount();;
25488         
25489         
25490         var tab = ctr.createChild({
25491             tag : 'li',
25492             cls : 'nav-tab' + (qty ? '' : ' active'),
25493             cn : [
25494                 {
25495                     tag : 'a',
25496                     href:'#',
25497                     html : pane.title
25498                 }
25499             ]
25500         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25501         pane.tab = tab;
25502         
25503         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25504         if (!qty) {
25505             pane.el.addClass('active');
25506         }
25507         
25508                 
25509     },
25510     onTabClick : function(ev,un,ob,pane)
25511     {
25512         //Roo.log('tab - prev default');
25513         ev.preventDefault();
25514         
25515         
25516         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25517         pane.tab.addClass('active');
25518         //Roo.log(pane.title);
25519         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25520         // technically we should have a deactivate event.. but maybe add later.
25521         // and it should not de-activate the selected tab...
25522         this.fireEvent('activatepane', pane);
25523         pane.el.addClass('active');
25524         pane.fireEvent('activate');
25525         
25526         
25527     },
25528     
25529     getActivePane : function()
25530     {
25531         var r = false;
25532         Roo.each(this.panes, function(p) {
25533             if(p.el.hasClass('active')){
25534                 r = p;
25535                 return false;
25536             }
25537             
25538             return;
25539         });
25540         
25541         return r;
25542     }
25543     
25544     
25545 });
25546
25547  
25548 /*
25549  * - LGPL
25550  *
25551  * Tab pane
25552  * 
25553  */
25554 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25555 /**
25556  * @class Roo.bootstrap.TabPane
25557  * @extends Roo.bootstrap.Component
25558  * Bootstrap TabPane class
25559  * @cfg {Boolean} active (false | true) Default false
25560  * @cfg {String} title title of panel
25561
25562  * 
25563  * @constructor
25564  * Create a new TabPane
25565  * @param {Object} config The config object
25566  */
25567
25568 Roo.bootstrap.dash.TabPane = function(config){
25569     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25570     
25571     this.addEvents({
25572         // raw events
25573         /**
25574          * @event activate
25575          * When a pane is activated
25576          * @param {Roo.bootstrap.dash.TabPane} pane
25577          */
25578         "activate" : true
25579          
25580     });
25581 };
25582
25583 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25584     
25585     active : false,
25586     title : '',
25587     
25588     // the tabBox that this is attached to.
25589     tab : false,
25590      
25591     getAutoCreate : function() 
25592     {
25593         var cfg = {
25594             tag: 'div',
25595             cls: 'tab-pane'
25596         };
25597         
25598         if(this.active){
25599             cfg.cls += ' active';
25600         }
25601         
25602         return cfg;
25603     },
25604     initEvents  : function()
25605     {
25606         //Roo.log('trigger add pane handler');
25607         this.parent().fireEvent('addpane', this)
25608     },
25609     
25610      /**
25611      * Updates the tab title 
25612      * @param {String} html to set the title to.
25613      */
25614     setTitle: function(str)
25615     {
25616         if (!this.tab) {
25617             return;
25618         }
25619         this.title = str;
25620         this.tab.select('a', true).first().dom.innerHTML = str;
25621         
25622     }
25623     
25624     
25625     
25626 });
25627
25628  
25629
25630
25631  /*
25632  * - LGPL
25633  *
25634  * menu
25635  * 
25636  */
25637 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25638
25639 /**
25640  * @class Roo.bootstrap.menu.Menu
25641  * @extends Roo.bootstrap.Component
25642  * Bootstrap Menu class - container for Menu
25643  * @cfg {String} html Text of the menu
25644  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25645  * @cfg {String} icon Font awesome icon
25646  * @cfg {String} pos Menu align to (top | bottom) default bottom
25647  * 
25648  * 
25649  * @constructor
25650  * Create a new Menu
25651  * @param {Object} config The config object
25652  */
25653
25654
25655 Roo.bootstrap.menu.Menu = function(config){
25656     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25657     
25658     this.addEvents({
25659         /**
25660          * @event beforeshow
25661          * Fires before this menu is displayed
25662          * @param {Roo.bootstrap.menu.Menu} this
25663          */
25664         beforeshow : true,
25665         /**
25666          * @event beforehide
25667          * Fires before this menu is hidden
25668          * @param {Roo.bootstrap.menu.Menu} this
25669          */
25670         beforehide : true,
25671         /**
25672          * @event show
25673          * Fires after this menu is displayed
25674          * @param {Roo.bootstrap.menu.Menu} this
25675          */
25676         show : true,
25677         /**
25678          * @event hide
25679          * Fires after this menu is hidden
25680          * @param {Roo.bootstrap.menu.Menu} this
25681          */
25682         hide : true,
25683         /**
25684          * @event click
25685          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25686          * @param {Roo.bootstrap.menu.Menu} this
25687          * @param {Roo.EventObject} e
25688          */
25689         click : true
25690     });
25691     
25692 };
25693
25694 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25695     
25696     submenu : false,
25697     html : '',
25698     weight : 'default',
25699     icon : false,
25700     pos : 'bottom',
25701     
25702     
25703     getChildContainer : function() {
25704         if(this.isSubMenu){
25705             return this.el;
25706         }
25707         
25708         return this.el.select('ul.dropdown-menu', true).first();  
25709     },
25710     
25711     getAutoCreate : function()
25712     {
25713         var text = [
25714             {
25715                 tag : 'span',
25716                 cls : 'roo-menu-text',
25717                 html : this.html
25718             }
25719         ];
25720         
25721         if(this.icon){
25722             text.unshift({
25723                 tag : 'i',
25724                 cls : 'fa ' + this.icon
25725             })
25726         }
25727         
25728         
25729         var cfg = {
25730             tag : 'div',
25731             cls : 'btn-group',
25732             cn : [
25733                 {
25734                     tag : 'button',
25735                     cls : 'dropdown-button btn btn-' + this.weight,
25736                     cn : text
25737                 },
25738                 {
25739                     tag : 'button',
25740                     cls : 'dropdown-toggle btn btn-' + this.weight,
25741                     cn : [
25742                         {
25743                             tag : 'span',
25744                             cls : 'caret'
25745                         }
25746                     ]
25747                 },
25748                 {
25749                     tag : 'ul',
25750                     cls : 'dropdown-menu'
25751                 }
25752             ]
25753             
25754         };
25755         
25756         if(this.pos == 'top'){
25757             cfg.cls += ' dropup';
25758         }
25759         
25760         if(this.isSubMenu){
25761             cfg = {
25762                 tag : 'ul',
25763                 cls : 'dropdown-menu'
25764             }
25765         }
25766         
25767         return cfg;
25768     },
25769     
25770     onRender : function(ct, position)
25771     {
25772         this.isSubMenu = ct.hasClass('dropdown-submenu');
25773         
25774         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25775     },
25776     
25777     initEvents : function() 
25778     {
25779         if(this.isSubMenu){
25780             return;
25781         }
25782         
25783         this.hidden = true;
25784         
25785         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25786         this.triggerEl.on('click', this.onTriggerPress, this);
25787         
25788         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25789         this.buttonEl.on('click', this.onClick, this);
25790         
25791     },
25792     
25793     list : function()
25794     {
25795         if(this.isSubMenu){
25796             return this.el;
25797         }
25798         
25799         return this.el.select('ul.dropdown-menu', true).first();
25800     },
25801     
25802     onClick : function(e)
25803     {
25804         this.fireEvent("click", this, e);
25805     },
25806     
25807     onTriggerPress  : function(e)
25808     {   
25809         if (this.isVisible()) {
25810             this.hide();
25811         } else {
25812             this.show();
25813         }
25814     },
25815     
25816     isVisible : function(){
25817         return !this.hidden;
25818     },
25819     
25820     show : function()
25821     {
25822         this.fireEvent("beforeshow", this);
25823         
25824         this.hidden = false;
25825         this.el.addClass('open');
25826         
25827         Roo.get(document).on("mouseup", this.onMouseUp, this);
25828         
25829         this.fireEvent("show", this);
25830         
25831         
25832     },
25833     
25834     hide : function()
25835     {
25836         this.fireEvent("beforehide", this);
25837         
25838         this.hidden = true;
25839         this.el.removeClass('open');
25840         
25841         Roo.get(document).un("mouseup", this.onMouseUp);
25842         
25843         this.fireEvent("hide", this);
25844     },
25845     
25846     onMouseUp : function()
25847     {
25848         this.hide();
25849     }
25850     
25851 });
25852
25853  
25854  /*
25855  * - LGPL
25856  *
25857  * menu item
25858  * 
25859  */
25860 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25861
25862 /**
25863  * @class Roo.bootstrap.menu.Item
25864  * @extends Roo.bootstrap.Component
25865  * Bootstrap MenuItem class
25866  * @cfg {Boolean} submenu (true | false) default false
25867  * @cfg {String} html text of the item
25868  * @cfg {String} href the link
25869  * @cfg {Boolean} disable (true | false) default false
25870  * @cfg {Boolean} preventDefault (true | false) default true
25871  * @cfg {String} icon Font awesome icon
25872  * @cfg {String} pos Submenu align to (left | right) default right 
25873  * 
25874  * 
25875  * @constructor
25876  * Create a new Item
25877  * @param {Object} config The config object
25878  */
25879
25880
25881 Roo.bootstrap.menu.Item = function(config){
25882     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25883     this.addEvents({
25884         /**
25885          * @event mouseover
25886          * Fires when the mouse is hovering over this menu
25887          * @param {Roo.bootstrap.menu.Item} this
25888          * @param {Roo.EventObject} e
25889          */
25890         mouseover : true,
25891         /**
25892          * @event mouseout
25893          * Fires when the mouse exits this menu
25894          * @param {Roo.bootstrap.menu.Item} this
25895          * @param {Roo.EventObject} e
25896          */
25897         mouseout : true,
25898         // raw events
25899         /**
25900          * @event click
25901          * The raw click event for the entire grid.
25902          * @param {Roo.EventObject} e
25903          */
25904         click : true
25905     });
25906 };
25907
25908 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25909     
25910     submenu : false,
25911     href : '',
25912     html : '',
25913     preventDefault: true,
25914     disable : false,
25915     icon : false,
25916     pos : 'right',
25917     
25918     getAutoCreate : function()
25919     {
25920         var text = [
25921             {
25922                 tag : 'span',
25923                 cls : 'roo-menu-item-text',
25924                 html : this.html
25925             }
25926         ];
25927         
25928         if(this.icon){
25929             text.unshift({
25930                 tag : 'i',
25931                 cls : 'fa ' + this.icon
25932             })
25933         }
25934         
25935         var cfg = {
25936             tag : 'li',
25937             cn : [
25938                 {
25939                     tag : 'a',
25940                     href : this.href || '#',
25941                     cn : text
25942                 }
25943             ]
25944         };
25945         
25946         if(this.disable){
25947             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25948         }
25949         
25950         if(this.submenu){
25951             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25952             
25953             if(this.pos == 'left'){
25954                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25955             }
25956         }
25957         
25958         return cfg;
25959     },
25960     
25961     initEvents : function() 
25962     {
25963         this.el.on('mouseover', this.onMouseOver, this);
25964         this.el.on('mouseout', this.onMouseOut, this);
25965         
25966         this.el.select('a', true).first().on('click', this.onClick, this);
25967         
25968     },
25969     
25970     onClick : function(e)
25971     {
25972         if(this.preventDefault){
25973             e.preventDefault();
25974         }
25975         
25976         this.fireEvent("click", this, e);
25977     },
25978     
25979     onMouseOver : function(e)
25980     {
25981         if(this.submenu && this.pos == 'left'){
25982             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25983         }
25984         
25985         this.fireEvent("mouseover", this, e);
25986     },
25987     
25988     onMouseOut : function(e)
25989     {
25990         this.fireEvent("mouseout", this, e);
25991     }
25992 });
25993
25994  
25995
25996  /*
25997  * - LGPL
25998  *
25999  * menu separator
26000  * 
26001  */
26002 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26003
26004 /**
26005  * @class Roo.bootstrap.menu.Separator
26006  * @extends Roo.bootstrap.Component
26007  * Bootstrap Separator class
26008  * 
26009  * @constructor
26010  * Create a new Separator
26011  * @param {Object} config The config object
26012  */
26013
26014
26015 Roo.bootstrap.menu.Separator = function(config){
26016     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26017 };
26018
26019 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26020     
26021     getAutoCreate : function(){
26022         var cfg = {
26023             tag : 'li',
26024             cls: 'divider'
26025         };
26026         
26027         return cfg;
26028     }
26029    
26030 });
26031
26032  
26033
26034  /*
26035  * - LGPL
26036  *
26037  * Tooltip
26038  * 
26039  */
26040
26041 /**
26042  * @class Roo.bootstrap.Tooltip
26043  * Bootstrap Tooltip class
26044  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26045  * to determine which dom element triggers the tooltip.
26046  * 
26047  * It needs to add support for additional attributes like tooltip-position
26048  * 
26049  * @constructor
26050  * Create a new Toolti
26051  * @param {Object} config The config object
26052  */
26053
26054 Roo.bootstrap.Tooltip = function(config){
26055     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26056     
26057     this.alignment = Roo.bootstrap.Tooltip.alignment;
26058     
26059     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26060         this.alignment = config.alignment;
26061     }
26062     
26063 };
26064
26065 Roo.apply(Roo.bootstrap.Tooltip, {
26066     /**
26067      * @function init initialize tooltip monitoring.
26068      * @static
26069      */
26070     currentEl : false,
26071     currentTip : false,
26072     currentRegion : false,
26073     
26074     //  init : delay?
26075     
26076     init : function()
26077     {
26078         Roo.get(document).on('mouseover', this.enter ,this);
26079         Roo.get(document).on('mouseout', this.leave, this);
26080          
26081         
26082         this.currentTip = new Roo.bootstrap.Tooltip();
26083     },
26084     
26085     enter : function(ev)
26086     {
26087         var dom = ev.getTarget();
26088         
26089         //Roo.log(['enter',dom]);
26090         var el = Roo.fly(dom);
26091         if (this.currentEl) {
26092             //Roo.log(dom);
26093             //Roo.log(this.currentEl);
26094             //Roo.log(this.currentEl.contains(dom));
26095             if (this.currentEl == el) {
26096                 return;
26097             }
26098             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26099                 return;
26100             }
26101
26102         }
26103         
26104         if (this.currentTip.el) {
26105             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26106         }    
26107         //Roo.log(ev);
26108         
26109         if(!el || el.dom == document){
26110             return;
26111         }
26112         
26113         var bindEl = el;
26114         
26115         // you can not look for children, as if el is the body.. then everythign is the child..
26116         if (!el.attr('tooltip')) { //
26117             if (!el.select("[tooltip]").elements.length) {
26118                 return;
26119             }
26120             // is the mouse over this child...?
26121             bindEl = el.select("[tooltip]").first();
26122             var xy = ev.getXY();
26123             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26124                 //Roo.log("not in region.");
26125                 return;
26126             }
26127             //Roo.log("child element over..");
26128             
26129         }
26130         this.currentEl = bindEl;
26131         this.currentTip.bind(bindEl);
26132         this.currentRegion = Roo.lib.Region.getRegion(dom);
26133         this.currentTip.enter();
26134         
26135     },
26136     leave : function(ev)
26137     {
26138         var dom = ev.getTarget();
26139         //Roo.log(['leave',dom]);
26140         if (!this.currentEl) {
26141             return;
26142         }
26143         
26144         
26145         if (dom != this.currentEl.dom) {
26146             return;
26147         }
26148         var xy = ev.getXY();
26149         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26150             return;
26151         }
26152         // only activate leave if mouse cursor is outside... bounding box..
26153         
26154         
26155         
26156         
26157         if (this.currentTip) {
26158             this.currentTip.leave();
26159         }
26160         //Roo.log('clear currentEl');
26161         this.currentEl = false;
26162         
26163         
26164     },
26165     alignment : {
26166         'left' : ['r-l', [-2,0], 'right'],
26167         'right' : ['l-r', [2,0], 'left'],
26168         'bottom' : ['t-b', [0,2], 'top'],
26169         'top' : [ 'b-t', [0,-2], 'bottom']
26170     }
26171     
26172 });
26173
26174
26175 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26176     
26177     
26178     bindEl : false,
26179     
26180     delay : null, // can be { show : 300 , hide: 500}
26181     
26182     timeout : null,
26183     
26184     hoverState : null, //???
26185     
26186     placement : 'bottom', 
26187     
26188     alignment : false,
26189     
26190     getAutoCreate : function(){
26191     
26192         var cfg = {
26193            cls : 'tooltip',
26194            role : 'tooltip',
26195            cn : [
26196                 {
26197                     cls : 'tooltip-arrow'
26198                 },
26199                 {
26200                     cls : 'tooltip-inner'
26201                 }
26202            ]
26203         };
26204         
26205         return cfg;
26206     },
26207     bind : function(el)
26208     {
26209         this.bindEl = el;
26210     },
26211       
26212     
26213     enter : function () {
26214        
26215         if (this.timeout != null) {
26216             clearTimeout(this.timeout);
26217         }
26218         
26219         this.hoverState = 'in';
26220          //Roo.log("enter - show");
26221         if (!this.delay || !this.delay.show) {
26222             this.show();
26223             return;
26224         }
26225         var _t = this;
26226         this.timeout = setTimeout(function () {
26227             if (_t.hoverState == 'in') {
26228                 _t.show();
26229             }
26230         }, this.delay.show);
26231     },
26232     leave : function()
26233     {
26234         clearTimeout(this.timeout);
26235     
26236         this.hoverState = 'out';
26237          if (!this.delay || !this.delay.hide) {
26238             this.hide();
26239             return;
26240         }
26241        
26242         var _t = this;
26243         this.timeout = setTimeout(function () {
26244             //Roo.log("leave - timeout");
26245             
26246             if (_t.hoverState == 'out') {
26247                 _t.hide();
26248                 Roo.bootstrap.Tooltip.currentEl = false;
26249             }
26250         }, delay);
26251     },
26252     
26253     show : function (msg)
26254     {
26255         if (!this.el) {
26256             this.render(document.body);
26257         }
26258         // set content.
26259         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26260         
26261         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26262         
26263         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26264         
26265         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26266         
26267         var placement = typeof this.placement == 'function' ?
26268             this.placement.call(this, this.el, on_el) :
26269             this.placement;
26270             
26271         var autoToken = /\s?auto?\s?/i;
26272         var autoPlace = autoToken.test(placement);
26273         if (autoPlace) {
26274             placement = placement.replace(autoToken, '') || 'top';
26275         }
26276         
26277         //this.el.detach()
26278         //this.el.setXY([0,0]);
26279         this.el.show();
26280         //this.el.dom.style.display='block';
26281         
26282         //this.el.appendTo(on_el);
26283         
26284         var p = this.getPosition();
26285         var box = this.el.getBox();
26286         
26287         if (autoPlace) {
26288             // fixme..
26289         }
26290         
26291         var align = this.alignment[placement];
26292         
26293         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26294         
26295         if(placement == 'top' || placement == 'bottom'){
26296             if(xy[0] < 0){
26297                 placement = 'right';
26298             }
26299             
26300             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26301                 placement = 'left';
26302             }
26303             
26304             var scroll = Roo.select('body', true).first().getScroll();
26305             
26306             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26307                 placement = 'top';
26308             }
26309             
26310             align = this.alignment[placement];
26311         }
26312         
26313         this.el.alignTo(this.bindEl, align[0],align[1]);
26314         //var arrow = this.el.select('.arrow',true).first();
26315         //arrow.set(align[2], 
26316         
26317         this.el.addClass(placement);
26318         
26319         this.el.addClass('in fade');
26320         
26321         this.hoverState = null;
26322         
26323         if (this.el.hasClass('fade')) {
26324             // fade it?
26325         }
26326         
26327     },
26328     hide : function()
26329     {
26330          
26331         if (!this.el) {
26332             return;
26333         }
26334         //this.el.setXY([0,0]);
26335         this.el.removeClass('in');
26336         //this.el.hide();
26337         
26338     }
26339     
26340 });
26341  
26342
26343  /*
26344  * - LGPL
26345  *
26346  * Location Picker
26347  * 
26348  */
26349
26350 /**
26351  * @class Roo.bootstrap.LocationPicker
26352  * @extends Roo.bootstrap.Component
26353  * Bootstrap LocationPicker class
26354  * @cfg {Number} latitude Position when init default 0
26355  * @cfg {Number} longitude Position when init default 0
26356  * @cfg {Number} zoom default 15
26357  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26358  * @cfg {Boolean} mapTypeControl default false
26359  * @cfg {Boolean} disableDoubleClickZoom default false
26360  * @cfg {Boolean} scrollwheel default true
26361  * @cfg {Boolean} streetViewControl default false
26362  * @cfg {Number} radius default 0
26363  * @cfg {String} locationName
26364  * @cfg {Boolean} draggable default true
26365  * @cfg {Boolean} enableAutocomplete default false
26366  * @cfg {Boolean} enableReverseGeocode default true
26367  * @cfg {String} markerTitle
26368  * 
26369  * @constructor
26370  * Create a new LocationPicker
26371  * @param {Object} config The config object
26372  */
26373
26374
26375 Roo.bootstrap.LocationPicker = function(config){
26376     
26377     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26378     
26379     this.addEvents({
26380         /**
26381          * @event initial
26382          * Fires when the picker initialized.
26383          * @param {Roo.bootstrap.LocationPicker} this
26384          * @param {Google Location} location
26385          */
26386         initial : true,
26387         /**
26388          * @event positionchanged
26389          * Fires when the picker position changed.
26390          * @param {Roo.bootstrap.LocationPicker} this
26391          * @param {Google Location} location
26392          */
26393         positionchanged : true,
26394         /**
26395          * @event resize
26396          * Fires when the map resize.
26397          * @param {Roo.bootstrap.LocationPicker} this
26398          */
26399         resize : true,
26400         /**
26401          * @event show
26402          * Fires when the map show.
26403          * @param {Roo.bootstrap.LocationPicker} this
26404          */
26405         show : true,
26406         /**
26407          * @event hide
26408          * Fires when the map hide.
26409          * @param {Roo.bootstrap.LocationPicker} this
26410          */
26411         hide : true,
26412         /**
26413          * @event mapClick
26414          * Fires when click the map.
26415          * @param {Roo.bootstrap.LocationPicker} this
26416          * @param {Map event} e
26417          */
26418         mapClick : true,
26419         /**
26420          * @event mapRightClick
26421          * Fires when right click the map.
26422          * @param {Roo.bootstrap.LocationPicker} this
26423          * @param {Map event} e
26424          */
26425         mapRightClick : true,
26426         /**
26427          * @event markerClick
26428          * Fires when click the marker.
26429          * @param {Roo.bootstrap.LocationPicker} this
26430          * @param {Map event} e
26431          */
26432         markerClick : true,
26433         /**
26434          * @event markerRightClick
26435          * Fires when right click the marker.
26436          * @param {Roo.bootstrap.LocationPicker} this
26437          * @param {Map event} e
26438          */
26439         markerRightClick : true,
26440         /**
26441          * @event OverlayViewDraw
26442          * Fires when OverlayView Draw
26443          * @param {Roo.bootstrap.LocationPicker} this
26444          */
26445         OverlayViewDraw : true,
26446         /**
26447          * @event OverlayViewOnAdd
26448          * Fires when OverlayView Draw
26449          * @param {Roo.bootstrap.LocationPicker} this
26450          */
26451         OverlayViewOnAdd : true,
26452         /**
26453          * @event OverlayViewOnRemove
26454          * Fires when OverlayView Draw
26455          * @param {Roo.bootstrap.LocationPicker} this
26456          */
26457         OverlayViewOnRemove : true,
26458         /**
26459          * @event OverlayViewShow
26460          * Fires when OverlayView Draw
26461          * @param {Roo.bootstrap.LocationPicker} this
26462          * @param {Pixel} cpx
26463          */
26464         OverlayViewShow : true,
26465         /**
26466          * @event OverlayViewHide
26467          * Fires when OverlayView Draw
26468          * @param {Roo.bootstrap.LocationPicker} this
26469          */
26470         OverlayViewHide : true,
26471         /**
26472          * @event loadexception
26473          * Fires when load google lib failed.
26474          * @param {Roo.bootstrap.LocationPicker} this
26475          */
26476         loadexception : true
26477     });
26478         
26479 };
26480
26481 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26482     
26483     gMapContext: false,
26484     
26485     latitude: 0,
26486     longitude: 0,
26487     zoom: 15,
26488     mapTypeId: false,
26489     mapTypeControl: false,
26490     disableDoubleClickZoom: false,
26491     scrollwheel: true,
26492     streetViewControl: false,
26493     radius: 0,
26494     locationName: '',
26495     draggable: true,
26496     enableAutocomplete: false,
26497     enableReverseGeocode: true,
26498     markerTitle: '',
26499     
26500     getAutoCreate: function()
26501     {
26502
26503         var cfg = {
26504             tag: 'div',
26505             cls: 'roo-location-picker'
26506         };
26507         
26508         return cfg
26509     },
26510     
26511     initEvents: function(ct, position)
26512     {       
26513         if(!this.el.getWidth() || this.isApplied()){
26514             return;
26515         }
26516         
26517         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26518         
26519         this.initial();
26520     },
26521     
26522     initial: function()
26523     {
26524         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26525             this.fireEvent('loadexception', this);
26526             return;
26527         }
26528         
26529         if(!this.mapTypeId){
26530             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26531         }
26532         
26533         this.gMapContext = this.GMapContext();
26534         
26535         this.initOverlayView();
26536         
26537         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26538         
26539         var _this = this;
26540                 
26541         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26542             _this.setPosition(_this.gMapContext.marker.position);
26543         });
26544         
26545         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26546             _this.fireEvent('mapClick', this, event);
26547             
26548         });
26549
26550         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26551             _this.fireEvent('mapRightClick', this, event);
26552             
26553         });
26554         
26555         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26556             _this.fireEvent('markerClick', this, event);
26557             
26558         });
26559
26560         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26561             _this.fireEvent('markerRightClick', this, event);
26562             
26563         });
26564         
26565         this.setPosition(this.gMapContext.location);
26566         
26567         this.fireEvent('initial', this, this.gMapContext.location);
26568     },
26569     
26570     initOverlayView: function()
26571     {
26572         var _this = this;
26573         
26574         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26575             
26576             draw: function()
26577             {
26578                 _this.fireEvent('OverlayViewDraw', _this);
26579             },
26580             
26581             onAdd: function()
26582             {
26583                 _this.fireEvent('OverlayViewOnAdd', _this);
26584             },
26585             
26586             onRemove: function()
26587             {
26588                 _this.fireEvent('OverlayViewOnRemove', _this);
26589             },
26590             
26591             show: function(cpx)
26592             {
26593                 _this.fireEvent('OverlayViewShow', _this, cpx);
26594             },
26595             
26596             hide: function()
26597             {
26598                 _this.fireEvent('OverlayViewHide', _this);
26599             }
26600             
26601         });
26602     },
26603     
26604     fromLatLngToContainerPixel: function(event)
26605     {
26606         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26607     },
26608     
26609     isApplied: function() 
26610     {
26611         return this.getGmapContext() == false ? false : true;
26612     },
26613     
26614     getGmapContext: function() 
26615     {
26616         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26617     },
26618     
26619     GMapContext: function() 
26620     {
26621         var position = new google.maps.LatLng(this.latitude, this.longitude);
26622         
26623         var _map = new google.maps.Map(this.el.dom, {
26624             center: position,
26625             zoom: this.zoom,
26626             mapTypeId: this.mapTypeId,
26627             mapTypeControl: this.mapTypeControl,
26628             disableDoubleClickZoom: this.disableDoubleClickZoom,
26629             scrollwheel: this.scrollwheel,
26630             streetViewControl: this.streetViewControl,
26631             locationName: this.locationName,
26632             draggable: this.draggable,
26633             enableAutocomplete: this.enableAutocomplete,
26634             enableReverseGeocode: this.enableReverseGeocode
26635         });
26636         
26637         var _marker = new google.maps.Marker({
26638             position: position,
26639             map: _map,
26640             title: this.markerTitle,
26641             draggable: this.draggable
26642         });
26643         
26644         return {
26645             map: _map,
26646             marker: _marker,
26647             circle: null,
26648             location: position,
26649             radius: this.radius,
26650             locationName: this.locationName,
26651             addressComponents: {
26652                 formatted_address: null,
26653                 addressLine1: null,
26654                 addressLine2: null,
26655                 streetName: null,
26656                 streetNumber: null,
26657                 city: null,
26658                 district: null,
26659                 state: null,
26660                 stateOrProvince: null
26661             },
26662             settings: this,
26663             domContainer: this.el.dom,
26664             geodecoder: new google.maps.Geocoder()
26665         };
26666     },
26667     
26668     drawCircle: function(center, radius, options) 
26669     {
26670         if (this.gMapContext.circle != null) {
26671             this.gMapContext.circle.setMap(null);
26672         }
26673         if (radius > 0) {
26674             radius *= 1;
26675             options = Roo.apply({}, options, {
26676                 strokeColor: "#0000FF",
26677                 strokeOpacity: .35,
26678                 strokeWeight: 2,
26679                 fillColor: "#0000FF",
26680                 fillOpacity: .2
26681             });
26682             
26683             options.map = this.gMapContext.map;
26684             options.radius = radius;
26685             options.center = center;
26686             this.gMapContext.circle = new google.maps.Circle(options);
26687             return this.gMapContext.circle;
26688         }
26689         
26690         return null;
26691     },
26692     
26693     setPosition: function(location) 
26694     {
26695         this.gMapContext.location = location;
26696         this.gMapContext.marker.setPosition(location);
26697         this.gMapContext.map.panTo(location);
26698         this.drawCircle(location, this.gMapContext.radius, {});
26699         
26700         var _this = this;
26701         
26702         if (this.gMapContext.settings.enableReverseGeocode) {
26703             this.gMapContext.geodecoder.geocode({
26704                 latLng: this.gMapContext.location
26705             }, function(results, status) {
26706                 
26707                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26708                     _this.gMapContext.locationName = results[0].formatted_address;
26709                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26710                     
26711                     _this.fireEvent('positionchanged', this, location);
26712                 }
26713             });
26714             
26715             return;
26716         }
26717         
26718         this.fireEvent('positionchanged', this, location);
26719     },
26720     
26721     resize: function()
26722     {
26723         google.maps.event.trigger(this.gMapContext.map, "resize");
26724         
26725         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26726         
26727         this.fireEvent('resize', this);
26728     },
26729     
26730     setPositionByLatLng: function(latitude, longitude)
26731     {
26732         this.setPosition(new google.maps.LatLng(latitude, longitude));
26733     },
26734     
26735     getCurrentPosition: function() 
26736     {
26737         return {
26738             latitude: this.gMapContext.location.lat(),
26739             longitude: this.gMapContext.location.lng()
26740         };
26741     },
26742     
26743     getAddressName: function() 
26744     {
26745         return this.gMapContext.locationName;
26746     },
26747     
26748     getAddressComponents: function() 
26749     {
26750         return this.gMapContext.addressComponents;
26751     },
26752     
26753     address_component_from_google_geocode: function(address_components) 
26754     {
26755         var result = {};
26756         
26757         for (var i = 0; i < address_components.length; i++) {
26758             var component = address_components[i];
26759             if (component.types.indexOf("postal_code") >= 0) {
26760                 result.postalCode = component.short_name;
26761             } else if (component.types.indexOf("street_number") >= 0) {
26762                 result.streetNumber = component.short_name;
26763             } else if (component.types.indexOf("route") >= 0) {
26764                 result.streetName = component.short_name;
26765             } else if (component.types.indexOf("neighborhood") >= 0) {
26766                 result.city = component.short_name;
26767             } else if (component.types.indexOf("locality") >= 0) {
26768                 result.city = component.short_name;
26769             } else if (component.types.indexOf("sublocality") >= 0) {
26770                 result.district = component.short_name;
26771             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26772                 result.stateOrProvince = component.short_name;
26773             } else if (component.types.indexOf("country") >= 0) {
26774                 result.country = component.short_name;
26775             }
26776         }
26777         
26778         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26779         result.addressLine2 = "";
26780         return result;
26781     },
26782     
26783     setZoomLevel: function(zoom)
26784     {
26785         this.gMapContext.map.setZoom(zoom);
26786     },
26787     
26788     show: function()
26789     {
26790         if(!this.el){
26791             return;
26792         }
26793         
26794         this.el.show();
26795         
26796         this.resize();
26797         
26798         this.fireEvent('show', this);
26799     },
26800     
26801     hide: function()
26802     {
26803         if(!this.el){
26804             return;
26805         }
26806         
26807         this.el.hide();
26808         
26809         this.fireEvent('hide', this);
26810     }
26811     
26812 });
26813
26814 Roo.apply(Roo.bootstrap.LocationPicker, {
26815     
26816     OverlayView : function(map, options)
26817     {
26818         options = options || {};
26819         
26820         this.setMap(map);
26821     }
26822     
26823     
26824 });/*
26825  * - LGPL
26826  *
26827  * Alert
26828  * 
26829  */
26830
26831 /**
26832  * @class Roo.bootstrap.Alert
26833  * @extends Roo.bootstrap.Component
26834  * Bootstrap Alert class
26835  * @cfg {String} title The title of alert
26836  * @cfg {String} html The content of alert
26837  * @cfg {String} weight (  success | info | warning | danger )
26838  * @cfg {String} faicon font-awesomeicon
26839  * 
26840  * @constructor
26841  * Create a new alert
26842  * @param {Object} config The config object
26843  */
26844
26845
26846 Roo.bootstrap.Alert = function(config){
26847     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26848     
26849 };
26850
26851 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26852     
26853     title: '',
26854     html: '',
26855     weight: false,
26856     faicon: false,
26857     
26858     getAutoCreate : function()
26859     {
26860         
26861         var cfg = {
26862             tag : 'div',
26863             cls : 'alert',
26864             cn : [
26865                 {
26866                     tag : 'i',
26867                     cls : 'roo-alert-icon'
26868                     
26869                 },
26870                 {
26871                     tag : 'b',
26872                     cls : 'roo-alert-title',
26873                     html : this.title
26874                 },
26875                 {
26876                     tag : 'span',
26877                     cls : 'roo-alert-text',
26878                     html : this.html
26879                 }
26880             ]
26881         };
26882         
26883         if(this.faicon){
26884             cfg.cn[0].cls += ' fa ' + this.faicon;
26885         }
26886         
26887         if(this.weight){
26888             cfg.cls += ' alert-' + this.weight;
26889         }
26890         
26891         return cfg;
26892     },
26893     
26894     initEvents: function() 
26895     {
26896         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26897     },
26898     
26899     setTitle : function(str)
26900     {
26901         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26902     },
26903     
26904     setText : function(str)
26905     {
26906         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26907     },
26908     
26909     setWeight : function(weight)
26910     {
26911         if(this.weight){
26912             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26913         }
26914         
26915         this.weight = weight;
26916         
26917         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26918     },
26919     
26920     setIcon : function(icon)
26921     {
26922         if(this.faicon){
26923             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26924         }
26925         
26926         this.faicon = icon;
26927         
26928         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26929     },
26930     
26931     hide: function() 
26932     {
26933         this.el.hide();   
26934     },
26935     
26936     show: function() 
26937     {  
26938         this.el.show();   
26939     }
26940     
26941 });
26942
26943  
26944 /*
26945 * Licence: LGPL
26946 */
26947
26948 /**
26949  * @class Roo.bootstrap.UploadCropbox
26950  * @extends Roo.bootstrap.Component
26951  * Bootstrap UploadCropbox class
26952  * @cfg {String} emptyText show when image has been loaded
26953  * @cfg {String} rotateNotify show when image too small to rotate
26954  * @cfg {Number} errorTimeout default 3000
26955  * @cfg {Number} minWidth default 300
26956  * @cfg {Number} minHeight default 300
26957  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26958  * @cfg {Boolean} isDocument (true|false) default false
26959  * @cfg {String} url action url
26960  * @cfg {String} paramName default 'imageUpload'
26961  * @cfg {String} method default POST
26962  * @cfg {Boolean} loadMask (true|false) default true
26963  * @cfg {Boolean} loadingText default 'Loading...'
26964  * 
26965  * @constructor
26966  * Create a new UploadCropbox
26967  * @param {Object} config The config object
26968  */
26969
26970 Roo.bootstrap.UploadCropbox = function(config){
26971     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26972     
26973     this.addEvents({
26974         /**
26975          * @event beforeselectfile
26976          * Fire before select file
26977          * @param {Roo.bootstrap.UploadCropbox} this
26978          */
26979         "beforeselectfile" : true,
26980         /**
26981          * @event initial
26982          * Fire after initEvent
26983          * @param {Roo.bootstrap.UploadCropbox} this
26984          */
26985         "initial" : true,
26986         /**
26987          * @event crop
26988          * Fire after initEvent
26989          * @param {Roo.bootstrap.UploadCropbox} this
26990          * @param {String} data
26991          */
26992         "crop" : true,
26993         /**
26994          * @event prepare
26995          * Fire when preparing the file data
26996          * @param {Roo.bootstrap.UploadCropbox} this
26997          * @param {Object} file
26998          */
26999         "prepare" : true,
27000         /**
27001          * @event exception
27002          * Fire when get exception
27003          * @param {Roo.bootstrap.UploadCropbox} this
27004          * @param {XMLHttpRequest} xhr
27005          */
27006         "exception" : true,
27007         /**
27008          * @event beforeloadcanvas
27009          * Fire before load the canvas
27010          * @param {Roo.bootstrap.UploadCropbox} this
27011          * @param {String} src
27012          */
27013         "beforeloadcanvas" : true,
27014         /**
27015          * @event trash
27016          * Fire when trash image
27017          * @param {Roo.bootstrap.UploadCropbox} this
27018          */
27019         "trash" : true,
27020         /**
27021          * @event download
27022          * Fire when download the image
27023          * @param {Roo.bootstrap.UploadCropbox} this
27024          */
27025         "download" : true,
27026         /**
27027          * @event footerbuttonclick
27028          * Fire when footerbuttonclick
27029          * @param {Roo.bootstrap.UploadCropbox} this
27030          * @param {String} type
27031          */
27032         "footerbuttonclick" : true,
27033         /**
27034          * @event resize
27035          * Fire when resize
27036          * @param {Roo.bootstrap.UploadCropbox} this
27037          */
27038         "resize" : true,
27039         /**
27040          * @event rotate
27041          * Fire when rotate the image
27042          * @param {Roo.bootstrap.UploadCropbox} this
27043          * @param {String} pos
27044          */
27045         "rotate" : true,
27046         /**
27047          * @event inspect
27048          * Fire when inspect the file
27049          * @param {Roo.bootstrap.UploadCropbox} this
27050          * @param {Object} file
27051          */
27052         "inspect" : true,
27053         /**
27054          * @event upload
27055          * Fire when xhr upload the file
27056          * @param {Roo.bootstrap.UploadCropbox} this
27057          * @param {Object} data
27058          */
27059         "upload" : true,
27060         /**
27061          * @event arrange
27062          * Fire when arrange the file data
27063          * @param {Roo.bootstrap.UploadCropbox} this
27064          * @param {Object} formData
27065          */
27066         "arrange" : true
27067     });
27068     
27069     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27070 };
27071
27072 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27073     
27074     emptyText : 'Click to upload image',
27075     rotateNotify : 'Image is too small to rotate',
27076     errorTimeout : 3000,
27077     scale : 0,
27078     baseScale : 1,
27079     rotate : 0,
27080     dragable : false,
27081     pinching : false,
27082     mouseX : 0,
27083     mouseY : 0,
27084     cropData : false,
27085     minWidth : 300,
27086     minHeight : 300,
27087     file : false,
27088     exif : {},
27089     baseRotate : 1,
27090     cropType : 'image/jpeg',
27091     buttons : false,
27092     canvasLoaded : false,
27093     isDocument : false,
27094     method : 'POST',
27095     paramName : 'imageUpload',
27096     loadMask : true,
27097     loadingText : 'Loading...',
27098     maskEl : false,
27099     
27100     getAutoCreate : function()
27101     {
27102         var cfg = {
27103             tag : 'div',
27104             cls : 'roo-upload-cropbox',
27105             cn : [
27106                 {
27107                     tag : 'input',
27108                     cls : 'roo-upload-cropbox-selector',
27109                     type : 'file'
27110                 },
27111                 {
27112                     tag : 'div',
27113                     cls : 'roo-upload-cropbox-body',
27114                     style : 'cursor:pointer',
27115                     cn : [
27116                         {
27117                             tag : 'div',
27118                             cls : 'roo-upload-cropbox-preview'
27119                         },
27120                         {
27121                             tag : 'div',
27122                             cls : 'roo-upload-cropbox-thumb'
27123                         },
27124                         {
27125                             tag : 'div',
27126                             cls : 'roo-upload-cropbox-empty-notify',
27127                             html : this.emptyText
27128                         },
27129                         {
27130                             tag : 'div',
27131                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27132                             html : this.rotateNotify
27133                         }
27134                     ]
27135                 },
27136                 {
27137                     tag : 'div',
27138                     cls : 'roo-upload-cropbox-footer',
27139                     cn : {
27140                         tag : 'div',
27141                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27142                         cn : []
27143                     }
27144                 }
27145             ]
27146         };
27147         
27148         return cfg;
27149     },
27150     
27151     onRender : function(ct, position)
27152     {
27153         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27154         
27155         if (this.buttons.length) {
27156             
27157             Roo.each(this.buttons, function(bb) {
27158                 
27159                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27160                 
27161                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27162                 
27163             }, this);
27164         }
27165         
27166         if(this.loadMask){
27167             this.maskEl = this.el;
27168         }
27169     },
27170     
27171     initEvents : function()
27172     {
27173         this.urlAPI = (window.createObjectURL && window) || 
27174                                 (window.URL && URL.revokeObjectURL && URL) || 
27175                                 (window.webkitURL && webkitURL);
27176                         
27177         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27178         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27179         
27180         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27181         this.selectorEl.hide();
27182         
27183         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27184         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27185         
27186         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27187         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27188         this.thumbEl.hide();
27189         
27190         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27191         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27192         
27193         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27194         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27195         this.errorEl.hide();
27196         
27197         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27198         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27199         this.footerEl.hide();
27200         
27201         this.setThumbBoxSize();
27202         
27203         this.bind();
27204         
27205         this.resize();
27206         
27207         this.fireEvent('initial', this);
27208     },
27209
27210     bind : function()
27211     {
27212         var _this = this;
27213         
27214         window.addEventListener("resize", function() { _this.resize(); } );
27215         
27216         this.bodyEl.on('click', this.beforeSelectFile, this);
27217         
27218         if(Roo.isTouch){
27219             this.bodyEl.on('touchstart', this.onTouchStart, this);
27220             this.bodyEl.on('touchmove', this.onTouchMove, this);
27221             this.bodyEl.on('touchend', this.onTouchEnd, this);
27222         }
27223         
27224         if(!Roo.isTouch){
27225             this.bodyEl.on('mousedown', this.onMouseDown, this);
27226             this.bodyEl.on('mousemove', this.onMouseMove, this);
27227             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27228             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27229             Roo.get(document).on('mouseup', this.onMouseUp, this);
27230         }
27231         
27232         this.selectorEl.on('change', this.onFileSelected, this);
27233     },
27234     
27235     reset : function()
27236     {    
27237         this.scale = 0;
27238         this.baseScale = 1;
27239         this.rotate = 0;
27240         this.baseRotate = 1;
27241         this.dragable = false;
27242         this.pinching = false;
27243         this.mouseX = 0;
27244         this.mouseY = 0;
27245         this.cropData = false;
27246         this.notifyEl.dom.innerHTML = this.emptyText;
27247         
27248         this.selectorEl.dom.value = '';
27249         
27250     },
27251     
27252     resize : function()
27253     {
27254         if(this.fireEvent('resize', this) != false){
27255             this.setThumbBoxPosition();
27256             this.setCanvasPosition();
27257         }
27258     },
27259     
27260     onFooterButtonClick : function(e, el, o, type)
27261     {
27262         switch (type) {
27263             case 'rotate-left' :
27264                 this.onRotateLeft(e);
27265                 break;
27266             case 'rotate-right' :
27267                 this.onRotateRight(e);
27268                 break;
27269             case 'picture' :
27270                 this.beforeSelectFile(e);
27271                 break;
27272             case 'trash' :
27273                 this.trash(e);
27274                 break;
27275             case 'crop' :
27276                 this.crop(e);
27277                 break;
27278             case 'download' :
27279                 this.download(e);
27280                 break;
27281             default :
27282                 break;
27283         }
27284         
27285         this.fireEvent('footerbuttonclick', this, type);
27286     },
27287     
27288     beforeSelectFile : function(e)
27289     {
27290         e.preventDefault();
27291         
27292         if(this.fireEvent('beforeselectfile', this) != false){
27293             this.selectorEl.dom.click();
27294         }
27295     },
27296     
27297     onFileSelected : function(e)
27298     {
27299         e.preventDefault();
27300         
27301         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27302             return;
27303         }
27304         
27305         var file = this.selectorEl.dom.files[0];
27306         
27307         if(this.fireEvent('inspect', this, file) != false){
27308             this.prepare(file);
27309         }
27310         
27311     },
27312     
27313     trash : function(e)
27314     {
27315         this.fireEvent('trash', this);
27316     },
27317     
27318     download : function(e)
27319     {
27320         this.fireEvent('download', this);
27321     },
27322     
27323     loadCanvas : function(src)
27324     {   
27325         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27326             
27327             this.reset();
27328             
27329             this.imageEl = document.createElement('img');
27330             
27331             var _this = this;
27332             
27333             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27334             
27335             this.imageEl.src = src;
27336         }
27337     },
27338     
27339     onLoadCanvas : function()
27340     {   
27341         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27342         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27343         
27344         this.bodyEl.un('click', this.beforeSelectFile, this);
27345         
27346         this.notifyEl.hide();
27347         this.thumbEl.show();
27348         this.footerEl.show();
27349         
27350         this.baseRotateLevel();
27351         
27352         if(this.isDocument){
27353             this.setThumbBoxSize();
27354         }
27355         
27356         this.setThumbBoxPosition();
27357         
27358         this.baseScaleLevel();
27359         
27360         this.draw();
27361         
27362         this.resize();
27363         
27364         this.canvasLoaded = true;
27365         
27366         if(this.loadMask){
27367             this.maskEl.unmask();
27368         }
27369         
27370     },
27371     
27372     setCanvasPosition : function()
27373     {   
27374         if(!this.canvasEl){
27375             return;
27376         }
27377         
27378         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27379         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27380         
27381         this.previewEl.setLeft(pw);
27382         this.previewEl.setTop(ph);
27383         
27384     },
27385     
27386     onMouseDown : function(e)
27387     {   
27388         e.stopEvent();
27389         
27390         this.dragable = true;
27391         this.pinching = false;
27392         
27393         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27394             this.dragable = false;
27395             return;
27396         }
27397         
27398         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27399         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27400         
27401     },
27402     
27403     onMouseMove : function(e)
27404     {   
27405         e.stopEvent();
27406         
27407         if(!this.canvasLoaded){
27408             return;
27409         }
27410         
27411         if (!this.dragable){
27412             return;
27413         }
27414         
27415         var minX = Math.ceil(this.thumbEl.getLeft(true));
27416         var minY = Math.ceil(this.thumbEl.getTop(true));
27417         
27418         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27419         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27420         
27421         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27422         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27423         
27424         x = x - this.mouseX;
27425         y = y - this.mouseY;
27426         
27427         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27428         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27429         
27430         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27431         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27432         
27433         this.previewEl.setLeft(bgX);
27434         this.previewEl.setTop(bgY);
27435         
27436         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27437         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27438     },
27439     
27440     onMouseUp : function(e)
27441     {   
27442         e.stopEvent();
27443         
27444         this.dragable = false;
27445     },
27446     
27447     onMouseWheel : function(e)
27448     {   
27449         e.stopEvent();
27450         
27451         this.startScale = this.scale;
27452         
27453         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27454         
27455         if(!this.zoomable()){
27456             this.scale = this.startScale;
27457             return;
27458         }
27459         
27460         this.draw();
27461         
27462         return;
27463     },
27464     
27465     zoomable : function()
27466     {
27467         var minScale = this.thumbEl.getWidth() / this.minWidth;
27468         
27469         if(this.minWidth < this.minHeight){
27470             minScale = this.thumbEl.getHeight() / this.minHeight;
27471         }
27472         
27473         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27474         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27475         
27476         if(
27477                 this.isDocument &&
27478                 (this.rotate == 0 || this.rotate == 180) && 
27479                 (
27480                     width > this.imageEl.OriginWidth || 
27481                     height > this.imageEl.OriginHeight ||
27482                     (width < this.minWidth && height < this.minHeight)
27483                 )
27484         ){
27485             return false;
27486         }
27487         
27488         if(
27489                 this.isDocument &&
27490                 (this.rotate == 90 || this.rotate == 270) && 
27491                 (
27492                     width > this.imageEl.OriginWidth || 
27493                     height > this.imageEl.OriginHeight ||
27494                     (width < this.minHeight && height < this.minWidth)
27495                 )
27496         ){
27497             return false;
27498         }
27499         
27500         if(
27501                 !this.isDocument &&
27502                 (this.rotate == 0 || this.rotate == 180) && 
27503                 (
27504                     width < this.minWidth || 
27505                     width > this.imageEl.OriginWidth || 
27506                     height < this.minHeight || 
27507                     height > this.imageEl.OriginHeight
27508                 )
27509         ){
27510             return false;
27511         }
27512         
27513         if(
27514                 !this.isDocument &&
27515                 (this.rotate == 90 || this.rotate == 270) && 
27516                 (
27517                     width < this.minHeight || 
27518                     width > this.imageEl.OriginWidth || 
27519                     height < this.minWidth || 
27520                     height > this.imageEl.OriginHeight
27521                 )
27522         ){
27523             return false;
27524         }
27525         
27526         return true;
27527         
27528     },
27529     
27530     onRotateLeft : function(e)
27531     {   
27532         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27533             
27534             var minScale = this.thumbEl.getWidth() / this.minWidth;
27535             
27536             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27537             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27538             
27539             this.startScale = this.scale;
27540             
27541             while (this.getScaleLevel() < minScale){
27542             
27543                 this.scale = this.scale + 1;
27544                 
27545                 if(!this.zoomable()){
27546                     break;
27547                 }
27548                 
27549                 if(
27550                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27551                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27552                 ){
27553                     continue;
27554                 }
27555                 
27556                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27557
27558                 this.draw();
27559                 
27560                 return;
27561             }
27562             
27563             this.scale = this.startScale;
27564             
27565             this.onRotateFail();
27566             
27567             return false;
27568         }
27569         
27570         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27571
27572         if(this.isDocument){
27573             this.setThumbBoxSize();
27574             this.setThumbBoxPosition();
27575             this.setCanvasPosition();
27576         }
27577         
27578         this.draw();
27579         
27580         this.fireEvent('rotate', this, 'left');
27581         
27582     },
27583     
27584     onRotateRight : function(e)
27585     {
27586         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27587             
27588             var minScale = this.thumbEl.getWidth() / this.minWidth;
27589         
27590             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27591             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27592             
27593             this.startScale = this.scale;
27594             
27595             while (this.getScaleLevel() < minScale){
27596             
27597                 this.scale = this.scale + 1;
27598                 
27599                 if(!this.zoomable()){
27600                     break;
27601                 }
27602                 
27603                 if(
27604                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27605                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27606                 ){
27607                     continue;
27608                 }
27609                 
27610                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27611
27612                 this.draw();
27613                 
27614                 return;
27615             }
27616             
27617             this.scale = this.startScale;
27618             
27619             this.onRotateFail();
27620             
27621             return false;
27622         }
27623         
27624         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27625
27626         if(this.isDocument){
27627             this.setThumbBoxSize();
27628             this.setThumbBoxPosition();
27629             this.setCanvasPosition();
27630         }
27631         
27632         this.draw();
27633         
27634         this.fireEvent('rotate', this, 'right');
27635     },
27636     
27637     onRotateFail : function()
27638     {
27639         this.errorEl.show(true);
27640         
27641         var _this = this;
27642         
27643         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27644     },
27645     
27646     draw : function()
27647     {
27648         this.previewEl.dom.innerHTML = '';
27649         
27650         var canvasEl = document.createElement("canvas");
27651         
27652         var contextEl = canvasEl.getContext("2d");
27653         
27654         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27655         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27656         var center = this.imageEl.OriginWidth / 2;
27657         
27658         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27659             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27660             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27661             center = this.imageEl.OriginHeight / 2;
27662         }
27663         
27664         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27665         
27666         contextEl.translate(center, center);
27667         contextEl.rotate(this.rotate * Math.PI / 180);
27668
27669         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27670         
27671         this.canvasEl = document.createElement("canvas");
27672         
27673         this.contextEl = this.canvasEl.getContext("2d");
27674         
27675         switch (this.rotate) {
27676             case 0 :
27677                 
27678                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27679                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27680                 
27681                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27682                 
27683                 break;
27684             case 90 : 
27685                 
27686                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27687                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27688                 
27689                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27690                     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);
27691                     break;
27692                 }
27693                 
27694                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27695                 
27696                 break;
27697             case 180 :
27698                 
27699                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27700                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27701                 
27702                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27703                     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);
27704                     break;
27705                 }
27706                 
27707                 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);
27708                 
27709                 break;
27710             case 270 :
27711                 
27712                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27713                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27714         
27715                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27716                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27717                     break;
27718                 }
27719                 
27720                 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);
27721                 
27722                 break;
27723             default : 
27724                 break;
27725         }
27726         
27727         this.previewEl.appendChild(this.canvasEl);
27728         
27729         this.setCanvasPosition();
27730     },
27731     
27732     crop : function()
27733     {
27734         if(!this.canvasLoaded){
27735             return;
27736         }
27737         
27738         var imageCanvas = document.createElement("canvas");
27739         
27740         var imageContext = imageCanvas.getContext("2d");
27741         
27742         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27743         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27744         
27745         var center = imageCanvas.width / 2;
27746         
27747         imageContext.translate(center, center);
27748         
27749         imageContext.rotate(this.rotate * Math.PI / 180);
27750         
27751         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27752         
27753         var canvas = document.createElement("canvas");
27754         
27755         var context = canvas.getContext("2d");
27756                 
27757         canvas.width = this.minWidth;
27758         canvas.height = this.minHeight;
27759
27760         switch (this.rotate) {
27761             case 0 :
27762                 
27763                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27764                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27765                 
27766                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27767                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27768                 
27769                 var targetWidth = this.minWidth - 2 * x;
27770                 var targetHeight = this.minHeight - 2 * y;
27771                 
27772                 var scale = 1;
27773                 
27774                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27775                     scale = targetWidth / width;
27776                 }
27777                 
27778                 if(x > 0 && y == 0){
27779                     scale = targetHeight / height;
27780                 }
27781                 
27782                 if(x > 0 && y > 0){
27783                     scale = targetWidth / width;
27784                     
27785                     if(width < height){
27786                         scale = targetHeight / height;
27787                     }
27788                 }
27789                 
27790                 context.scale(scale, scale);
27791                 
27792                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27793                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27794
27795                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27796                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27797
27798                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27799                 
27800                 break;
27801             case 90 : 
27802                 
27803                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27804                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27805                 
27806                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27807                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27808                 
27809                 var targetWidth = this.minWidth - 2 * x;
27810                 var targetHeight = this.minHeight - 2 * y;
27811                 
27812                 var scale = 1;
27813                 
27814                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27815                     scale = targetWidth / width;
27816                 }
27817                 
27818                 if(x > 0 && y == 0){
27819                     scale = targetHeight / height;
27820                 }
27821                 
27822                 if(x > 0 && y > 0){
27823                     scale = targetWidth / width;
27824                     
27825                     if(width < height){
27826                         scale = targetHeight / height;
27827                     }
27828                 }
27829                 
27830                 context.scale(scale, scale);
27831                 
27832                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27833                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27834
27835                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27836                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27837                 
27838                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27839                 
27840                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27841                 
27842                 break;
27843             case 180 :
27844                 
27845                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27846                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27847                 
27848                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27849                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27850                 
27851                 var targetWidth = this.minWidth - 2 * x;
27852                 var targetHeight = this.minHeight - 2 * y;
27853                 
27854                 var scale = 1;
27855                 
27856                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27857                     scale = targetWidth / width;
27858                 }
27859                 
27860                 if(x > 0 && y == 0){
27861                     scale = targetHeight / height;
27862                 }
27863                 
27864                 if(x > 0 && y > 0){
27865                     scale = targetWidth / width;
27866                     
27867                     if(width < height){
27868                         scale = targetHeight / height;
27869                     }
27870                 }
27871                 
27872                 context.scale(scale, scale);
27873                 
27874                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27875                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27876
27877                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27878                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27879
27880                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27881                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27882                 
27883                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27884                 
27885                 break;
27886             case 270 :
27887                 
27888                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27889                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27890                 
27891                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27892                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27893                 
27894                 var targetWidth = this.minWidth - 2 * x;
27895                 var targetHeight = this.minHeight - 2 * y;
27896                 
27897                 var scale = 1;
27898                 
27899                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27900                     scale = targetWidth / width;
27901                 }
27902                 
27903                 if(x > 0 && y == 0){
27904                     scale = targetHeight / height;
27905                 }
27906                 
27907                 if(x > 0 && y > 0){
27908                     scale = targetWidth / width;
27909                     
27910                     if(width < height){
27911                         scale = targetHeight / height;
27912                     }
27913                 }
27914                 
27915                 context.scale(scale, scale);
27916                 
27917                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27918                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27919
27920                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27921                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27922                 
27923                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27924                 
27925                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27926                 
27927                 break;
27928             default : 
27929                 break;
27930         }
27931         
27932         this.cropData = canvas.toDataURL(this.cropType);
27933         
27934         if(this.fireEvent('crop', this, this.cropData) !== false){
27935             this.process(this.file, this.cropData);
27936         }
27937         
27938         return;
27939         
27940     },
27941     
27942     setThumbBoxSize : function()
27943     {
27944         var width, height;
27945         
27946         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27947             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27948             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27949             
27950             this.minWidth = width;
27951             this.minHeight = height;
27952             
27953             if(this.rotate == 90 || this.rotate == 270){
27954                 this.minWidth = height;
27955                 this.minHeight = width;
27956             }
27957         }
27958         
27959         height = 300;
27960         width = Math.ceil(this.minWidth * height / this.minHeight);
27961         
27962         if(this.minWidth > this.minHeight){
27963             width = 300;
27964             height = Math.ceil(this.minHeight * width / this.minWidth);
27965         }
27966         
27967         this.thumbEl.setStyle({
27968             width : width + 'px',
27969             height : height + 'px'
27970         });
27971
27972         return;
27973             
27974     },
27975     
27976     setThumbBoxPosition : function()
27977     {
27978         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27979         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27980         
27981         this.thumbEl.setLeft(x);
27982         this.thumbEl.setTop(y);
27983         
27984     },
27985     
27986     baseRotateLevel : function()
27987     {
27988         this.baseRotate = 1;
27989         
27990         if(
27991                 typeof(this.exif) != 'undefined' &&
27992                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27993                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27994         ){
27995             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27996         }
27997         
27998         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27999         
28000     },
28001     
28002     baseScaleLevel : function()
28003     {
28004         var width, height;
28005         
28006         if(this.isDocument){
28007             
28008             if(this.baseRotate == 6 || this.baseRotate == 8){
28009             
28010                 height = this.thumbEl.getHeight();
28011                 this.baseScale = height / this.imageEl.OriginWidth;
28012
28013                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28014                     width = this.thumbEl.getWidth();
28015                     this.baseScale = width / this.imageEl.OriginHeight;
28016                 }
28017
28018                 return;
28019             }
28020
28021             height = this.thumbEl.getHeight();
28022             this.baseScale = height / this.imageEl.OriginHeight;
28023
28024             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28025                 width = this.thumbEl.getWidth();
28026                 this.baseScale = width / this.imageEl.OriginWidth;
28027             }
28028
28029             return;
28030         }
28031         
28032         if(this.baseRotate == 6 || this.baseRotate == 8){
28033             
28034             width = this.thumbEl.getHeight();
28035             this.baseScale = width / this.imageEl.OriginHeight;
28036             
28037             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28038                 height = this.thumbEl.getWidth();
28039                 this.baseScale = height / this.imageEl.OriginHeight;
28040             }
28041             
28042             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28043                 height = this.thumbEl.getWidth();
28044                 this.baseScale = height / this.imageEl.OriginHeight;
28045                 
28046                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28047                     width = this.thumbEl.getHeight();
28048                     this.baseScale = width / this.imageEl.OriginWidth;
28049                 }
28050             }
28051             
28052             return;
28053         }
28054         
28055         width = this.thumbEl.getWidth();
28056         this.baseScale = width / this.imageEl.OriginWidth;
28057         
28058         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28059             height = this.thumbEl.getHeight();
28060             this.baseScale = height / this.imageEl.OriginHeight;
28061         }
28062         
28063         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28064             
28065             height = this.thumbEl.getHeight();
28066             this.baseScale = height / this.imageEl.OriginHeight;
28067             
28068             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28069                 width = this.thumbEl.getWidth();
28070                 this.baseScale = width / this.imageEl.OriginWidth;
28071             }
28072             
28073         }
28074         
28075         return;
28076     },
28077     
28078     getScaleLevel : function()
28079     {
28080         return this.baseScale * Math.pow(1.1, this.scale);
28081     },
28082     
28083     onTouchStart : function(e)
28084     {
28085         if(!this.canvasLoaded){
28086             this.beforeSelectFile(e);
28087             return;
28088         }
28089         
28090         var touches = e.browserEvent.touches;
28091         
28092         if(!touches){
28093             return;
28094         }
28095         
28096         if(touches.length == 1){
28097             this.onMouseDown(e);
28098             return;
28099         }
28100         
28101         if(touches.length != 2){
28102             return;
28103         }
28104         
28105         var coords = [];
28106         
28107         for(var i = 0, finger; finger = touches[i]; i++){
28108             coords.push(finger.pageX, finger.pageY);
28109         }
28110         
28111         var x = Math.pow(coords[0] - coords[2], 2);
28112         var y = Math.pow(coords[1] - coords[3], 2);
28113         
28114         this.startDistance = Math.sqrt(x + y);
28115         
28116         this.startScale = this.scale;
28117         
28118         this.pinching = true;
28119         this.dragable = false;
28120         
28121     },
28122     
28123     onTouchMove : function(e)
28124     {
28125         if(!this.pinching && !this.dragable){
28126             return;
28127         }
28128         
28129         var touches = e.browserEvent.touches;
28130         
28131         if(!touches){
28132             return;
28133         }
28134         
28135         if(this.dragable){
28136             this.onMouseMove(e);
28137             return;
28138         }
28139         
28140         var coords = [];
28141         
28142         for(var i = 0, finger; finger = touches[i]; i++){
28143             coords.push(finger.pageX, finger.pageY);
28144         }
28145         
28146         var x = Math.pow(coords[0] - coords[2], 2);
28147         var y = Math.pow(coords[1] - coords[3], 2);
28148         
28149         this.endDistance = Math.sqrt(x + y);
28150         
28151         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28152         
28153         if(!this.zoomable()){
28154             this.scale = this.startScale;
28155             return;
28156         }
28157         
28158         this.draw();
28159         
28160     },
28161     
28162     onTouchEnd : function(e)
28163     {
28164         this.pinching = false;
28165         this.dragable = false;
28166         
28167     },
28168     
28169     process : function(file, crop)
28170     {
28171         if(this.loadMask){
28172             this.maskEl.mask(this.loadingText);
28173         }
28174         
28175         this.xhr = new XMLHttpRequest();
28176         
28177         file.xhr = this.xhr;
28178
28179         this.xhr.open(this.method, this.url, true);
28180         
28181         var headers = {
28182             "Accept": "application/json",
28183             "Cache-Control": "no-cache",
28184             "X-Requested-With": "XMLHttpRequest"
28185         };
28186         
28187         for (var headerName in headers) {
28188             var headerValue = headers[headerName];
28189             if (headerValue) {
28190                 this.xhr.setRequestHeader(headerName, headerValue);
28191             }
28192         }
28193         
28194         var _this = this;
28195         
28196         this.xhr.onload = function()
28197         {
28198             _this.xhrOnLoad(_this.xhr);
28199         }
28200         
28201         this.xhr.onerror = function()
28202         {
28203             _this.xhrOnError(_this.xhr);
28204         }
28205         
28206         var formData = new FormData();
28207
28208         formData.append('returnHTML', 'NO');
28209         
28210         if(crop){
28211             formData.append('crop', crop);
28212         }
28213         
28214         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28215             formData.append(this.paramName, file, file.name);
28216         }
28217         
28218         if(typeof(file.filename) != 'undefined'){
28219             formData.append('filename', file.filename);
28220         }
28221         
28222         if(typeof(file.mimetype) != 'undefined'){
28223             formData.append('mimetype', file.mimetype);
28224         }
28225         
28226         if(this.fireEvent('arrange', this, formData) != false){
28227             this.xhr.send(formData);
28228         };
28229     },
28230     
28231     xhrOnLoad : function(xhr)
28232     {
28233         if(this.loadMask){
28234             this.maskEl.unmask();
28235         }
28236         
28237         if (xhr.readyState !== 4) {
28238             this.fireEvent('exception', this, xhr);
28239             return;
28240         }
28241
28242         var response = Roo.decode(xhr.responseText);
28243         
28244         if(!response.success){
28245             this.fireEvent('exception', this, xhr);
28246             return;
28247         }
28248         
28249         var response = Roo.decode(xhr.responseText);
28250         
28251         this.fireEvent('upload', this, response);
28252         
28253     },
28254     
28255     xhrOnError : function()
28256     {
28257         if(this.loadMask){
28258             this.maskEl.unmask();
28259         }
28260         
28261         Roo.log('xhr on error');
28262         
28263         var response = Roo.decode(xhr.responseText);
28264           
28265         Roo.log(response);
28266         
28267     },
28268     
28269     prepare : function(file)
28270     {   
28271         if(this.loadMask){
28272             this.maskEl.mask(this.loadingText);
28273         }
28274         
28275         this.file = false;
28276         this.exif = {};
28277         
28278         if(typeof(file) === 'string'){
28279             this.loadCanvas(file);
28280             return;
28281         }
28282         
28283         if(!file || !this.urlAPI){
28284             return;
28285         }
28286         
28287         this.file = file;
28288         this.cropType = file.type;
28289         
28290         var _this = this;
28291         
28292         if(this.fireEvent('prepare', this, this.file) != false){
28293             
28294             var reader = new FileReader();
28295             
28296             reader.onload = function (e) {
28297                 if (e.target.error) {
28298                     Roo.log(e.target.error);
28299                     return;
28300                 }
28301                 
28302                 var buffer = e.target.result,
28303                     dataView = new DataView(buffer),
28304                     offset = 2,
28305                     maxOffset = dataView.byteLength - 4,
28306                     markerBytes,
28307                     markerLength;
28308                 
28309                 if (dataView.getUint16(0) === 0xffd8) {
28310                     while (offset < maxOffset) {
28311                         markerBytes = dataView.getUint16(offset);
28312                         
28313                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28314                             markerLength = dataView.getUint16(offset + 2) + 2;
28315                             if (offset + markerLength > dataView.byteLength) {
28316                                 Roo.log('Invalid meta data: Invalid segment size.');
28317                                 break;
28318                             }
28319                             
28320                             if(markerBytes == 0xffe1){
28321                                 _this.parseExifData(
28322                                     dataView,
28323                                     offset,
28324                                     markerLength
28325                                 );
28326                             }
28327                             
28328                             offset += markerLength;
28329                             
28330                             continue;
28331                         }
28332                         
28333                         break;
28334                     }
28335                     
28336                 }
28337                 
28338                 var url = _this.urlAPI.createObjectURL(_this.file);
28339                 
28340                 _this.loadCanvas(url);
28341                 
28342                 return;
28343             }
28344             
28345             reader.readAsArrayBuffer(this.file);
28346             
28347         }
28348         
28349     },
28350     
28351     parseExifData : function(dataView, offset, length)
28352     {
28353         var tiffOffset = offset + 10,
28354             littleEndian,
28355             dirOffset;
28356     
28357         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28358             // No Exif data, might be XMP data instead
28359             return;
28360         }
28361         
28362         // Check for the ASCII code for "Exif" (0x45786966):
28363         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28364             // No Exif data, might be XMP data instead
28365             return;
28366         }
28367         if (tiffOffset + 8 > dataView.byteLength) {
28368             Roo.log('Invalid Exif data: Invalid segment size.');
28369             return;
28370         }
28371         // Check for the two null bytes:
28372         if (dataView.getUint16(offset + 8) !== 0x0000) {
28373             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28374             return;
28375         }
28376         // Check the byte alignment:
28377         switch (dataView.getUint16(tiffOffset)) {
28378         case 0x4949:
28379             littleEndian = true;
28380             break;
28381         case 0x4D4D:
28382             littleEndian = false;
28383             break;
28384         default:
28385             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28386             return;
28387         }
28388         // Check for the TIFF tag marker (0x002A):
28389         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28390             Roo.log('Invalid Exif data: Missing TIFF marker.');
28391             return;
28392         }
28393         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28394         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28395         
28396         this.parseExifTags(
28397             dataView,
28398             tiffOffset,
28399             tiffOffset + dirOffset,
28400             littleEndian
28401         );
28402     },
28403     
28404     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28405     {
28406         var tagsNumber,
28407             dirEndOffset,
28408             i;
28409         if (dirOffset + 6 > dataView.byteLength) {
28410             Roo.log('Invalid Exif data: Invalid directory offset.');
28411             return;
28412         }
28413         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28414         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28415         if (dirEndOffset + 4 > dataView.byteLength) {
28416             Roo.log('Invalid Exif data: Invalid directory size.');
28417             return;
28418         }
28419         for (i = 0; i < tagsNumber; i += 1) {
28420             this.parseExifTag(
28421                 dataView,
28422                 tiffOffset,
28423                 dirOffset + 2 + 12 * i, // tag offset
28424                 littleEndian
28425             );
28426         }
28427         // Return the offset to the next directory:
28428         return dataView.getUint32(dirEndOffset, littleEndian);
28429     },
28430     
28431     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28432     {
28433         var tag = dataView.getUint16(offset, littleEndian);
28434         
28435         this.exif[tag] = this.getExifValue(
28436             dataView,
28437             tiffOffset,
28438             offset,
28439             dataView.getUint16(offset + 2, littleEndian), // tag type
28440             dataView.getUint32(offset + 4, littleEndian), // tag length
28441             littleEndian
28442         );
28443     },
28444     
28445     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28446     {
28447         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28448             tagSize,
28449             dataOffset,
28450             values,
28451             i,
28452             str,
28453             c;
28454     
28455         if (!tagType) {
28456             Roo.log('Invalid Exif data: Invalid tag type.');
28457             return;
28458         }
28459         
28460         tagSize = tagType.size * length;
28461         // Determine if the value is contained in the dataOffset bytes,
28462         // or if the value at the dataOffset is a pointer to the actual data:
28463         dataOffset = tagSize > 4 ?
28464                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28465         if (dataOffset + tagSize > dataView.byteLength) {
28466             Roo.log('Invalid Exif data: Invalid data offset.');
28467             return;
28468         }
28469         if (length === 1) {
28470             return tagType.getValue(dataView, dataOffset, littleEndian);
28471         }
28472         values = [];
28473         for (i = 0; i < length; i += 1) {
28474             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28475         }
28476         
28477         if (tagType.ascii) {
28478             str = '';
28479             // Concatenate the chars:
28480             for (i = 0; i < values.length; i += 1) {
28481                 c = values[i];
28482                 // Ignore the terminating NULL byte(s):
28483                 if (c === '\u0000') {
28484                     break;
28485                 }
28486                 str += c;
28487             }
28488             return str;
28489         }
28490         return values;
28491     }
28492     
28493 });
28494
28495 Roo.apply(Roo.bootstrap.UploadCropbox, {
28496     tags : {
28497         'Orientation': 0x0112
28498     },
28499     
28500     Orientation: {
28501             1: 0, //'top-left',
28502 //            2: 'top-right',
28503             3: 180, //'bottom-right',
28504 //            4: 'bottom-left',
28505 //            5: 'left-top',
28506             6: 90, //'right-top',
28507 //            7: 'right-bottom',
28508             8: 270 //'left-bottom'
28509     },
28510     
28511     exifTagTypes : {
28512         // byte, 8-bit unsigned int:
28513         1: {
28514             getValue: function (dataView, dataOffset) {
28515                 return dataView.getUint8(dataOffset);
28516             },
28517             size: 1
28518         },
28519         // ascii, 8-bit byte:
28520         2: {
28521             getValue: function (dataView, dataOffset) {
28522                 return String.fromCharCode(dataView.getUint8(dataOffset));
28523             },
28524             size: 1,
28525             ascii: true
28526         },
28527         // short, 16 bit int:
28528         3: {
28529             getValue: function (dataView, dataOffset, littleEndian) {
28530                 return dataView.getUint16(dataOffset, littleEndian);
28531             },
28532             size: 2
28533         },
28534         // long, 32 bit int:
28535         4: {
28536             getValue: function (dataView, dataOffset, littleEndian) {
28537                 return dataView.getUint32(dataOffset, littleEndian);
28538             },
28539             size: 4
28540         },
28541         // rational = two long values, first is numerator, second is denominator:
28542         5: {
28543             getValue: function (dataView, dataOffset, littleEndian) {
28544                 return dataView.getUint32(dataOffset, littleEndian) /
28545                     dataView.getUint32(dataOffset + 4, littleEndian);
28546             },
28547             size: 8
28548         },
28549         // slong, 32 bit signed int:
28550         9: {
28551             getValue: function (dataView, dataOffset, littleEndian) {
28552                 return dataView.getInt32(dataOffset, littleEndian);
28553             },
28554             size: 4
28555         },
28556         // srational, two slongs, first is numerator, second is denominator:
28557         10: {
28558             getValue: function (dataView, dataOffset, littleEndian) {
28559                 return dataView.getInt32(dataOffset, littleEndian) /
28560                     dataView.getInt32(dataOffset + 4, littleEndian);
28561             },
28562             size: 8
28563         }
28564     },
28565     
28566     footer : {
28567         STANDARD : [
28568             {
28569                 tag : 'div',
28570                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28571                 action : 'rotate-left',
28572                 cn : [
28573                     {
28574                         tag : 'button',
28575                         cls : 'btn btn-default',
28576                         html : '<i class="fa fa-undo"></i>'
28577                     }
28578                 ]
28579             },
28580             {
28581                 tag : 'div',
28582                 cls : 'btn-group roo-upload-cropbox-picture',
28583                 action : 'picture',
28584                 cn : [
28585                     {
28586                         tag : 'button',
28587                         cls : 'btn btn-default',
28588                         html : '<i class="fa fa-picture-o"></i>'
28589                     }
28590                 ]
28591             },
28592             {
28593                 tag : 'div',
28594                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28595                 action : 'rotate-right',
28596                 cn : [
28597                     {
28598                         tag : 'button',
28599                         cls : 'btn btn-default',
28600                         html : '<i class="fa fa-repeat"></i>'
28601                     }
28602                 ]
28603             }
28604         ],
28605         DOCUMENT : [
28606             {
28607                 tag : 'div',
28608                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28609                 action : 'rotate-left',
28610                 cn : [
28611                     {
28612                         tag : 'button',
28613                         cls : 'btn btn-default',
28614                         html : '<i class="fa fa-undo"></i>'
28615                     }
28616                 ]
28617             },
28618             {
28619                 tag : 'div',
28620                 cls : 'btn-group roo-upload-cropbox-download',
28621                 action : 'download',
28622                 cn : [
28623                     {
28624                         tag : 'button',
28625                         cls : 'btn btn-default',
28626                         html : '<i class="fa fa-download"></i>'
28627                     }
28628                 ]
28629             },
28630             {
28631                 tag : 'div',
28632                 cls : 'btn-group roo-upload-cropbox-crop',
28633                 action : 'crop',
28634                 cn : [
28635                     {
28636                         tag : 'button',
28637                         cls : 'btn btn-default',
28638                         html : '<i class="fa fa-crop"></i>'
28639                     }
28640                 ]
28641             },
28642             {
28643                 tag : 'div',
28644                 cls : 'btn-group roo-upload-cropbox-trash',
28645                 action : 'trash',
28646                 cn : [
28647                     {
28648                         tag : 'button',
28649                         cls : 'btn btn-default',
28650                         html : '<i class="fa fa-trash"></i>'
28651                     }
28652                 ]
28653             },
28654             {
28655                 tag : 'div',
28656                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28657                 action : 'rotate-right',
28658                 cn : [
28659                     {
28660                         tag : 'button',
28661                         cls : 'btn btn-default',
28662                         html : '<i class="fa fa-repeat"></i>'
28663                     }
28664                 ]
28665             }
28666         ],
28667         ROTATOR : [
28668             {
28669                 tag : 'div',
28670                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28671                 action : 'rotate-left',
28672                 cn : [
28673                     {
28674                         tag : 'button',
28675                         cls : 'btn btn-default',
28676                         html : '<i class="fa fa-undo"></i>'
28677                     }
28678                 ]
28679             },
28680             {
28681                 tag : 'div',
28682                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28683                 action : 'rotate-right',
28684                 cn : [
28685                     {
28686                         tag : 'button',
28687                         cls : 'btn btn-default',
28688                         html : '<i class="fa fa-repeat"></i>'
28689                     }
28690                 ]
28691             }
28692         ]
28693     }
28694 });
28695
28696 /*
28697 * Licence: LGPL
28698 */
28699
28700 /**
28701  * @class Roo.bootstrap.DocumentManager
28702  * @extends Roo.bootstrap.Component
28703  * Bootstrap DocumentManager class
28704  * @cfg {String} paramName default 'imageUpload'
28705  * @cfg {String} toolTipName default 'filename'
28706  * @cfg {String} method default POST
28707  * @cfg {String} url action url
28708  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28709  * @cfg {Boolean} multiple multiple upload default true
28710  * @cfg {Number} thumbSize default 300
28711  * @cfg {String} fieldLabel
28712  * @cfg {Number} labelWidth default 4
28713  * @cfg {String} labelAlign (left|top) default left
28714  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28715 * @cfg {Number} labellg set the width of label (1-12)
28716  * @cfg {Number} labelmd set the width of label (1-12)
28717  * @cfg {Number} labelsm set the width of label (1-12)
28718  * @cfg {Number} labelxs set the width of label (1-12)
28719  * 
28720  * @constructor
28721  * Create a new DocumentManager
28722  * @param {Object} config The config object
28723  */
28724
28725 Roo.bootstrap.DocumentManager = function(config){
28726     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28727     
28728     this.files = [];
28729     this.delegates = [];
28730     
28731     this.addEvents({
28732         /**
28733          * @event initial
28734          * Fire when initial the DocumentManager
28735          * @param {Roo.bootstrap.DocumentManager} this
28736          */
28737         "initial" : true,
28738         /**
28739          * @event inspect
28740          * inspect selected file
28741          * @param {Roo.bootstrap.DocumentManager} this
28742          * @param {File} file
28743          */
28744         "inspect" : true,
28745         /**
28746          * @event exception
28747          * Fire when xhr load exception
28748          * @param {Roo.bootstrap.DocumentManager} this
28749          * @param {XMLHttpRequest} xhr
28750          */
28751         "exception" : true,
28752         /**
28753          * @event afterupload
28754          * Fire when xhr load exception
28755          * @param {Roo.bootstrap.DocumentManager} this
28756          * @param {XMLHttpRequest} xhr
28757          */
28758         "afterupload" : true,
28759         /**
28760          * @event prepare
28761          * prepare the form data
28762          * @param {Roo.bootstrap.DocumentManager} this
28763          * @param {Object} formData
28764          */
28765         "prepare" : true,
28766         /**
28767          * @event remove
28768          * Fire when remove the file
28769          * @param {Roo.bootstrap.DocumentManager} this
28770          * @param {Object} file
28771          */
28772         "remove" : true,
28773         /**
28774          * @event refresh
28775          * Fire after refresh the file
28776          * @param {Roo.bootstrap.DocumentManager} this
28777          */
28778         "refresh" : true,
28779         /**
28780          * @event click
28781          * Fire after click the image
28782          * @param {Roo.bootstrap.DocumentManager} this
28783          * @param {Object} file
28784          */
28785         "click" : true,
28786         /**
28787          * @event edit
28788          * Fire when upload a image and editable set to true
28789          * @param {Roo.bootstrap.DocumentManager} this
28790          * @param {Object} file
28791          */
28792         "edit" : true,
28793         /**
28794          * @event beforeselectfile
28795          * Fire before select file
28796          * @param {Roo.bootstrap.DocumentManager} this
28797          */
28798         "beforeselectfile" : true,
28799         /**
28800          * @event process
28801          * Fire before process file
28802          * @param {Roo.bootstrap.DocumentManager} this
28803          * @param {Object} file
28804          */
28805         "process" : true,
28806         /**
28807          * @event previewrendered
28808          * Fire when preview rendered
28809          * @param {Roo.bootstrap.DocumentManager} this
28810          * @param {Object} file
28811          */
28812         "previewrendered" : true,
28813         /**
28814          */
28815         "previewResize" : true
28816         
28817     });
28818 };
28819
28820 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28821     
28822     boxes : 0,
28823     inputName : '',
28824     thumbSize : 300,
28825     multiple : true,
28826     files : false,
28827     method : 'POST',
28828     url : '',
28829     paramName : 'imageUpload',
28830     toolTipName : 'filename',
28831     fieldLabel : '',
28832     labelWidth : 4,
28833     labelAlign : 'left',
28834     editable : true,
28835     delegates : false,
28836     xhr : false, 
28837     
28838     labellg : 0,
28839     labelmd : 0,
28840     labelsm : 0,
28841     labelxs : 0,
28842     
28843     getAutoCreate : function()
28844     {   
28845         var managerWidget = {
28846             tag : 'div',
28847             cls : 'roo-document-manager',
28848             cn : [
28849                 {
28850                     tag : 'input',
28851                     cls : 'roo-document-manager-selector',
28852                     type : 'file'
28853                 },
28854                 {
28855                     tag : 'div',
28856                     cls : 'roo-document-manager-uploader',
28857                     cn : [
28858                         {
28859                             tag : 'div',
28860                             cls : 'roo-document-manager-upload-btn',
28861                             html : '<i class="fa fa-plus"></i>'
28862                         }
28863                     ]
28864                     
28865                 }
28866             ]
28867         };
28868         
28869         var content = [
28870             {
28871                 tag : 'div',
28872                 cls : 'column col-md-12',
28873                 cn : managerWidget
28874             }
28875         ];
28876         
28877         if(this.fieldLabel.length){
28878             
28879             content = [
28880                 {
28881                     tag : 'div',
28882                     cls : 'column col-md-12',
28883                     html : this.fieldLabel
28884                 },
28885                 {
28886                     tag : 'div',
28887                     cls : 'column col-md-12',
28888                     cn : managerWidget
28889                 }
28890             ];
28891
28892             if(this.labelAlign == 'left'){
28893                 content = [
28894                     {
28895                         tag : 'div',
28896                         cls : 'column',
28897                         html : this.fieldLabel
28898                     },
28899                     {
28900                         tag : 'div',
28901                         cls : 'column',
28902                         cn : managerWidget
28903                     }
28904                 ];
28905                 
28906                 if(this.labelWidth > 12){
28907                     content[0].style = "width: " + this.labelWidth + 'px';
28908                 }
28909
28910                 if(this.labelWidth < 13 && this.labelmd == 0){
28911                     this.labelmd = this.labelWidth;
28912                 }
28913
28914                 if(this.labellg > 0){
28915                     content[0].cls += ' col-lg-' + this.labellg;
28916                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28917                 }
28918
28919                 if(this.labelmd > 0){
28920                     content[0].cls += ' col-md-' + this.labelmd;
28921                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28922                 }
28923
28924                 if(this.labelsm > 0){
28925                     content[0].cls += ' col-sm-' + this.labelsm;
28926                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28927                 }
28928
28929                 if(this.labelxs > 0){
28930                     content[0].cls += ' col-xs-' + this.labelxs;
28931                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28932                 }
28933                 
28934             }
28935         }
28936         
28937         var cfg = {
28938             tag : 'div',
28939             cls : 'row clearfix',
28940             cn : content
28941         };
28942         
28943         return cfg;
28944         
28945     },
28946     
28947     initEvents : function()
28948     {
28949         this.managerEl = this.el.select('.roo-document-manager', true).first();
28950         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28951         
28952         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28953         this.selectorEl.hide();
28954         
28955         if(this.multiple){
28956             this.selectorEl.attr('multiple', 'multiple');
28957         }
28958         
28959         this.selectorEl.on('change', this.onFileSelected, this);
28960         
28961         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28962         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28963         
28964         this.uploader.on('click', this.onUploaderClick, this);
28965         
28966         this.renderProgressDialog();
28967         
28968         var _this = this;
28969         
28970         window.addEventListener("resize", function() { _this.refresh(); } );
28971         
28972         this.fireEvent('initial', this);
28973     },
28974     
28975     renderProgressDialog : function()
28976     {
28977         var _this = this;
28978         
28979         this.progressDialog = new Roo.bootstrap.Modal({
28980             cls : 'roo-document-manager-progress-dialog',
28981             allow_close : false,
28982             title : '',
28983             buttons : [
28984                 {
28985                     name  :'cancel',
28986                     weight : 'danger',
28987                     html : 'Cancel'
28988                 }
28989             ], 
28990             listeners : { 
28991                 btnclick : function() {
28992                     _this.uploadCancel();
28993                     this.hide();
28994                 }
28995             }
28996         });
28997          
28998         this.progressDialog.render(Roo.get(document.body));
28999          
29000         this.progress = new Roo.bootstrap.Progress({
29001             cls : 'roo-document-manager-progress',
29002             active : true,
29003             striped : true
29004         });
29005         
29006         this.progress.render(this.progressDialog.getChildContainer());
29007         
29008         this.progressBar = new Roo.bootstrap.ProgressBar({
29009             cls : 'roo-document-manager-progress-bar',
29010             aria_valuenow : 0,
29011             aria_valuemin : 0,
29012             aria_valuemax : 12,
29013             panel : 'success'
29014         });
29015         
29016         this.progressBar.render(this.progress.getChildContainer());
29017     },
29018     
29019     onUploaderClick : function(e)
29020     {
29021         e.preventDefault();
29022      
29023         if(this.fireEvent('beforeselectfile', this) != false){
29024             this.selectorEl.dom.click();
29025         }
29026         
29027     },
29028     
29029     onFileSelected : function(e)
29030     {
29031         e.preventDefault();
29032         
29033         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29034             return;
29035         }
29036         
29037         Roo.each(this.selectorEl.dom.files, function(file){
29038             if(this.fireEvent('inspect', this, file) != false){
29039                 this.files.push(file);
29040             }
29041         }, this);
29042         
29043         this.queue();
29044         
29045     },
29046     
29047     queue : function()
29048     {
29049         this.selectorEl.dom.value = '';
29050         
29051         if(!this.files || !this.files.length){
29052             return;
29053         }
29054         
29055         if(this.boxes > 0 && this.files.length > this.boxes){
29056             this.files = this.files.slice(0, this.boxes);
29057         }
29058         
29059         this.uploader.show();
29060         
29061         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29062             this.uploader.hide();
29063         }
29064         
29065         var _this = this;
29066         
29067         var files = [];
29068         
29069         var docs = [];
29070         
29071         Roo.each(this.files, function(file){
29072             
29073             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29074                 var f = this.renderPreview(file);
29075                 files.push(f);
29076                 return;
29077             }
29078             
29079             if(file.type.indexOf('image') != -1){
29080                 this.delegates.push(
29081                     (function(){
29082                         _this.process(file);
29083                     }).createDelegate(this)
29084                 );
29085         
29086                 return;
29087             }
29088             
29089             docs.push(
29090                 (function(){
29091                     _this.process(file);
29092                 }).createDelegate(this)
29093             );
29094             
29095         }, this);
29096         
29097         this.files = files;
29098         
29099         this.delegates = this.delegates.concat(docs);
29100         
29101         if(!this.delegates.length){
29102             this.refresh();
29103             return;
29104         }
29105         
29106         this.progressBar.aria_valuemax = this.delegates.length;
29107         
29108         this.arrange();
29109         
29110         return;
29111     },
29112     
29113     arrange : function()
29114     {
29115         if(!this.delegates.length){
29116             this.progressDialog.hide();
29117             this.refresh();
29118             return;
29119         }
29120         
29121         var delegate = this.delegates.shift();
29122         
29123         this.progressDialog.show();
29124         
29125         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29126         
29127         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29128         
29129         delegate();
29130     },
29131     
29132     refresh : function()
29133     {
29134         this.uploader.show();
29135         
29136         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29137             this.uploader.hide();
29138         }
29139         
29140         Roo.isTouch ? this.closable(false) : this.closable(true);
29141         
29142         this.fireEvent('refresh', this);
29143     },
29144     
29145     onRemove : function(e, el, o)
29146     {
29147         e.preventDefault();
29148         
29149         this.fireEvent('remove', this, o);
29150         
29151     },
29152     
29153     remove : function(o)
29154     {
29155         var files = [];
29156         
29157         Roo.each(this.files, function(file){
29158             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29159                 files.push(file);
29160                 return;
29161             }
29162
29163             o.target.remove();
29164
29165         }, this);
29166         
29167         this.files = files;
29168         
29169         this.refresh();
29170     },
29171     
29172     clear : function()
29173     {
29174         Roo.each(this.files, function(file){
29175             if(!file.target){
29176                 return;
29177             }
29178             
29179             file.target.remove();
29180
29181         }, this);
29182         
29183         this.files = [];
29184         
29185         this.refresh();
29186     },
29187     
29188     onClick : function(e, el, o)
29189     {
29190         e.preventDefault();
29191         
29192         this.fireEvent('click', this, o);
29193         
29194     },
29195     
29196     closable : function(closable)
29197     {
29198         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29199             
29200             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29201             
29202             if(closable){
29203                 el.show();
29204                 return;
29205             }
29206             
29207             el.hide();
29208             
29209         }, this);
29210     },
29211     
29212     xhrOnLoad : function(xhr)
29213     {
29214         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29215             el.remove();
29216         }, this);
29217         
29218         if (xhr.readyState !== 4) {
29219             this.arrange();
29220             this.fireEvent('exception', this, xhr);
29221             return;
29222         }
29223
29224         var response = Roo.decode(xhr.responseText);
29225         
29226         if(!response.success){
29227             this.arrange();
29228             this.fireEvent('exception', this, xhr);
29229             return;
29230         }
29231         
29232         var file = this.renderPreview(response.data);
29233         
29234         this.files.push(file);
29235         
29236         this.arrange();
29237         
29238         this.fireEvent('afterupload', this, xhr);
29239         
29240     },
29241     
29242     xhrOnError : function(xhr)
29243     {
29244         Roo.log('xhr on error');
29245         
29246         var response = Roo.decode(xhr.responseText);
29247           
29248         Roo.log(response);
29249         
29250         this.arrange();
29251     },
29252     
29253     process : function(file)
29254     {
29255         if(this.fireEvent('process', this, file) !== false){
29256             if(this.editable && file.type.indexOf('image') != -1){
29257                 this.fireEvent('edit', this, file);
29258                 return;
29259             }
29260
29261             this.uploadStart(file, false);
29262
29263             return;
29264         }
29265         
29266     },
29267     
29268     uploadStart : function(file, crop)
29269     {
29270         this.xhr = new XMLHttpRequest();
29271         
29272         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29273             this.arrange();
29274             return;
29275         }
29276         
29277         file.xhr = this.xhr;
29278             
29279         this.managerEl.createChild({
29280             tag : 'div',
29281             cls : 'roo-document-manager-loading',
29282             cn : [
29283                 {
29284                     tag : 'div',
29285                     tooltip : file.name,
29286                     cls : 'roo-document-manager-thumb',
29287                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29288                 }
29289             ]
29290
29291         });
29292
29293         this.xhr.open(this.method, this.url, true);
29294         
29295         var headers = {
29296             "Accept": "application/json",
29297             "Cache-Control": "no-cache",
29298             "X-Requested-With": "XMLHttpRequest"
29299         };
29300         
29301         for (var headerName in headers) {
29302             var headerValue = headers[headerName];
29303             if (headerValue) {
29304                 this.xhr.setRequestHeader(headerName, headerValue);
29305             }
29306         }
29307         
29308         var _this = this;
29309         
29310         this.xhr.onload = function()
29311         {
29312             _this.xhrOnLoad(_this.xhr);
29313         }
29314         
29315         this.xhr.onerror = function()
29316         {
29317             _this.xhrOnError(_this.xhr);
29318         }
29319         
29320         var formData = new FormData();
29321
29322         formData.append('returnHTML', 'NO');
29323         
29324         if(crop){
29325             formData.append('crop', crop);
29326         }
29327         
29328         formData.append(this.paramName, file, file.name);
29329         
29330         var options = {
29331             file : file, 
29332             manually : false
29333         };
29334         
29335         if(this.fireEvent('prepare', this, formData, options) != false){
29336             
29337             if(options.manually){
29338                 return;
29339             }
29340             
29341             this.xhr.send(formData);
29342             return;
29343         };
29344         
29345         this.uploadCancel();
29346     },
29347     
29348     uploadCancel : function()
29349     {
29350         if (this.xhr) {
29351             this.xhr.abort();
29352         }
29353         
29354         this.delegates = [];
29355         
29356         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29357             el.remove();
29358         }, this);
29359         
29360         this.arrange();
29361     },
29362     
29363     renderPreview : function(file)
29364     {
29365         if(typeof(file.target) != 'undefined' && file.target){
29366             return file;
29367         }
29368         
29369         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29370         
29371         var previewEl = this.managerEl.createChild({
29372             tag : 'div',
29373             cls : 'roo-document-manager-preview',
29374             cn : [
29375                 {
29376                     tag : 'div',
29377                     tooltip : file[this.toolTipName],
29378                     cls : 'roo-document-manager-thumb',
29379                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29380                 },
29381                 {
29382                     tag : 'button',
29383                     cls : 'close',
29384                     html : '<i class="fa fa-times-circle"></i>'
29385                 }
29386             ]
29387         });
29388
29389         var close = previewEl.select('button.close', true).first();
29390
29391         close.on('click', this.onRemove, this, file);
29392
29393         file.target = previewEl;
29394
29395         var image = previewEl.select('img', true).first();
29396         
29397         var _this = this;
29398         
29399         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29400         
29401         image.on('click', this.onClick, this, file);
29402         
29403         this.fireEvent('previewrendered', this, file);
29404         
29405         return file;
29406         
29407     },
29408     
29409     onPreviewLoad : function(file, image)
29410     {
29411         if(typeof(file.target) == 'undefined' || !file.target){
29412             return;
29413         }
29414         
29415         var width = image.dom.naturalWidth || image.dom.width;
29416         var height = image.dom.naturalHeight || image.dom.height;
29417         
29418         if(!this.previewResize) {
29419             return;
29420         }
29421         
29422         if(width > height){
29423             file.target.addClass('wide');
29424             return;
29425         }
29426         
29427         file.target.addClass('tall');
29428         return;
29429         
29430     },
29431     
29432     uploadFromSource : function(file, crop)
29433     {
29434         this.xhr = new XMLHttpRequest();
29435         
29436         this.managerEl.createChild({
29437             tag : 'div',
29438             cls : 'roo-document-manager-loading',
29439             cn : [
29440                 {
29441                     tag : 'div',
29442                     tooltip : file.name,
29443                     cls : 'roo-document-manager-thumb',
29444                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29445                 }
29446             ]
29447
29448         });
29449
29450         this.xhr.open(this.method, this.url, true);
29451         
29452         var headers = {
29453             "Accept": "application/json",
29454             "Cache-Control": "no-cache",
29455             "X-Requested-With": "XMLHttpRequest"
29456         };
29457         
29458         for (var headerName in headers) {
29459             var headerValue = headers[headerName];
29460             if (headerValue) {
29461                 this.xhr.setRequestHeader(headerName, headerValue);
29462             }
29463         }
29464         
29465         var _this = this;
29466         
29467         this.xhr.onload = function()
29468         {
29469             _this.xhrOnLoad(_this.xhr);
29470         }
29471         
29472         this.xhr.onerror = function()
29473         {
29474             _this.xhrOnError(_this.xhr);
29475         }
29476         
29477         var formData = new FormData();
29478
29479         formData.append('returnHTML', 'NO');
29480         
29481         formData.append('crop', crop);
29482         
29483         if(typeof(file.filename) != 'undefined'){
29484             formData.append('filename', file.filename);
29485         }
29486         
29487         if(typeof(file.mimetype) != 'undefined'){
29488             formData.append('mimetype', file.mimetype);
29489         }
29490         
29491         Roo.log(formData);
29492         
29493         if(this.fireEvent('prepare', this, formData) != false){
29494             this.xhr.send(formData);
29495         };
29496     }
29497 });
29498
29499 /*
29500 * Licence: LGPL
29501 */
29502
29503 /**
29504  * @class Roo.bootstrap.DocumentViewer
29505  * @extends Roo.bootstrap.Component
29506  * Bootstrap DocumentViewer class
29507  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29508  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29509  * 
29510  * @constructor
29511  * Create a new DocumentViewer
29512  * @param {Object} config The config object
29513  */
29514
29515 Roo.bootstrap.DocumentViewer = function(config){
29516     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29517     
29518     this.addEvents({
29519         /**
29520          * @event initial
29521          * Fire after initEvent
29522          * @param {Roo.bootstrap.DocumentViewer} this
29523          */
29524         "initial" : true,
29525         /**
29526          * @event click
29527          * Fire after click
29528          * @param {Roo.bootstrap.DocumentViewer} this
29529          */
29530         "click" : true,
29531         /**
29532          * @event download
29533          * Fire after download button
29534          * @param {Roo.bootstrap.DocumentViewer} this
29535          */
29536         "download" : true,
29537         /**
29538          * @event trash
29539          * Fire after trash button
29540          * @param {Roo.bootstrap.DocumentViewer} this
29541          */
29542         "trash" : true
29543         
29544     });
29545 };
29546
29547 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29548     
29549     showDownload : true,
29550     
29551     showTrash : true,
29552     
29553     getAutoCreate : function()
29554     {
29555         var cfg = {
29556             tag : 'div',
29557             cls : 'roo-document-viewer',
29558             cn : [
29559                 {
29560                     tag : 'div',
29561                     cls : 'roo-document-viewer-body',
29562                     cn : [
29563                         {
29564                             tag : 'div',
29565                             cls : 'roo-document-viewer-thumb',
29566                             cn : [
29567                                 {
29568                                     tag : 'img',
29569                                     cls : 'roo-document-viewer-image'
29570                                 }
29571                             ]
29572                         }
29573                     ]
29574                 },
29575                 {
29576                     tag : 'div',
29577                     cls : 'roo-document-viewer-footer',
29578                     cn : {
29579                         tag : 'div',
29580                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29581                         cn : [
29582                             {
29583                                 tag : 'div',
29584                                 cls : 'btn-group roo-document-viewer-download',
29585                                 cn : [
29586                                     {
29587                                         tag : 'button',
29588                                         cls : 'btn btn-default',
29589                                         html : '<i class="fa fa-download"></i>'
29590                                     }
29591                                 ]
29592                             },
29593                             {
29594                                 tag : 'div',
29595                                 cls : 'btn-group roo-document-viewer-trash',
29596                                 cn : [
29597                                     {
29598                                         tag : 'button',
29599                                         cls : 'btn btn-default',
29600                                         html : '<i class="fa fa-trash"></i>'
29601                                     }
29602                                 ]
29603                             }
29604                         ]
29605                     }
29606                 }
29607             ]
29608         };
29609         
29610         return cfg;
29611     },
29612     
29613     initEvents : function()
29614     {
29615         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29616         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29617         
29618         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29619         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29620         
29621         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29622         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29623         
29624         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29625         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29626         
29627         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29628         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29629         
29630         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29631         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29632         
29633         this.bodyEl.on('click', this.onClick, this);
29634         this.downloadBtn.on('click', this.onDownload, this);
29635         this.trashBtn.on('click', this.onTrash, this);
29636         
29637         this.downloadBtn.hide();
29638         this.trashBtn.hide();
29639         
29640         if(this.showDownload){
29641             this.downloadBtn.show();
29642         }
29643         
29644         if(this.showTrash){
29645             this.trashBtn.show();
29646         }
29647         
29648         if(!this.showDownload && !this.showTrash) {
29649             this.footerEl.hide();
29650         }
29651         
29652     },
29653     
29654     initial : function()
29655     {
29656         this.fireEvent('initial', this);
29657         
29658     },
29659     
29660     onClick : function(e)
29661     {
29662         e.preventDefault();
29663         
29664         this.fireEvent('click', this);
29665     },
29666     
29667     onDownload : function(e)
29668     {
29669         e.preventDefault();
29670         
29671         this.fireEvent('download', this);
29672     },
29673     
29674     onTrash : function(e)
29675     {
29676         e.preventDefault();
29677         
29678         this.fireEvent('trash', this);
29679     }
29680     
29681 });
29682 /*
29683  * - LGPL
29684  *
29685  * nav progress bar
29686  * 
29687  */
29688
29689 /**
29690  * @class Roo.bootstrap.NavProgressBar
29691  * @extends Roo.bootstrap.Component
29692  * Bootstrap NavProgressBar class
29693  * 
29694  * @constructor
29695  * Create a new nav progress bar
29696  * @param {Object} config The config object
29697  */
29698
29699 Roo.bootstrap.NavProgressBar = function(config){
29700     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29701
29702     this.bullets = this.bullets || [];
29703    
29704 //    Roo.bootstrap.NavProgressBar.register(this);
29705      this.addEvents({
29706         /**
29707              * @event changed
29708              * Fires when the active item changes
29709              * @param {Roo.bootstrap.NavProgressBar} this
29710              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29711              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29712          */
29713         'changed': true
29714      });
29715     
29716 };
29717
29718 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29719     
29720     bullets : [],
29721     barItems : [],
29722     
29723     getAutoCreate : function()
29724     {
29725         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29726         
29727         cfg = {
29728             tag : 'div',
29729             cls : 'roo-navigation-bar-group',
29730             cn : [
29731                 {
29732                     tag : 'div',
29733                     cls : 'roo-navigation-top-bar'
29734                 },
29735                 {
29736                     tag : 'div',
29737                     cls : 'roo-navigation-bullets-bar',
29738                     cn : [
29739                         {
29740                             tag : 'ul',
29741                             cls : 'roo-navigation-bar'
29742                         }
29743                     ]
29744                 },
29745                 
29746                 {
29747                     tag : 'div',
29748                     cls : 'roo-navigation-bottom-bar'
29749                 }
29750             ]
29751             
29752         };
29753         
29754         return cfg;
29755         
29756     },
29757     
29758     initEvents: function() 
29759     {
29760         
29761     },
29762     
29763     onRender : function(ct, position) 
29764     {
29765         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29766         
29767         if(this.bullets.length){
29768             Roo.each(this.bullets, function(b){
29769                this.addItem(b);
29770             }, this);
29771         }
29772         
29773         this.format();
29774         
29775     },
29776     
29777     addItem : function(cfg)
29778     {
29779         var item = new Roo.bootstrap.NavProgressItem(cfg);
29780         
29781         item.parentId = this.id;
29782         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29783         
29784         if(cfg.html){
29785             var top = new Roo.bootstrap.Element({
29786                 tag : 'div',
29787                 cls : 'roo-navigation-bar-text'
29788             });
29789             
29790             var bottom = new Roo.bootstrap.Element({
29791                 tag : 'div',
29792                 cls : 'roo-navigation-bar-text'
29793             });
29794             
29795             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29796             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29797             
29798             var topText = new Roo.bootstrap.Element({
29799                 tag : 'span',
29800                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29801             });
29802             
29803             var bottomText = new Roo.bootstrap.Element({
29804                 tag : 'span',
29805                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29806             });
29807             
29808             topText.onRender(top.el, null);
29809             bottomText.onRender(bottom.el, null);
29810             
29811             item.topEl = top;
29812             item.bottomEl = bottom;
29813         }
29814         
29815         this.barItems.push(item);
29816         
29817         return item;
29818     },
29819     
29820     getActive : function()
29821     {
29822         var active = false;
29823         
29824         Roo.each(this.barItems, function(v){
29825             
29826             if (!v.isActive()) {
29827                 return;
29828             }
29829             
29830             active = v;
29831             return false;
29832             
29833         });
29834         
29835         return active;
29836     },
29837     
29838     setActiveItem : function(item)
29839     {
29840         var prev = false;
29841         
29842         Roo.each(this.barItems, function(v){
29843             if (v.rid == item.rid) {
29844                 return ;
29845             }
29846             
29847             if (v.isActive()) {
29848                 v.setActive(false);
29849                 prev = v;
29850             }
29851         });
29852
29853         item.setActive(true);
29854         
29855         this.fireEvent('changed', this, item, prev);
29856     },
29857     
29858     getBarItem: function(rid)
29859     {
29860         var ret = false;
29861         
29862         Roo.each(this.barItems, function(e) {
29863             if (e.rid != rid) {
29864                 return;
29865             }
29866             
29867             ret =  e;
29868             return false;
29869         });
29870         
29871         return ret;
29872     },
29873     
29874     indexOfItem : function(item)
29875     {
29876         var index = false;
29877         
29878         Roo.each(this.barItems, function(v, i){
29879             
29880             if (v.rid != item.rid) {
29881                 return;
29882             }
29883             
29884             index = i;
29885             return false
29886         });
29887         
29888         return index;
29889     },
29890     
29891     setActiveNext : function()
29892     {
29893         var i = this.indexOfItem(this.getActive());
29894         
29895         if (i > this.barItems.length) {
29896             return;
29897         }
29898         
29899         this.setActiveItem(this.barItems[i+1]);
29900     },
29901     
29902     setActivePrev : function()
29903     {
29904         var i = this.indexOfItem(this.getActive());
29905         
29906         if (i  < 1) {
29907             return;
29908         }
29909         
29910         this.setActiveItem(this.barItems[i-1]);
29911     },
29912     
29913     format : function()
29914     {
29915         if(!this.barItems.length){
29916             return;
29917         }
29918      
29919         var width = 100 / this.barItems.length;
29920         
29921         Roo.each(this.barItems, function(i){
29922             i.el.setStyle('width', width + '%');
29923             i.topEl.el.setStyle('width', width + '%');
29924             i.bottomEl.el.setStyle('width', width + '%');
29925         }, this);
29926         
29927     }
29928     
29929 });
29930 /*
29931  * - LGPL
29932  *
29933  * Nav Progress Item
29934  * 
29935  */
29936
29937 /**
29938  * @class Roo.bootstrap.NavProgressItem
29939  * @extends Roo.bootstrap.Component
29940  * Bootstrap NavProgressItem class
29941  * @cfg {String} rid the reference id
29942  * @cfg {Boolean} active (true|false) Is item active default false
29943  * @cfg {Boolean} disabled (true|false) Is item active default false
29944  * @cfg {String} html
29945  * @cfg {String} position (top|bottom) text position default bottom
29946  * @cfg {String} icon show icon instead of number
29947  * 
29948  * @constructor
29949  * Create a new NavProgressItem
29950  * @param {Object} config The config object
29951  */
29952 Roo.bootstrap.NavProgressItem = function(config){
29953     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29954     this.addEvents({
29955         // raw events
29956         /**
29957          * @event click
29958          * The raw click event for the entire grid.
29959          * @param {Roo.bootstrap.NavProgressItem} this
29960          * @param {Roo.EventObject} e
29961          */
29962         "click" : true
29963     });
29964    
29965 };
29966
29967 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29968     
29969     rid : '',
29970     active : false,
29971     disabled : false,
29972     html : '',
29973     position : 'bottom',
29974     icon : false,
29975     
29976     getAutoCreate : function()
29977     {
29978         var iconCls = 'roo-navigation-bar-item-icon';
29979         
29980         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29981         
29982         var cfg = {
29983             tag: 'li',
29984             cls: 'roo-navigation-bar-item',
29985             cn : [
29986                 {
29987                     tag : 'i',
29988                     cls : iconCls
29989                 }
29990             ]
29991         };
29992         
29993         if(this.active){
29994             cfg.cls += ' active';
29995         }
29996         if(this.disabled){
29997             cfg.cls += ' disabled';
29998         }
29999         
30000         return cfg;
30001     },
30002     
30003     disable : function()
30004     {
30005         this.setDisabled(true);
30006     },
30007     
30008     enable : function()
30009     {
30010         this.setDisabled(false);
30011     },
30012     
30013     initEvents: function() 
30014     {
30015         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30016         
30017         this.iconEl.on('click', this.onClick, this);
30018     },
30019     
30020     onClick : function(e)
30021     {
30022         e.preventDefault();
30023         
30024         if(this.disabled){
30025             return;
30026         }
30027         
30028         if(this.fireEvent('click', this, e) === false){
30029             return;
30030         };
30031         
30032         this.parent().setActiveItem(this);
30033     },
30034     
30035     isActive: function () 
30036     {
30037         return this.active;
30038     },
30039     
30040     setActive : function(state)
30041     {
30042         if(this.active == state){
30043             return;
30044         }
30045         
30046         this.active = state;
30047         
30048         if (state) {
30049             this.el.addClass('active');
30050             return;
30051         }
30052         
30053         this.el.removeClass('active');
30054         
30055         return;
30056     },
30057     
30058     setDisabled : function(state)
30059     {
30060         if(this.disabled == state){
30061             return;
30062         }
30063         
30064         this.disabled = state;
30065         
30066         if (state) {
30067             this.el.addClass('disabled');
30068             return;
30069         }
30070         
30071         this.el.removeClass('disabled');
30072     },
30073     
30074     tooltipEl : function()
30075     {
30076         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30077     }
30078 });
30079  
30080
30081  /*
30082  * - LGPL
30083  *
30084  * FieldLabel
30085  * 
30086  */
30087
30088 /**
30089  * @class Roo.bootstrap.FieldLabel
30090  * @extends Roo.bootstrap.Component
30091  * Bootstrap FieldLabel class
30092  * @cfg {String} html contents of the element
30093  * @cfg {String} tag tag of the element default label
30094  * @cfg {String} cls class of the element
30095  * @cfg {String} target label target 
30096  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30097  * @cfg {String} invalidClass default "text-warning"
30098  * @cfg {String} validClass default "text-success"
30099  * @cfg {String} iconTooltip default "This field is required"
30100  * @cfg {String} indicatorpos (left|right) default left
30101  * 
30102  * @constructor
30103  * Create a new FieldLabel
30104  * @param {Object} config The config object
30105  */
30106
30107 Roo.bootstrap.FieldLabel = function(config){
30108     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30109     
30110     this.addEvents({
30111             /**
30112              * @event invalid
30113              * Fires after the field has been marked as invalid.
30114              * @param {Roo.form.FieldLabel} this
30115              * @param {String} msg The validation message
30116              */
30117             invalid : true,
30118             /**
30119              * @event valid
30120              * Fires after the field has been validated with no errors.
30121              * @param {Roo.form.FieldLabel} this
30122              */
30123             valid : true
30124         });
30125 };
30126
30127 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30128     
30129     tag: 'label',
30130     cls: '',
30131     html: '',
30132     target: '',
30133     allowBlank : true,
30134     invalidClass : 'has-warning',
30135     validClass : 'has-success',
30136     iconTooltip : 'This field is required',
30137     indicatorpos : 'left',
30138     
30139     getAutoCreate : function(){
30140         
30141         var cls = "";
30142         if (!this.allowBlank) {
30143             cls  = "visible";
30144         }
30145         
30146         var cfg = {
30147             tag : this.tag,
30148             cls : 'roo-bootstrap-field-label ' + this.cls,
30149             for : this.target,
30150             cn : [
30151                 {
30152                     tag : 'i',
30153                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30154                     tooltip : this.iconTooltip
30155                 },
30156                 {
30157                     tag : 'span',
30158                     html : this.html
30159                 }
30160             ] 
30161         };
30162         
30163         if(this.indicatorpos == 'right'){
30164             var cfg = {
30165                 tag : this.tag,
30166                 cls : 'roo-bootstrap-field-label ' + this.cls,
30167                 for : this.target,
30168                 cn : [
30169                     {
30170                         tag : 'span',
30171                         html : this.html
30172                     },
30173                     {
30174                         tag : 'i',
30175                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30176                         tooltip : this.iconTooltip
30177                     }
30178                 ] 
30179             };
30180         }
30181         
30182         return cfg;
30183     },
30184     
30185     initEvents: function() 
30186     {
30187         Roo.bootstrap.Element.superclass.initEvents.call(this);
30188         
30189         this.indicator = this.indicatorEl();
30190         
30191         if(this.indicator){
30192             this.indicator.removeClass('visible');
30193             this.indicator.addClass('invisible');
30194         }
30195         
30196         Roo.bootstrap.FieldLabel.register(this);
30197     },
30198     
30199     indicatorEl : function()
30200     {
30201         var indicator = this.el.select('i.roo-required-indicator',true).first();
30202         
30203         if(!indicator){
30204             return false;
30205         }
30206         
30207         return indicator;
30208         
30209     },
30210     
30211     /**
30212      * Mark this field as valid
30213      */
30214     markValid : function()
30215     {
30216         if(this.indicator){
30217             this.indicator.removeClass('visible');
30218             this.indicator.addClass('invisible');
30219         }
30220         
30221         this.el.removeClass(this.invalidClass);
30222         
30223         this.el.addClass(this.validClass);
30224         
30225         this.fireEvent('valid', this);
30226     },
30227     
30228     /**
30229      * Mark this field as invalid
30230      * @param {String} msg The validation message
30231      */
30232     markInvalid : function(msg)
30233     {
30234         if(this.indicator){
30235             this.indicator.removeClass('invisible');
30236             this.indicator.addClass('visible');
30237         }
30238         
30239         this.el.removeClass(this.validClass);
30240         
30241         this.el.addClass(this.invalidClass);
30242         
30243         this.fireEvent('invalid', this, msg);
30244     }
30245     
30246    
30247 });
30248
30249 Roo.apply(Roo.bootstrap.FieldLabel, {
30250     
30251     groups: {},
30252     
30253      /**
30254     * register a FieldLabel Group
30255     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30256     */
30257     register : function(label)
30258     {
30259         if(this.groups.hasOwnProperty(label.target)){
30260             return;
30261         }
30262      
30263         this.groups[label.target] = label;
30264         
30265     },
30266     /**
30267     * fetch a FieldLabel Group based on the target
30268     * @param {string} target
30269     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30270     */
30271     get: function(target) {
30272         if (typeof(this.groups[target]) == 'undefined') {
30273             return false;
30274         }
30275         
30276         return this.groups[target] ;
30277     }
30278 });
30279
30280  
30281
30282  /*
30283  * - LGPL
30284  *
30285  * page DateSplitField.
30286  * 
30287  */
30288
30289
30290 /**
30291  * @class Roo.bootstrap.DateSplitField
30292  * @extends Roo.bootstrap.Component
30293  * Bootstrap DateSplitField class
30294  * @cfg {string} fieldLabel - the label associated
30295  * @cfg {Number} labelWidth set the width of label (0-12)
30296  * @cfg {String} labelAlign (top|left)
30297  * @cfg {Boolean} dayAllowBlank (true|false) default false
30298  * @cfg {Boolean} monthAllowBlank (true|false) default false
30299  * @cfg {Boolean} yearAllowBlank (true|false) default false
30300  * @cfg {string} dayPlaceholder 
30301  * @cfg {string} monthPlaceholder
30302  * @cfg {string} yearPlaceholder
30303  * @cfg {string} dayFormat default 'd'
30304  * @cfg {string} monthFormat default 'm'
30305  * @cfg {string} yearFormat default 'Y'
30306  * @cfg {Number} labellg set the width of label (1-12)
30307  * @cfg {Number} labelmd set the width of label (1-12)
30308  * @cfg {Number} labelsm set the width of label (1-12)
30309  * @cfg {Number} labelxs set the width of label (1-12)
30310
30311  *     
30312  * @constructor
30313  * Create a new DateSplitField
30314  * @param {Object} config The config object
30315  */
30316
30317 Roo.bootstrap.DateSplitField = function(config){
30318     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30319     
30320     this.addEvents({
30321         // raw events
30322          /**
30323          * @event years
30324          * getting the data of years
30325          * @param {Roo.bootstrap.DateSplitField} this
30326          * @param {Object} years
30327          */
30328         "years" : true,
30329         /**
30330          * @event days
30331          * getting the data of days
30332          * @param {Roo.bootstrap.DateSplitField} this
30333          * @param {Object} days
30334          */
30335         "days" : true,
30336         /**
30337          * @event invalid
30338          * Fires after the field has been marked as invalid.
30339          * @param {Roo.form.Field} this
30340          * @param {String} msg The validation message
30341          */
30342         invalid : true,
30343        /**
30344          * @event valid
30345          * Fires after the field has been validated with no errors.
30346          * @param {Roo.form.Field} this
30347          */
30348         valid : true
30349     });
30350 };
30351
30352 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30353     
30354     fieldLabel : '',
30355     labelAlign : 'top',
30356     labelWidth : 3,
30357     dayAllowBlank : false,
30358     monthAllowBlank : false,
30359     yearAllowBlank : false,
30360     dayPlaceholder : '',
30361     monthPlaceholder : '',
30362     yearPlaceholder : '',
30363     dayFormat : 'd',
30364     monthFormat : 'm',
30365     yearFormat : 'Y',
30366     isFormField : true,
30367     labellg : 0,
30368     labelmd : 0,
30369     labelsm : 0,
30370     labelxs : 0,
30371     
30372     getAutoCreate : function()
30373     {
30374         var cfg = {
30375             tag : 'div',
30376             cls : 'row roo-date-split-field-group',
30377             cn : [
30378                 {
30379                     tag : 'input',
30380                     type : 'hidden',
30381                     cls : 'form-hidden-field roo-date-split-field-group-value',
30382                     name : this.name
30383                 }
30384             ]
30385         };
30386         
30387         var labelCls = 'col-md-12';
30388         var contentCls = 'col-md-4';
30389         
30390         if(this.fieldLabel){
30391             
30392             var label = {
30393                 tag : 'div',
30394                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30395                 cn : [
30396                     {
30397                         tag : 'label',
30398                         html : this.fieldLabel
30399                     }
30400                 ]
30401             };
30402             
30403             if(this.labelAlign == 'left'){
30404             
30405                 if(this.labelWidth > 12){
30406                     label.style = "width: " + this.labelWidth + 'px';
30407                 }
30408
30409                 if(this.labelWidth < 13 && this.labelmd == 0){
30410                     this.labelmd = this.labelWidth;
30411                 }
30412
30413                 if(this.labellg > 0){
30414                     labelCls = ' col-lg-' + this.labellg;
30415                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30416                 }
30417
30418                 if(this.labelmd > 0){
30419                     labelCls = ' col-md-' + this.labelmd;
30420                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30421                 }
30422
30423                 if(this.labelsm > 0){
30424                     labelCls = ' col-sm-' + this.labelsm;
30425                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30426                 }
30427
30428                 if(this.labelxs > 0){
30429                     labelCls = ' col-xs-' + this.labelxs;
30430                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30431                 }
30432             }
30433             
30434             label.cls += ' ' + labelCls;
30435             
30436             cfg.cn.push(label);
30437         }
30438         
30439         Roo.each(['day', 'month', 'year'], function(t){
30440             cfg.cn.push({
30441                 tag : 'div',
30442                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30443             });
30444         }, this);
30445         
30446         return cfg;
30447     },
30448     
30449     inputEl: function ()
30450     {
30451         return this.el.select('.roo-date-split-field-group-value', true).first();
30452     },
30453     
30454     onRender : function(ct, position) 
30455     {
30456         var _this = this;
30457         
30458         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30459         
30460         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30461         
30462         this.dayField = new Roo.bootstrap.ComboBox({
30463             allowBlank : this.dayAllowBlank,
30464             alwaysQuery : true,
30465             displayField : 'value',
30466             editable : false,
30467             fieldLabel : '',
30468             forceSelection : true,
30469             mode : 'local',
30470             placeholder : this.dayPlaceholder,
30471             selectOnFocus : true,
30472             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30473             triggerAction : 'all',
30474             typeAhead : true,
30475             valueField : 'value',
30476             store : new Roo.data.SimpleStore({
30477                 data : (function() {    
30478                     var days = [];
30479                     _this.fireEvent('days', _this, days);
30480                     return days;
30481                 })(),
30482                 fields : [ 'value' ]
30483             }),
30484             listeners : {
30485                 select : function (_self, record, index)
30486                 {
30487                     _this.setValue(_this.getValue());
30488                 }
30489             }
30490         });
30491
30492         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30493         
30494         this.monthField = new Roo.bootstrap.MonthField({
30495             after : '<i class=\"fa fa-calendar\"></i>',
30496             allowBlank : this.monthAllowBlank,
30497             placeholder : this.monthPlaceholder,
30498             readOnly : true,
30499             listeners : {
30500                 render : function (_self)
30501                 {
30502                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30503                         e.preventDefault();
30504                         _self.focus();
30505                     });
30506                 },
30507                 select : function (_self, oldvalue, newvalue)
30508                 {
30509                     _this.setValue(_this.getValue());
30510                 }
30511             }
30512         });
30513         
30514         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30515         
30516         this.yearField = new Roo.bootstrap.ComboBox({
30517             allowBlank : this.yearAllowBlank,
30518             alwaysQuery : true,
30519             displayField : 'value',
30520             editable : false,
30521             fieldLabel : '',
30522             forceSelection : true,
30523             mode : 'local',
30524             placeholder : this.yearPlaceholder,
30525             selectOnFocus : true,
30526             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30527             triggerAction : 'all',
30528             typeAhead : true,
30529             valueField : 'value',
30530             store : new Roo.data.SimpleStore({
30531                 data : (function() {
30532                     var years = [];
30533                     _this.fireEvent('years', _this, years);
30534                     return years;
30535                 })(),
30536                 fields : [ 'value' ]
30537             }),
30538             listeners : {
30539                 select : function (_self, record, index)
30540                 {
30541                     _this.setValue(_this.getValue());
30542                 }
30543             }
30544         });
30545
30546         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30547     },
30548     
30549     setValue : function(v, format)
30550     {
30551         this.inputEl.dom.value = v;
30552         
30553         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30554         
30555         var d = Date.parseDate(v, f);
30556         
30557         if(!d){
30558             this.validate();
30559             return;
30560         }
30561         
30562         this.setDay(d.format(this.dayFormat));
30563         this.setMonth(d.format(this.monthFormat));
30564         this.setYear(d.format(this.yearFormat));
30565         
30566         this.validate();
30567         
30568         return;
30569     },
30570     
30571     setDay : function(v)
30572     {
30573         this.dayField.setValue(v);
30574         this.inputEl.dom.value = this.getValue();
30575         this.validate();
30576         return;
30577     },
30578     
30579     setMonth : function(v)
30580     {
30581         this.monthField.setValue(v, true);
30582         this.inputEl.dom.value = this.getValue();
30583         this.validate();
30584         return;
30585     },
30586     
30587     setYear : function(v)
30588     {
30589         this.yearField.setValue(v);
30590         this.inputEl.dom.value = this.getValue();
30591         this.validate();
30592         return;
30593     },
30594     
30595     getDay : function()
30596     {
30597         return this.dayField.getValue();
30598     },
30599     
30600     getMonth : function()
30601     {
30602         return this.monthField.getValue();
30603     },
30604     
30605     getYear : function()
30606     {
30607         return this.yearField.getValue();
30608     },
30609     
30610     getValue : function()
30611     {
30612         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30613         
30614         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30615         
30616         return date;
30617     },
30618     
30619     reset : function()
30620     {
30621         this.setDay('');
30622         this.setMonth('');
30623         this.setYear('');
30624         this.inputEl.dom.value = '';
30625         this.validate();
30626         return;
30627     },
30628     
30629     validate : function()
30630     {
30631         var d = this.dayField.validate();
30632         var m = this.monthField.validate();
30633         var y = this.yearField.validate();
30634         
30635         var valid = true;
30636         
30637         if(
30638                 (!this.dayAllowBlank && !d) ||
30639                 (!this.monthAllowBlank && !m) ||
30640                 (!this.yearAllowBlank && !y)
30641         ){
30642             valid = false;
30643         }
30644         
30645         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30646             return valid;
30647         }
30648         
30649         if(valid){
30650             this.markValid();
30651             return valid;
30652         }
30653         
30654         this.markInvalid();
30655         
30656         return valid;
30657     },
30658     
30659     markValid : function()
30660     {
30661         
30662         var label = this.el.select('label', true).first();
30663         var icon = this.el.select('i.fa-star', true).first();
30664
30665         if(label && icon){
30666             icon.remove();
30667         }
30668         
30669         this.fireEvent('valid', this);
30670     },
30671     
30672      /**
30673      * Mark this field as invalid
30674      * @param {String} msg The validation message
30675      */
30676     markInvalid : function(msg)
30677     {
30678         
30679         var label = this.el.select('label', true).first();
30680         var icon = this.el.select('i.fa-star', true).first();
30681
30682         if(label && !icon){
30683             this.el.select('.roo-date-split-field-label', true).createChild({
30684                 tag : 'i',
30685                 cls : 'text-danger fa fa-lg fa-star',
30686                 tooltip : 'This field is required',
30687                 style : 'margin-right:5px;'
30688             }, label, true);
30689         }
30690         
30691         this.fireEvent('invalid', this, msg);
30692     },
30693     
30694     clearInvalid : function()
30695     {
30696         var label = this.el.select('label', true).first();
30697         var icon = this.el.select('i.fa-star', true).first();
30698
30699         if(label && icon){
30700             icon.remove();
30701         }
30702         
30703         this.fireEvent('valid', this);
30704     },
30705     
30706     getName: function()
30707     {
30708         return this.name;
30709     }
30710     
30711 });
30712
30713  /**
30714  *
30715  * This is based on 
30716  * http://masonry.desandro.com
30717  *
30718  * The idea is to render all the bricks based on vertical width...
30719  *
30720  * The original code extends 'outlayer' - we might need to use that....
30721  * 
30722  */
30723
30724
30725 /**
30726  * @class Roo.bootstrap.LayoutMasonry
30727  * @extends Roo.bootstrap.Component
30728  * Bootstrap Layout Masonry class
30729  * 
30730  * @constructor
30731  * Create a new Element
30732  * @param {Object} config The config object
30733  */
30734
30735 Roo.bootstrap.LayoutMasonry = function(config){
30736     
30737     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30738     
30739     this.bricks = [];
30740     
30741     Roo.bootstrap.LayoutMasonry.register(this);
30742     
30743     this.addEvents({
30744         // raw events
30745         /**
30746          * @event layout
30747          * Fire after layout the items
30748          * @param {Roo.bootstrap.LayoutMasonry} this
30749          * @param {Roo.EventObject} e
30750          */
30751         "layout" : true
30752     });
30753     
30754 };
30755
30756 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30757     
30758     /**
30759      * @cfg {Boolean} isLayoutInstant = no animation?
30760      */   
30761     isLayoutInstant : false, // needed?
30762    
30763     /**
30764      * @cfg {Number} boxWidth  width of the columns
30765      */   
30766     boxWidth : 450,
30767     
30768       /**
30769      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30770      */   
30771     boxHeight : 0,
30772     
30773     /**
30774      * @cfg {Number} padWidth padding below box..
30775      */   
30776     padWidth : 10, 
30777     
30778     /**
30779      * @cfg {Number} gutter gutter width..
30780      */   
30781     gutter : 10,
30782     
30783      /**
30784      * @cfg {Number} maxCols maximum number of columns
30785      */   
30786     
30787     maxCols: 0,
30788     
30789     /**
30790      * @cfg {Boolean} isAutoInitial defalut true
30791      */   
30792     isAutoInitial : true, 
30793     
30794     containerWidth: 0,
30795     
30796     /**
30797      * @cfg {Boolean} isHorizontal defalut false
30798      */   
30799     isHorizontal : false, 
30800
30801     currentSize : null,
30802     
30803     tag: 'div',
30804     
30805     cls: '',
30806     
30807     bricks: null, //CompositeElement
30808     
30809     cols : 1,
30810     
30811     _isLayoutInited : false,
30812     
30813 //    isAlternative : false, // only use for vertical layout...
30814     
30815     /**
30816      * @cfg {Number} alternativePadWidth padding below box..
30817      */   
30818     alternativePadWidth : 50,
30819     
30820     selectedBrick : [],
30821     
30822     getAutoCreate : function(){
30823         
30824         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30825         
30826         var cfg = {
30827             tag: this.tag,
30828             cls: 'blog-masonary-wrapper ' + this.cls,
30829             cn : {
30830                 cls : 'mas-boxes masonary'
30831             }
30832         };
30833         
30834         return cfg;
30835     },
30836     
30837     getChildContainer: function( )
30838     {
30839         if (this.boxesEl) {
30840             return this.boxesEl;
30841         }
30842         
30843         this.boxesEl = this.el.select('.mas-boxes').first();
30844         
30845         return this.boxesEl;
30846     },
30847     
30848     
30849     initEvents : function()
30850     {
30851         var _this = this;
30852         
30853         if(this.isAutoInitial){
30854             Roo.log('hook children rendered');
30855             this.on('childrenrendered', function() {
30856                 Roo.log('children rendered');
30857                 _this.initial();
30858             } ,this);
30859         }
30860     },
30861     
30862     initial : function()
30863     {
30864         this.selectedBrick = [];
30865         
30866         this.currentSize = this.el.getBox(true);
30867         
30868         Roo.EventManager.onWindowResize(this.resize, this); 
30869
30870         if(!this.isAutoInitial){
30871             this.layout();
30872             return;
30873         }
30874         
30875         this.layout();
30876         
30877         return;
30878         //this.layout.defer(500,this);
30879         
30880     },
30881     
30882     resize : function()
30883     {
30884         var cs = this.el.getBox(true);
30885         
30886         if (
30887                 this.currentSize.width == cs.width && 
30888                 this.currentSize.x == cs.x && 
30889                 this.currentSize.height == cs.height && 
30890                 this.currentSize.y == cs.y 
30891         ) {
30892             Roo.log("no change in with or X or Y");
30893             return;
30894         }
30895         
30896         this.currentSize = cs;
30897         
30898         this.layout();
30899         
30900     },
30901     
30902     layout : function()
30903     {   
30904         this._resetLayout();
30905         
30906         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30907         
30908         this.layoutItems( isInstant );
30909       
30910         this._isLayoutInited = true;
30911         
30912         this.fireEvent('layout', this);
30913         
30914     },
30915     
30916     _resetLayout : function()
30917     {
30918         if(this.isHorizontal){
30919             this.horizontalMeasureColumns();
30920             return;
30921         }
30922         
30923         this.verticalMeasureColumns();
30924         
30925     },
30926     
30927     verticalMeasureColumns : function()
30928     {
30929         this.getContainerWidth();
30930         
30931 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30932 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30933 //            return;
30934 //        }
30935         
30936         var boxWidth = this.boxWidth + this.padWidth;
30937         
30938         if(this.containerWidth < this.boxWidth){
30939             boxWidth = this.containerWidth
30940         }
30941         
30942         var containerWidth = this.containerWidth;
30943         
30944         var cols = Math.floor(containerWidth / boxWidth);
30945         
30946         this.cols = Math.max( cols, 1 );
30947         
30948         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30949         
30950         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30951         
30952         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30953         
30954         this.colWidth = boxWidth + avail - this.padWidth;
30955         
30956         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30957         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30958     },
30959     
30960     horizontalMeasureColumns : function()
30961     {
30962         this.getContainerWidth();
30963         
30964         var boxWidth = this.boxWidth;
30965         
30966         if(this.containerWidth < boxWidth){
30967             boxWidth = this.containerWidth;
30968         }
30969         
30970         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30971         
30972         this.el.setHeight(boxWidth);
30973         
30974     },
30975     
30976     getContainerWidth : function()
30977     {
30978         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30979     },
30980     
30981     layoutItems : function( isInstant )
30982     {
30983         Roo.log(this.bricks);
30984         
30985         var items = Roo.apply([], this.bricks);
30986         
30987         if(this.isHorizontal){
30988             this._horizontalLayoutItems( items , isInstant );
30989             return;
30990         }
30991         
30992 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30993 //            this._verticalAlternativeLayoutItems( items , isInstant );
30994 //            return;
30995 //        }
30996         
30997         this._verticalLayoutItems( items , isInstant );
30998         
30999     },
31000     
31001     _verticalLayoutItems : function ( items , isInstant)
31002     {
31003         if ( !items || !items.length ) {
31004             return;
31005         }
31006         
31007         var standard = [
31008             ['xs', 'xs', 'xs', 'tall'],
31009             ['xs', 'xs', 'tall'],
31010             ['xs', 'xs', 'sm'],
31011             ['xs', 'xs', 'xs'],
31012             ['xs', 'tall'],
31013             ['xs', 'sm'],
31014             ['xs', 'xs'],
31015             ['xs'],
31016             
31017             ['sm', 'xs', 'xs'],
31018             ['sm', 'xs'],
31019             ['sm'],
31020             
31021             ['tall', 'xs', 'xs', 'xs'],
31022             ['tall', 'xs', 'xs'],
31023             ['tall', 'xs'],
31024             ['tall']
31025             
31026         ];
31027         
31028         var queue = [];
31029         
31030         var boxes = [];
31031         
31032         var box = [];
31033         
31034         Roo.each(items, function(item, k){
31035             
31036             switch (item.size) {
31037                 // these layouts take up a full box,
31038                 case 'md' :
31039                 case 'md-left' :
31040                 case 'md-right' :
31041                 case 'wide' :
31042                     
31043                     if(box.length){
31044                         boxes.push(box);
31045                         box = [];
31046                     }
31047                     
31048                     boxes.push([item]);
31049                     
31050                     break;
31051                     
31052                 case 'xs' :
31053                 case 'sm' :
31054                 case 'tall' :
31055                     
31056                     box.push(item);
31057                     
31058                     break;
31059                 default :
31060                     break;
31061                     
31062             }
31063             
31064         }, this);
31065         
31066         if(box.length){
31067             boxes.push(box);
31068             box = [];
31069         }
31070         
31071         var filterPattern = function(box, length)
31072         {
31073             if(!box.length){
31074                 return;
31075             }
31076             
31077             var match = false;
31078             
31079             var pattern = box.slice(0, length);
31080             
31081             var format = [];
31082             
31083             Roo.each(pattern, function(i){
31084                 format.push(i.size);
31085             }, this);
31086             
31087             Roo.each(standard, function(s){
31088                 
31089                 if(String(s) != String(format)){
31090                     return;
31091                 }
31092                 
31093                 match = true;
31094                 return false;
31095                 
31096             }, this);
31097             
31098             if(!match && length == 1){
31099                 return;
31100             }
31101             
31102             if(!match){
31103                 filterPattern(box, length - 1);
31104                 return;
31105             }
31106                 
31107             queue.push(pattern);
31108
31109             box = box.slice(length, box.length);
31110
31111             filterPattern(box, 4);
31112
31113             return;
31114             
31115         }
31116         
31117         Roo.each(boxes, function(box, k){
31118             
31119             if(!box.length){
31120                 return;
31121             }
31122             
31123             if(box.length == 1){
31124                 queue.push(box);
31125                 return;
31126             }
31127             
31128             filterPattern(box, 4);
31129             
31130         }, this);
31131         
31132         this._processVerticalLayoutQueue( queue, isInstant );
31133         
31134     },
31135     
31136 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31137 //    {
31138 //        if ( !items || !items.length ) {
31139 //            return;
31140 //        }
31141 //
31142 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31143 //        
31144 //    },
31145     
31146     _horizontalLayoutItems : function ( items , isInstant)
31147     {
31148         if ( !items || !items.length || items.length < 3) {
31149             return;
31150         }
31151         
31152         items.reverse();
31153         
31154         var eItems = items.slice(0, 3);
31155         
31156         items = items.slice(3, items.length);
31157         
31158         var standard = [
31159             ['xs', 'xs', 'xs', 'wide'],
31160             ['xs', 'xs', 'wide'],
31161             ['xs', 'xs', 'sm'],
31162             ['xs', 'xs', 'xs'],
31163             ['xs', 'wide'],
31164             ['xs', 'sm'],
31165             ['xs', 'xs'],
31166             ['xs'],
31167             
31168             ['sm', 'xs', 'xs'],
31169             ['sm', 'xs'],
31170             ['sm'],
31171             
31172             ['wide', 'xs', 'xs', 'xs'],
31173             ['wide', 'xs', 'xs'],
31174             ['wide', 'xs'],
31175             ['wide'],
31176             
31177             ['wide-thin']
31178         ];
31179         
31180         var queue = [];
31181         
31182         var boxes = [];
31183         
31184         var box = [];
31185         
31186         Roo.each(items, function(item, k){
31187             
31188             switch (item.size) {
31189                 case 'md' :
31190                 case 'md-left' :
31191                 case 'md-right' :
31192                 case 'tall' :
31193                     
31194                     if(box.length){
31195                         boxes.push(box);
31196                         box = [];
31197                     }
31198                     
31199                     boxes.push([item]);
31200                     
31201                     break;
31202                     
31203                 case 'xs' :
31204                 case 'sm' :
31205                 case 'wide' :
31206                 case 'wide-thin' :
31207                     
31208                     box.push(item);
31209                     
31210                     break;
31211                 default :
31212                     break;
31213                     
31214             }
31215             
31216         }, this);
31217         
31218         if(box.length){
31219             boxes.push(box);
31220             box = [];
31221         }
31222         
31223         var filterPattern = function(box, length)
31224         {
31225             if(!box.length){
31226                 return;
31227             }
31228             
31229             var match = false;
31230             
31231             var pattern = box.slice(0, length);
31232             
31233             var format = [];
31234             
31235             Roo.each(pattern, function(i){
31236                 format.push(i.size);
31237             }, this);
31238             
31239             Roo.each(standard, function(s){
31240                 
31241                 if(String(s) != String(format)){
31242                     return;
31243                 }
31244                 
31245                 match = true;
31246                 return false;
31247                 
31248             }, this);
31249             
31250             if(!match && length == 1){
31251                 return;
31252             }
31253             
31254             if(!match){
31255                 filterPattern(box, length - 1);
31256                 return;
31257             }
31258                 
31259             queue.push(pattern);
31260
31261             box = box.slice(length, box.length);
31262
31263             filterPattern(box, 4);
31264
31265             return;
31266             
31267         }
31268         
31269         Roo.each(boxes, function(box, k){
31270             
31271             if(!box.length){
31272                 return;
31273             }
31274             
31275             if(box.length == 1){
31276                 queue.push(box);
31277                 return;
31278             }
31279             
31280             filterPattern(box, 4);
31281             
31282         }, this);
31283         
31284         
31285         var prune = [];
31286         
31287         var pos = this.el.getBox(true);
31288         
31289         var minX = pos.x;
31290         
31291         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31292         
31293         var hit_end = false;
31294         
31295         Roo.each(queue, function(box){
31296             
31297             if(hit_end){
31298                 
31299                 Roo.each(box, function(b){
31300                 
31301                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31302                     b.el.hide();
31303
31304                 }, this);
31305
31306                 return;
31307             }
31308             
31309             var mx = 0;
31310             
31311             Roo.each(box, function(b){
31312                 
31313                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31314                 b.el.show();
31315
31316                 mx = Math.max(mx, b.x);
31317                 
31318             }, this);
31319             
31320             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31321             
31322             if(maxX < minX){
31323                 
31324                 Roo.each(box, function(b){
31325                 
31326                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31327                     b.el.hide();
31328                     
31329                 }, this);
31330                 
31331                 hit_end = true;
31332                 
31333                 return;
31334             }
31335             
31336             prune.push(box);
31337             
31338         }, this);
31339         
31340         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31341     },
31342     
31343     /** Sets position of item in DOM
31344     * @param {Element} item
31345     * @param {Number} x - horizontal position
31346     * @param {Number} y - vertical position
31347     * @param {Boolean} isInstant - disables transitions
31348     */
31349     _processVerticalLayoutQueue : function( queue, isInstant )
31350     {
31351         var pos = this.el.getBox(true);
31352         var x = pos.x;
31353         var y = pos.y;
31354         var maxY = [];
31355         
31356         for (var i = 0; i < this.cols; i++){
31357             maxY[i] = pos.y;
31358         }
31359         
31360         Roo.each(queue, function(box, k){
31361             
31362             var col = k % this.cols;
31363             
31364             Roo.each(box, function(b,kk){
31365                 
31366                 b.el.position('absolute');
31367                 
31368                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31369                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31370                 
31371                 if(b.size == 'md-left' || b.size == 'md-right'){
31372                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31373                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31374                 }
31375                 
31376                 b.el.setWidth(width);
31377                 b.el.setHeight(height);
31378                 // iframe?
31379                 b.el.select('iframe',true).setSize(width,height);
31380                 
31381             }, this);
31382             
31383             for (var i = 0; i < this.cols; i++){
31384                 
31385                 if(maxY[i] < maxY[col]){
31386                     col = i;
31387                     continue;
31388                 }
31389                 
31390                 col = Math.min(col, i);
31391                 
31392             }
31393             
31394             x = pos.x + col * (this.colWidth + this.padWidth);
31395             
31396             y = maxY[col];
31397             
31398             var positions = [];
31399             
31400             switch (box.length){
31401                 case 1 :
31402                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31403                     break;
31404                 case 2 :
31405                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31406                     break;
31407                 case 3 :
31408                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31409                     break;
31410                 case 4 :
31411                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31412                     break;
31413                 default :
31414                     break;
31415             }
31416             
31417             Roo.each(box, function(b,kk){
31418                 
31419                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31420                 
31421                 var sz = b.el.getSize();
31422                 
31423                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31424                 
31425             }, this);
31426             
31427         }, this);
31428         
31429         var mY = 0;
31430         
31431         for (var i = 0; i < this.cols; i++){
31432             mY = Math.max(mY, maxY[i]);
31433         }
31434         
31435         this.el.setHeight(mY - pos.y);
31436         
31437     },
31438     
31439 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31440 //    {
31441 //        var pos = this.el.getBox(true);
31442 //        var x = pos.x;
31443 //        var y = pos.y;
31444 //        var maxX = pos.right;
31445 //        
31446 //        var maxHeight = 0;
31447 //        
31448 //        Roo.each(items, function(item, k){
31449 //            
31450 //            var c = k % 2;
31451 //            
31452 //            item.el.position('absolute');
31453 //                
31454 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31455 //
31456 //            item.el.setWidth(width);
31457 //
31458 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31459 //
31460 //            item.el.setHeight(height);
31461 //            
31462 //            if(c == 0){
31463 //                item.el.setXY([x, y], isInstant ? false : true);
31464 //            } else {
31465 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31466 //            }
31467 //            
31468 //            y = y + height + this.alternativePadWidth;
31469 //            
31470 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31471 //            
31472 //        }, this);
31473 //        
31474 //        this.el.setHeight(maxHeight);
31475 //        
31476 //    },
31477     
31478     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31479     {
31480         var pos = this.el.getBox(true);
31481         
31482         var minX = pos.x;
31483         var minY = pos.y;
31484         
31485         var maxX = pos.right;
31486         
31487         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31488         
31489         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31490         
31491         Roo.each(queue, function(box, k){
31492             
31493             Roo.each(box, function(b, kk){
31494                 
31495                 b.el.position('absolute');
31496                 
31497                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31498                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31499                 
31500                 if(b.size == 'md-left' || b.size == 'md-right'){
31501                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31502                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31503                 }
31504                 
31505                 b.el.setWidth(width);
31506                 b.el.setHeight(height);
31507                 
31508             }, this);
31509             
31510             if(!box.length){
31511                 return;
31512             }
31513             
31514             var positions = [];
31515             
31516             switch (box.length){
31517                 case 1 :
31518                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31519                     break;
31520                 case 2 :
31521                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31522                     break;
31523                 case 3 :
31524                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31525                     break;
31526                 case 4 :
31527                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31528                     break;
31529                 default :
31530                     break;
31531             }
31532             
31533             Roo.each(box, function(b,kk){
31534                 
31535                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31536                 
31537                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31538                 
31539             }, this);
31540             
31541         }, this);
31542         
31543     },
31544     
31545     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31546     {
31547         Roo.each(eItems, function(b,k){
31548             
31549             b.size = (k == 0) ? 'sm' : 'xs';
31550             b.x = (k == 0) ? 2 : 1;
31551             b.y = (k == 0) ? 2 : 1;
31552             
31553             b.el.position('absolute');
31554             
31555             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31556                 
31557             b.el.setWidth(width);
31558             
31559             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31560             
31561             b.el.setHeight(height);
31562             
31563         }, this);
31564
31565         var positions = [];
31566         
31567         positions.push({
31568             x : maxX - this.unitWidth * 2 - this.gutter,
31569             y : minY
31570         });
31571         
31572         positions.push({
31573             x : maxX - this.unitWidth,
31574             y : minY + (this.unitWidth + this.gutter) * 2
31575         });
31576         
31577         positions.push({
31578             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31579             y : minY
31580         });
31581         
31582         Roo.each(eItems, function(b,k){
31583             
31584             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31585
31586         }, this);
31587         
31588     },
31589     
31590     getVerticalOneBoxColPositions : function(x, y, box)
31591     {
31592         var pos = [];
31593         
31594         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31595         
31596         if(box[0].size == 'md-left'){
31597             rand = 0;
31598         }
31599         
31600         if(box[0].size == 'md-right'){
31601             rand = 1;
31602         }
31603         
31604         pos.push({
31605             x : x + (this.unitWidth + this.gutter) * rand,
31606             y : y
31607         });
31608         
31609         return pos;
31610     },
31611     
31612     getVerticalTwoBoxColPositions : function(x, y, box)
31613     {
31614         var pos = [];
31615         
31616         if(box[0].size == 'xs'){
31617             
31618             pos.push({
31619                 x : x,
31620                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31621             });
31622
31623             pos.push({
31624                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31625                 y : y
31626             });
31627             
31628             return pos;
31629             
31630         }
31631         
31632         pos.push({
31633             x : x,
31634             y : y
31635         });
31636
31637         pos.push({
31638             x : x + (this.unitWidth + this.gutter) * 2,
31639             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31640         });
31641         
31642         return pos;
31643         
31644     },
31645     
31646     getVerticalThreeBoxColPositions : function(x, y, box)
31647     {
31648         var pos = [];
31649         
31650         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31651             
31652             pos.push({
31653                 x : x,
31654                 y : y
31655             });
31656
31657             pos.push({
31658                 x : x + (this.unitWidth + this.gutter) * 1,
31659                 y : y
31660             });
31661             
31662             pos.push({
31663                 x : x + (this.unitWidth + this.gutter) * 2,
31664                 y : y
31665             });
31666             
31667             return pos;
31668             
31669         }
31670         
31671         if(box[0].size == 'xs' && box[1].size == 'xs'){
31672             
31673             pos.push({
31674                 x : x,
31675                 y : y
31676             });
31677
31678             pos.push({
31679                 x : x,
31680                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31681             });
31682             
31683             pos.push({
31684                 x : x + (this.unitWidth + this.gutter) * 1,
31685                 y : y
31686             });
31687             
31688             return pos;
31689             
31690         }
31691         
31692         pos.push({
31693             x : x,
31694             y : y
31695         });
31696
31697         pos.push({
31698             x : x + (this.unitWidth + this.gutter) * 2,
31699             y : y
31700         });
31701
31702         pos.push({
31703             x : x + (this.unitWidth + this.gutter) * 2,
31704             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31705         });
31706             
31707         return pos;
31708         
31709     },
31710     
31711     getVerticalFourBoxColPositions : function(x, y, box)
31712     {
31713         var pos = [];
31714         
31715         if(box[0].size == 'xs'){
31716             
31717             pos.push({
31718                 x : x,
31719                 y : y
31720             });
31721
31722             pos.push({
31723                 x : x,
31724                 y : y + (this.unitHeight + this.gutter) * 1
31725             });
31726             
31727             pos.push({
31728                 x : x,
31729                 y : y + (this.unitHeight + this.gutter) * 2
31730             });
31731             
31732             pos.push({
31733                 x : x + (this.unitWidth + this.gutter) * 1,
31734                 y : y
31735             });
31736             
31737             return pos;
31738             
31739         }
31740         
31741         pos.push({
31742             x : x,
31743             y : y
31744         });
31745
31746         pos.push({
31747             x : x + (this.unitWidth + this.gutter) * 2,
31748             y : y
31749         });
31750
31751         pos.push({
31752             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31753             y : y + (this.unitHeight + this.gutter) * 1
31754         });
31755
31756         pos.push({
31757             x : x + (this.unitWidth + this.gutter) * 2,
31758             y : y + (this.unitWidth + this.gutter) * 2
31759         });
31760
31761         return pos;
31762         
31763     },
31764     
31765     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31766     {
31767         var pos = [];
31768         
31769         if(box[0].size == 'md-left'){
31770             pos.push({
31771                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31772                 y : minY
31773             });
31774             
31775             return pos;
31776         }
31777         
31778         if(box[0].size == 'md-right'){
31779             pos.push({
31780                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31781                 y : minY + (this.unitWidth + this.gutter) * 1
31782             });
31783             
31784             return pos;
31785         }
31786         
31787         var rand = Math.floor(Math.random() * (4 - box[0].y));
31788         
31789         pos.push({
31790             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31791             y : minY + (this.unitWidth + this.gutter) * rand
31792         });
31793         
31794         return pos;
31795         
31796     },
31797     
31798     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31799     {
31800         var pos = [];
31801         
31802         if(box[0].size == 'xs'){
31803             
31804             pos.push({
31805                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31806                 y : minY
31807             });
31808
31809             pos.push({
31810                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31811                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31812             });
31813             
31814             return pos;
31815             
31816         }
31817         
31818         pos.push({
31819             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31820             y : minY
31821         });
31822
31823         pos.push({
31824             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31825             y : minY + (this.unitWidth + this.gutter) * 2
31826         });
31827         
31828         return pos;
31829         
31830     },
31831     
31832     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31833     {
31834         var pos = [];
31835         
31836         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31837             
31838             pos.push({
31839                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31840                 y : minY
31841             });
31842
31843             pos.push({
31844                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31845                 y : minY + (this.unitWidth + this.gutter) * 1
31846             });
31847             
31848             pos.push({
31849                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31850                 y : minY + (this.unitWidth + this.gutter) * 2
31851             });
31852             
31853             return pos;
31854             
31855         }
31856         
31857         if(box[0].size == 'xs' && box[1].size == 'xs'){
31858             
31859             pos.push({
31860                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31861                 y : minY
31862             });
31863
31864             pos.push({
31865                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31866                 y : minY
31867             });
31868             
31869             pos.push({
31870                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31871                 y : minY + (this.unitWidth + this.gutter) * 1
31872             });
31873             
31874             return pos;
31875             
31876         }
31877         
31878         pos.push({
31879             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31880             y : minY
31881         });
31882
31883         pos.push({
31884             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31885             y : minY + (this.unitWidth + this.gutter) * 2
31886         });
31887
31888         pos.push({
31889             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31890             y : minY + (this.unitWidth + this.gutter) * 2
31891         });
31892             
31893         return pos;
31894         
31895     },
31896     
31897     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31898     {
31899         var pos = [];
31900         
31901         if(box[0].size == 'xs'){
31902             
31903             pos.push({
31904                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31905                 y : minY
31906             });
31907
31908             pos.push({
31909                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31915                 y : minY
31916             });
31917             
31918             pos.push({
31919                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31920                 y : minY + (this.unitWidth + this.gutter) * 1
31921             });
31922             
31923             return pos;
31924             
31925         }
31926         
31927         pos.push({
31928             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31929             y : minY
31930         });
31931         
31932         pos.push({
31933             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31934             y : minY + (this.unitWidth + this.gutter) * 2
31935         });
31936         
31937         pos.push({
31938             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].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) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31944             y : minY + (this.unitWidth + this.gutter) * 2
31945         });
31946
31947         return pos;
31948         
31949     },
31950     
31951     /**
31952     * remove a Masonry Brick
31953     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31954     */
31955     removeBrick : function(brick_id)
31956     {
31957         if (!brick_id) {
31958             return;
31959         }
31960         
31961         for (var i = 0; i<this.bricks.length; i++) {
31962             if (this.bricks[i].id == brick_id) {
31963                 this.bricks.splice(i,1);
31964                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31965                 this.initial();
31966             }
31967         }
31968     },
31969     
31970     /**
31971     * adds a Masonry Brick
31972     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31973     */
31974     addBrick : function(cfg)
31975     {
31976         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31977         //this.register(cn);
31978         cn.parentId = this.id;
31979         cn.onRender(this.el, null);
31980         return cn;
31981     },
31982     
31983     /**
31984     * register a Masonry Brick
31985     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31986     */
31987     
31988     register : function(brick)
31989     {
31990         this.bricks.push(brick);
31991         brick.masonryId = this.id;
31992     },
31993     
31994     /**
31995     * clear all the Masonry Brick
31996     */
31997     clearAll : function()
31998     {
31999         this.bricks = [];
32000         //this.getChildContainer().dom.innerHTML = "";
32001         this.el.dom.innerHTML = '';
32002     },
32003     
32004     getSelected : function()
32005     {
32006         if (!this.selectedBrick) {
32007             return false;
32008         }
32009         
32010         return this.selectedBrick;
32011     }
32012 });
32013
32014 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32015     
32016     groups: {},
32017      /**
32018     * register a Masonry Layout
32019     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32020     */
32021     
32022     register : function(layout)
32023     {
32024         this.groups[layout.id] = layout;
32025     },
32026     /**
32027     * fetch a  Masonry Layout based on the masonry layout ID
32028     * @param {string} the masonry layout to add
32029     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32030     */
32031     
32032     get: function(layout_id) {
32033         if (typeof(this.groups[layout_id]) == 'undefined') {
32034             return false;
32035         }
32036         return this.groups[layout_id] ;
32037     }
32038     
32039     
32040     
32041 });
32042
32043  
32044
32045  /**
32046  *
32047  * This is based on 
32048  * http://masonry.desandro.com
32049  *
32050  * The idea is to render all the bricks based on vertical width...
32051  *
32052  * The original code extends 'outlayer' - we might need to use that....
32053  * 
32054  */
32055
32056
32057 /**
32058  * @class Roo.bootstrap.LayoutMasonryAuto
32059  * @extends Roo.bootstrap.Component
32060  * Bootstrap Layout Masonry class
32061  * 
32062  * @constructor
32063  * Create a new Element
32064  * @param {Object} config The config object
32065  */
32066
32067 Roo.bootstrap.LayoutMasonryAuto = function(config){
32068     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32069 };
32070
32071 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32072     
32073       /**
32074      * @cfg {Boolean} isFitWidth  - resize the width..
32075      */   
32076     isFitWidth : false,  // options..
32077     /**
32078      * @cfg {Boolean} isOriginLeft = left align?
32079      */   
32080     isOriginLeft : true,
32081     /**
32082      * @cfg {Boolean} isOriginTop = top align?
32083      */   
32084     isOriginTop : false,
32085     /**
32086      * @cfg {Boolean} isLayoutInstant = no animation?
32087      */   
32088     isLayoutInstant : false, // needed?
32089     /**
32090      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32091      */   
32092     isResizingContainer : true,
32093     /**
32094      * @cfg {Number} columnWidth  width of the columns 
32095      */   
32096     
32097     columnWidth : 0,
32098     
32099     /**
32100      * @cfg {Number} maxCols maximum number of columns
32101      */   
32102     
32103     maxCols: 0,
32104     /**
32105      * @cfg {Number} padHeight padding below box..
32106      */   
32107     
32108     padHeight : 10, 
32109     
32110     /**
32111      * @cfg {Boolean} isAutoInitial defalut true
32112      */   
32113     
32114     isAutoInitial : true, 
32115     
32116     // private?
32117     gutter : 0,
32118     
32119     containerWidth: 0,
32120     initialColumnWidth : 0,
32121     currentSize : null,
32122     
32123     colYs : null, // array.
32124     maxY : 0,
32125     padWidth: 10,
32126     
32127     
32128     tag: 'div',
32129     cls: '',
32130     bricks: null, //CompositeElement
32131     cols : 0, // array?
32132     // element : null, // wrapped now this.el
32133     _isLayoutInited : null, 
32134     
32135     
32136     getAutoCreate : function(){
32137         
32138         var cfg = {
32139             tag: this.tag,
32140             cls: 'blog-masonary-wrapper ' + this.cls,
32141             cn : {
32142                 cls : 'mas-boxes masonary'
32143             }
32144         };
32145         
32146         return cfg;
32147     },
32148     
32149     getChildContainer: function( )
32150     {
32151         if (this.boxesEl) {
32152             return this.boxesEl;
32153         }
32154         
32155         this.boxesEl = this.el.select('.mas-boxes').first();
32156         
32157         return this.boxesEl;
32158     },
32159     
32160     
32161     initEvents : function()
32162     {
32163         var _this = this;
32164         
32165         if(this.isAutoInitial){
32166             Roo.log('hook children rendered');
32167             this.on('childrenrendered', function() {
32168                 Roo.log('children rendered');
32169                 _this.initial();
32170             } ,this);
32171         }
32172         
32173     },
32174     
32175     initial : function()
32176     {
32177         this.reloadItems();
32178
32179         this.currentSize = this.el.getBox(true);
32180
32181         /// was window resize... - let's see if this works..
32182         Roo.EventManager.onWindowResize(this.resize, this); 
32183
32184         if(!this.isAutoInitial){
32185             this.layout();
32186             return;
32187         }
32188         
32189         this.layout.defer(500,this);
32190     },
32191     
32192     reloadItems: function()
32193     {
32194         this.bricks = this.el.select('.masonry-brick', true);
32195         
32196         this.bricks.each(function(b) {
32197             //Roo.log(b.getSize());
32198             if (!b.attr('originalwidth')) {
32199                 b.attr('originalwidth',  b.getSize().width);
32200             }
32201             
32202         });
32203         
32204         Roo.log(this.bricks.elements.length);
32205     },
32206     
32207     resize : function()
32208     {
32209         Roo.log('resize');
32210         var cs = this.el.getBox(true);
32211         
32212         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32213             Roo.log("no change in with or X");
32214             return;
32215         }
32216         this.currentSize = cs;
32217         this.layout();
32218     },
32219     
32220     layout : function()
32221     {
32222          Roo.log('layout');
32223         this._resetLayout();
32224         //this._manageStamps();
32225       
32226         // don't animate first layout
32227         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32228         this.layoutItems( isInstant );
32229       
32230         // flag for initalized
32231         this._isLayoutInited = true;
32232     },
32233     
32234     layoutItems : function( isInstant )
32235     {
32236         //var items = this._getItemsForLayout( this.items );
32237         // original code supports filtering layout items.. we just ignore it..
32238         
32239         this._layoutItems( this.bricks , isInstant );
32240       
32241         this._postLayout();
32242     },
32243     _layoutItems : function ( items , isInstant)
32244     {
32245        //this.fireEvent( 'layout', this, items );
32246     
32247
32248         if ( !items || !items.elements.length ) {
32249           // no items, emit event with empty array
32250             return;
32251         }
32252
32253         var queue = [];
32254         items.each(function(item) {
32255             Roo.log("layout item");
32256             Roo.log(item);
32257             // get x/y object from method
32258             var position = this._getItemLayoutPosition( item );
32259             // enqueue
32260             position.item = item;
32261             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32262             queue.push( position );
32263         }, this);
32264       
32265         this._processLayoutQueue( queue );
32266     },
32267     /** Sets position of item in DOM
32268     * @param {Element} item
32269     * @param {Number} x - horizontal position
32270     * @param {Number} y - vertical position
32271     * @param {Boolean} isInstant - disables transitions
32272     */
32273     _processLayoutQueue : function( queue )
32274     {
32275         for ( var i=0, len = queue.length; i < len; i++ ) {
32276             var obj = queue[i];
32277             obj.item.position('absolute');
32278             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32279         }
32280     },
32281       
32282     
32283     /**
32284     * Any logic you want to do after each layout,
32285     * i.e. size the container
32286     */
32287     _postLayout : function()
32288     {
32289         this.resizeContainer();
32290     },
32291     
32292     resizeContainer : function()
32293     {
32294         if ( !this.isResizingContainer ) {
32295             return;
32296         }
32297         var size = this._getContainerSize();
32298         if ( size ) {
32299             this.el.setSize(size.width,size.height);
32300             this.boxesEl.setSize(size.width,size.height);
32301         }
32302     },
32303     
32304     
32305     
32306     _resetLayout : function()
32307     {
32308         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32309         this.colWidth = this.el.getWidth();
32310         //this.gutter = this.el.getWidth(); 
32311         
32312         this.measureColumns();
32313
32314         // reset column Y
32315         var i = this.cols;
32316         this.colYs = [];
32317         while (i--) {
32318             this.colYs.push( 0 );
32319         }
32320     
32321         this.maxY = 0;
32322     },
32323
32324     measureColumns : function()
32325     {
32326         this.getContainerWidth();
32327       // if columnWidth is 0, default to outerWidth of first item
32328         if ( !this.columnWidth ) {
32329             var firstItem = this.bricks.first();
32330             Roo.log(firstItem);
32331             this.columnWidth  = this.containerWidth;
32332             if (firstItem && firstItem.attr('originalwidth') ) {
32333                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32334             }
32335             // columnWidth fall back to item of first element
32336             Roo.log("set column width?");
32337                         this.initialColumnWidth = this.columnWidth  ;
32338
32339             // if first elem has no width, default to size of container
32340             
32341         }
32342         
32343         
32344         if (this.initialColumnWidth) {
32345             this.columnWidth = this.initialColumnWidth;
32346         }
32347         
32348         
32349             
32350         // column width is fixed at the top - however if container width get's smaller we should
32351         // reduce it...
32352         
32353         // this bit calcs how man columns..
32354             
32355         var columnWidth = this.columnWidth += this.gutter;
32356       
32357         // calculate columns
32358         var containerWidth = this.containerWidth + this.gutter;
32359         
32360         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32361         // fix rounding errors, typically with gutters
32362         var excess = columnWidth - containerWidth % columnWidth;
32363         
32364         
32365         // if overshoot is less than a pixel, round up, otherwise floor it
32366         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32367         cols = Math[ mathMethod ]( cols );
32368         this.cols = Math.max( cols, 1 );
32369         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32370         
32371          // padding positioning..
32372         var totalColWidth = this.cols * this.columnWidth;
32373         var padavail = this.containerWidth - totalColWidth;
32374         // so for 2 columns - we need 3 'pads'
32375         
32376         var padNeeded = (1+this.cols) * this.padWidth;
32377         
32378         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32379         
32380         this.columnWidth += padExtra
32381         //this.padWidth = Math.floor(padavail /  ( this.cols));
32382         
32383         // adjust colum width so that padding is fixed??
32384         
32385         // we have 3 columns ... total = width * 3
32386         // we have X left over... that should be used by 
32387         
32388         //if (this.expandC) {
32389             
32390         //}
32391         
32392         
32393         
32394     },
32395     
32396     getContainerWidth : function()
32397     {
32398        /* // container is parent if fit width
32399         var container = this.isFitWidth ? this.element.parentNode : this.element;
32400         // check that this.size and size are there
32401         // IE8 triggers resize on body size change, so they might not be
32402         
32403         var size = getSize( container );  //FIXME
32404         this.containerWidth = size && size.innerWidth; //FIXME
32405         */
32406          
32407         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32408         
32409     },
32410     
32411     _getItemLayoutPosition : function( item )  // what is item?
32412     {
32413         // we resize the item to our columnWidth..
32414       
32415         item.setWidth(this.columnWidth);
32416         item.autoBoxAdjust  = false;
32417         
32418         var sz = item.getSize();
32419  
32420         // how many columns does this brick span
32421         var remainder = this.containerWidth % this.columnWidth;
32422         
32423         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32424         // round if off by 1 pixel, otherwise use ceil
32425         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32426         colSpan = Math.min( colSpan, this.cols );
32427         
32428         // normally this should be '1' as we dont' currently allow multi width columns..
32429         
32430         var colGroup = this._getColGroup( colSpan );
32431         // get the minimum Y value from the columns
32432         var minimumY = Math.min.apply( Math, colGroup );
32433         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32434         
32435         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32436          
32437         // position the brick
32438         var position = {
32439             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32440             y: this.currentSize.y + minimumY + this.padHeight
32441         };
32442         
32443         Roo.log(position);
32444         // apply setHeight to necessary columns
32445         var setHeight = minimumY + sz.height + this.padHeight;
32446         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32447         
32448         var setSpan = this.cols + 1 - colGroup.length;
32449         for ( var i = 0; i < setSpan; i++ ) {
32450           this.colYs[ shortColIndex + i ] = setHeight ;
32451         }
32452       
32453         return position;
32454     },
32455     
32456     /**
32457      * @param {Number} colSpan - number of columns the element spans
32458      * @returns {Array} colGroup
32459      */
32460     _getColGroup : function( colSpan )
32461     {
32462         if ( colSpan < 2 ) {
32463           // if brick spans only one column, use all the column Ys
32464           return this.colYs;
32465         }
32466       
32467         var colGroup = [];
32468         // how many different places could this brick fit horizontally
32469         var groupCount = this.cols + 1 - colSpan;
32470         // for each group potential horizontal position
32471         for ( var i = 0; i < groupCount; i++ ) {
32472           // make an array of colY values for that one group
32473           var groupColYs = this.colYs.slice( i, i + colSpan );
32474           // and get the max value of the array
32475           colGroup[i] = Math.max.apply( Math, groupColYs );
32476         }
32477         return colGroup;
32478     },
32479     /*
32480     _manageStamp : function( stamp )
32481     {
32482         var stampSize =  stamp.getSize();
32483         var offset = stamp.getBox();
32484         // get the columns that this stamp affects
32485         var firstX = this.isOriginLeft ? offset.x : offset.right;
32486         var lastX = firstX + stampSize.width;
32487         var firstCol = Math.floor( firstX / this.columnWidth );
32488         firstCol = Math.max( 0, firstCol );
32489         
32490         var lastCol = Math.floor( lastX / this.columnWidth );
32491         // lastCol should not go over if multiple of columnWidth #425
32492         lastCol -= lastX % this.columnWidth ? 0 : 1;
32493         lastCol = Math.min( this.cols - 1, lastCol );
32494         
32495         // set colYs to bottom of the stamp
32496         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32497             stampSize.height;
32498             
32499         for ( var i = firstCol; i <= lastCol; i++ ) {
32500           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32501         }
32502     },
32503     */
32504     
32505     _getContainerSize : function()
32506     {
32507         this.maxY = Math.max.apply( Math, this.colYs );
32508         var size = {
32509             height: this.maxY
32510         };
32511       
32512         if ( this.isFitWidth ) {
32513             size.width = this._getContainerFitWidth();
32514         }
32515       
32516         return size;
32517     },
32518     
32519     _getContainerFitWidth : function()
32520     {
32521         var unusedCols = 0;
32522         // count unused columns
32523         var i = this.cols;
32524         while ( --i ) {
32525           if ( this.colYs[i] !== 0 ) {
32526             break;
32527           }
32528           unusedCols++;
32529         }
32530         // fit container to columns that have been used
32531         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32532     },
32533     
32534     needsResizeLayout : function()
32535     {
32536         var previousWidth = this.containerWidth;
32537         this.getContainerWidth();
32538         return previousWidth !== this.containerWidth;
32539     }
32540  
32541 });
32542
32543  
32544
32545  /*
32546  * - LGPL
32547  *
32548  * element
32549  * 
32550  */
32551
32552 /**
32553  * @class Roo.bootstrap.MasonryBrick
32554  * @extends Roo.bootstrap.Component
32555  * Bootstrap MasonryBrick class
32556  * 
32557  * @constructor
32558  * Create a new MasonryBrick
32559  * @param {Object} config The config object
32560  */
32561
32562 Roo.bootstrap.MasonryBrick = function(config){
32563     
32564     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32565     
32566     Roo.bootstrap.MasonryBrick.register(this);
32567     
32568     this.addEvents({
32569         // raw events
32570         /**
32571          * @event click
32572          * When a MasonryBrick is clcik
32573          * @param {Roo.bootstrap.MasonryBrick} this
32574          * @param {Roo.EventObject} e
32575          */
32576         "click" : true
32577     });
32578 };
32579
32580 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32581     
32582     /**
32583      * @cfg {String} title
32584      */   
32585     title : '',
32586     /**
32587      * @cfg {String} html
32588      */   
32589     html : '',
32590     /**
32591      * @cfg {String} bgimage
32592      */   
32593     bgimage : '',
32594     /**
32595      * @cfg {String} videourl
32596      */   
32597     videourl : '',
32598     /**
32599      * @cfg {String} cls
32600      */   
32601     cls : '',
32602     /**
32603      * @cfg {String} href
32604      */   
32605     href : '',
32606     /**
32607      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32608      */   
32609     size : 'xs',
32610     
32611     /**
32612      * @cfg {String} placetitle (center|bottom)
32613      */   
32614     placetitle : '',
32615     
32616     /**
32617      * @cfg {Boolean} isFitContainer defalut true
32618      */   
32619     isFitContainer : true, 
32620     
32621     /**
32622      * @cfg {Boolean} preventDefault defalut false
32623      */   
32624     preventDefault : false, 
32625     
32626     /**
32627      * @cfg {Boolean} inverse defalut false
32628      */   
32629     maskInverse : false, 
32630     
32631     getAutoCreate : function()
32632     {
32633         if(!this.isFitContainer){
32634             return this.getSplitAutoCreate();
32635         }
32636         
32637         var cls = 'masonry-brick masonry-brick-full';
32638         
32639         if(this.href.length){
32640             cls += ' masonry-brick-link';
32641         }
32642         
32643         if(this.bgimage.length){
32644             cls += ' masonry-brick-image';
32645         }
32646         
32647         if(this.maskInverse){
32648             cls += ' mask-inverse';
32649         }
32650         
32651         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32652             cls += ' enable-mask';
32653         }
32654         
32655         if(this.size){
32656             cls += ' masonry-' + this.size + '-brick';
32657         }
32658         
32659         if(this.placetitle.length){
32660             
32661             switch (this.placetitle) {
32662                 case 'center' :
32663                     cls += ' masonry-center-title';
32664                     break;
32665                 case 'bottom' :
32666                     cls += ' masonry-bottom-title';
32667                     break;
32668                 default:
32669                     break;
32670             }
32671             
32672         } else {
32673             if(!this.html.length && !this.bgimage.length){
32674                 cls += ' masonry-center-title';
32675             }
32676
32677             if(!this.html.length && this.bgimage.length){
32678                 cls += ' masonry-bottom-title';
32679             }
32680         }
32681         
32682         if(this.cls){
32683             cls += ' ' + this.cls;
32684         }
32685         
32686         var cfg = {
32687             tag: (this.href.length) ? 'a' : 'div',
32688             cls: cls,
32689             cn: [
32690                 {
32691                     tag: 'div',
32692                     cls: 'masonry-brick-mask'
32693                 },
32694                 {
32695                     tag: 'div',
32696                     cls: 'masonry-brick-paragraph',
32697                     cn: []
32698                 }
32699             ]
32700         };
32701         
32702         if(this.href.length){
32703             cfg.href = this.href;
32704         }
32705         
32706         var cn = cfg.cn[1].cn;
32707         
32708         if(this.title.length){
32709             cn.push({
32710                 tag: 'h4',
32711                 cls: 'masonry-brick-title',
32712                 html: this.title
32713             });
32714         }
32715         
32716         if(this.html.length){
32717             cn.push({
32718                 tag: 'p',
32719                 cls: 'masonry-brick-text',
32720                 html: this.html
32721             });
32722         }
32723         
32724         if (!this.title.length && !this.html.length) {
32725             cfg.cn[1].cls += ' hide';
32726         }
32727         
32728         if(this.bgimage.length){
32729             cfg.cn.push({
32730                 tag: 'img',
32731                 cls: 'masonry-brick-image-view',
32732                 src: this.bgimage
32733             });
32734         }
32735         
32736         if(this.videourl.length){
32737             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32738             // youtube support only?
32739             cfg.cn.push({
32740                 tag: 'iframe',
32741                 cls: 'masonry-brick-image-view',
32742                 src: vurl,
32743                 frameborder : 0,
32744                 allowfullscreen : true
32745             });
32746         }
32747         
32748         return cfg;
32749         
32750     },
32751     
32752     getSplitAutoCreate : function()
32753     {
32754         var cls = 'masonry-brick masonry-brick-split';
32755         
32756         if(this.href.length){
32757             cls += ' masonry-brick-link';
32758         }
32759         
32760         if(this.bgimage.length){
32761             cls += ' masonry-brick-image';
32762         }
32763         
32764         if(this.size){
32765             cls += ' masonry-' + this.size + '-brick';
32766         }
32767         
32768         switch (this.placetitle) {
32769             case 'center' :
32770                 cls += ' masonry-center-title';
32771                 break;
32772             case 'bottom' :
32773                 cls += ' masonry-bottom-title';
32774                 break;
32775             default:
32776                 if(!this.bgimage.length){
32777                     cls += ' masonry-center-title';
32778                 }
32779
32780                 if(this.bgimage.length){
32781                     cls += ' masonry-bottom-title';
32782                 }
32783                 break;
32784         }
32785         
32786         if(this.cls){
32787             cls += ' ' + this.cls;
32788         }
32789         
32790         var cfg = {
32791             tag: (this.href.length) ? 'a' : 'div',
32792             cls: cls,
32793             cn: [
32794                 {
32795                     tag: 'div',
32796                     cls: 'masonry-brick-split-head',
32797                     cn: [
32798                         {
32799                             tag: 'div',
32800                             cls: 'masonry-brick-paragraph',
32801                             cn: []
32802                         }
32803                     ]
32804                 },
32805                 {
32806                     tag: 'div',
32807                     cls: 'masonry-brick-split-body',
32808                     cn: []
32809                 }
32810             ]
32811         };
32812         
32813         if(this.href.length){
32814             cfg.href = this.href;
32815         }
32816         
32817         if(this.title.length){
32818             cfg.cn[0].cn[0].cn.push({
32819                 tag: 'h4',
32820                 cls: 'masonry-brick-title',
32821                 html: this.title
32822             });
32823         }
32824         
32825         if(this.html.length){
32826             cfg.cn[1].cn.push({
32827                 tag: 'p',
32828                 cls: 'masonry-brick-text',
32829                 html: this.html
32830             });
32831         }
32832
32833         if(this.bgimage.length){
32834             cfg.cn[0].cn.push({
32835                 tag: 'img',
32836                 cls: 'masonry-brick-image-view',
32837                 src: this.bgimage
32838             });
32839         }
32840         
32841         if(this.videourl.length){
32842             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32843             // youtube support only?
32844             cfg.cn[0].cn.cn.push({
32845                 tag: 'iframe',
32846                 cls: 'masonry-brick-image-view',
32847                 src: vurl,
32848                 frameborder : 0,
32849                 allowfullscreen : true
32850             });
32851         }
32852         
32853         return cfg;
32854     },
32855     
32856     initEvents: function() 
32857     {
32858         switch (this.size) {
32859             case 'xs' :
32860                 this.x = 1;
32861                 this.y = 1;
32862                 break;
32863             case 'sm' :
32864                 this.x = 2;
32865                 this.y = 2;
32866                 break;
32867             case 'md' :
32868             case 'md-left' :
32869             case 'md-right' :
32870                 this.x = 3;
32871                 this.y = 3;
32872                 break;
32873             case 'tall' :
32874                 this.x = 2;
32875                 this.y = 3;
32876                 break;
32877             case 'wide' :
32878                 this.x = 3;
32879                 this.y = 2;
32880                 break;
32881             case 'wide-thin' :
32882                 this.x = 3;
32883                 this.y = 1;
32884                 break;
32885                         
32886             default :
32887                 break;
32888         }
32889         
32890         if(Roo.isTouch){
32891             this.el.on('touchstart', this.onTouchStart, this);
32892             this.el.on('touchmove', this.onTouchMove, this);
32893             this.el.on('touchend', this.onTouchEnd, this);
32894             this.el.on('contextmenu', this.onContextMenu, this);
32895         } else {
32896             this.el.on('mouseenter'  ,this.enter, this);
32897             this.el.on('mouseleave', this.leave, this);
32898             this.el.on('click', this.onClick, this);
32899         }
32900         
32901         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32902             this.parent().bricks.push(this);   
32903         }
32904         
32905     },
32906     
32907     onClick: function(e, el)
32908     {
32909         var time = this.endTimer - this.startTimer;
32910         // Roo.log(e.preventDefault());
32911         if(Roo.isTouch){
32912             if(time > 1000){
32913                 e.preventDefault();
32914                 return;
32915             }
32916         }
32917         
32918         if(!this.preventDefault){
32919             return;
32920         }
32921         
32922         e.preventDefault();
32923         
32924         if (this.activeClass != '') {
32925             this.selectBrick();
32926         }
32927         
32928         this.fireEvent('click', this, e);
32929     },
32930     
32931     enter: function(e, el)
32932     {
32933         e.preventDefault();
32934         
32935         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32936             return;
32937         }
32938         
32939         if(this.bgimage.length && this.html.length){
32940             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32941         }
32942     },
32943     
32944     leave: function(e, el)
32945     {
32946         e.preventDefault();
32947         
32948         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32949             return;
32950         }
32951         
32952         if(this.bgimage.length && this.html.length){
32953             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32954         }
32955     },
32956     
32957     onTouchStart: function(e, el)
32958     {
32959 //        e.preventDefault();
32960         
32961         this.touchmoved = false;
32962         
32963         if(!this.isFitContainer){
32964             return;
32965         }
32966         
32967         if(!this.bgimage.length || !this.html.length){
32968             return;
32969         }
32970         
32971         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32972         
32973         this.timer = new Date().getTime();
32974         
32975     },
32976     
32977     onTouchMove: function(e, el)
32978     {
32979         this.touchmoved = true;
32980     },
32981     
32982     onContextMenu : function(e,el)
32983     {
32984         e.preventDefault();
32985         e.stopPropagation();
32986         return false;
32987     },
32988     
32989     onTouchEnd: function(e, el)
32990     {
32991 //        e.preventDefault();
32992         
32993         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32994         
32995             this.leave(e,el);
32996             
32997             return;
32998         }
32999         
33000         if(!this.bgimage.length || !this.html.length){
33001             
33002             if(this.href.length){
33003                 window.location.href = this.href;
33004             }
33005             
33006             return;
33007         }
33008         
33009         if(!this.isFitContainer){
33010             return;
33011         }
33012         
33013         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33014         
33015         window.location.href = this.href;
33016     },
33017     
33018     //selection on single brick only
33019     selectBrick : function() {
33020         
33021         if (!this.parentId) {
33022             return;
33023         }
33024         
33025         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33026         var index = m.selectedBrick.indexOf(this.id);
33027         
33028         if ( index > -1) {
33029             m.selectedBrick.splice(index,1);
33030             this.el.removeClass(this.activeClass);
33031             return;
33032         }
33033         
33034         for(var i = 0; i < m.selectedBrick.length; i++) {
33035             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33036             b.el.removeClass(b.activeClass);
33037         }
33038         
33039         m.selectedBrick = [];
33040         
33041         m.selectedBrick.push(this.id);
33042         this.el.addClass(this.activeClass);
33043         return;
33044     },
33045     
33046     isSelected : function(){
33047         return this.el.hasClass(this.activeClass);
33048         
33049     }
33050 });
33051
33052 Roo.apply(Roo.bootstrap.MasonryBrick, {
33053     
33054     //groups: {},
33055     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33056      /**
33057     * register a Masonry Brick
33058     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33059     */
33060     
33061     register : function(brick)
33062     {
33063         //this.groups[brick.id] = brick;
33064         this.groups.add(brick.id, brick);
33065     },
33066     /**
33067     * fetch a  masonry brick based on the masonry brick ID
33068     * @param {string} the masonry brick to add
33069     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33070     */
33071     
33072     get: function(brick_id) 
33073     {
33074         // if (typeof(this.groups[brick_id]) == 'undefined') {
33075         //     return false;
33076         // }
33077         // return this.groups[brick_id] ;
33078         
33079         if(this.groups.key(brick_id)) {
33080             return this.groups.key(brick_id);
33081         }
33082         
33083         return false;
33084     }
33085     
33086     
33087     
33088 });
33089
33090  /*
33091  * - LGPL
33092  *
33093  * element
33094  * 
33095  */
33096
33097 /**
33098  * @class Roo.bootstrap.Brick
33099  * @extends Roo.bootstrap.Component
33100  * Bootstrap Brick class
33101  * 
33102  * @constructor
33103  * Create a new Brick
33104  * @param {Object} config The config object
33105  */
33106
33107 Roo.bootstrap.Brick = function(config){
33108     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33109     
33110     this.addEvents({
33111         // raw events
33112         /**
33113          * @event click
33114          * When a Brick is click
33115          * @param {Roo.bootstrap.Brick} this
33116          * @param {Roo.EventObject} e
33117          */
33118         "click" : true
33119     });
33120 };
33121
33122 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33123     
33124     /**
33125      * @cfg {String} title
33126      */   
33127     title : '',
33128     /**
33129      * @cfg {String} html
33130      */   
33131     html : '',
33132     /**
33133      * @cfg {String} bgimage
33134      */   
33135     bgimage : '',
33136     /**
33137      * @cfg {String} cls
33138      */   
33139     cls : '',
33140     /**
33141      * @cfg {String} href
33142      */   
33143     href : '',
33144     /**
33145      * @cfg {String} video
33146      */   
33147     video : '',
33148     /**
33149      * @cfg {Boolean} square
33150      */   
33151     square : true,
33152     
33153     getAutoCreate : function()
33154     {
33155         var cls = 'roo-brick';
33156         
33157         if(this.href.length){
33158             cls += ' roo-brick-link';
33159         }
33160         
33161         if(this.bgimage.length){
33162             cls += ' roo-brick-image';
33163         }
33164         
33165         if(!this.html.length && !this.bgimage.length){
33166             cls += ' roo-brick-center-title';
33167         }
33168         
33169         if(!this.html.length && this.bgimage.length){
33170             cls += ' roo-brick-bottom-title';
33171         }
33172         
33173         if(this.cls){
33174             cls += ' ' + this.cls;
33175         }
33176         
33177         var cfg = {
33178             tag: (this.href.length) ? 'a' : 'div',
33179             cls: cls,
33180             cn: [
33181                 {
33182                     tag: 'div',
33183                     cls: 'roo-brick-paragraph',
33184                     cn: []
33185                 }
33186             ]
33187         };
33188         
33189         if(this.href.length){
33190             cfg.href = this.href;
33191         }
33192         
33193         var cn = cfg.cn[0].cn;
33194         
33195         if(this.title.length){
33196             cn.push({
33197                 tag: 'h4',
33198                 cls: 'roo-brick-title',
33199                 html: this.title
33200             });
33201         }
33202         
33203         if(this.html.length){
33204             cn.push({
33205                 tag: 'p',
33206                 cls: 'roo-brick-text',
33207                 html: this.html
33208             });
33209         } else {
33210             cn.cls += ' hide';
33211         }
33212         
33213         if(this.bgimage.length){
33214             cfg.cn.push({
33215                 tag: 'img',
33216                 cls: 'roo-brick-image-view',
33217                 src: this.bgimage
33218             });
33219         }
33220         
33221         return cfg;
33222     },
33223     
33224     initEvents: function() 
33225     {
33226         if(this.title.length || this.html.length){
33227             this.el.on('mouseenter'  ,this.enter, this);
33228             this.el.on('mouseleave', this.leave, this);
33229         }
33230         
33231         Roo.EventManager.onWindowResize(this.resize, this); 
33232         
33233         if(this.bgimage.length){
33234             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33235             this.imageEl.on('load', this.onImageLoad, this);
33236             return;
33237         }
33238         
33239         this.resize();
33240     },
33241     
33242     onImageLoad : function()
33243     {
33244         this.resize();
33245     },
33246     
33247     resize : function()
33248     {
33249         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33250         
33251         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33252         
33253         if(this.bgimage.length){
33254             var image = this.el.select('.roo-brick-image-view', true).first();
33255             
33256             image.setWidth(paragraph.getWidth());
33257             
33258             if(this.square){
33259                 image.setHeight(paragraph.getWidth());
33260             }
33261             
33262             this.el.setHeight(image.getHeight());
33263             paragraph.setHeight(image.getHeight());
33264             
33265         }
33266         
33267     },
33268     
33269     enter: function(e, el)
33270     {
33271         e.preventDefault();
33272         
33273         if(this.bgimage.length){
33274             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33275             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33276         }
33277     },
33278     
33279     leave: function(e, el)
33280     {
33281         e.preventDefault();
33282         
33283         if(this.bgimage.length){
33284             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33285             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33286         }
33287     }
33288     
33289 });
33290
33291  
33292
33293  /*
33294  * - LGPL
33295  *
33296  * Number field 
33297  */
33298
33299 /**
33300  * @class Roo.bootstrap.NumberField
33301  * @extends Roo.bootstrap.Input
33302  * Bootstrap NumberField class
33303  * 
33304  * 
33305  * 
33306  * 
33307  * @constructor
33308  * Create a new NumberField
33309  * @param {Object} config The config object
33310  */
33311
33312 Roo.bootstrap.NumberField = function(config){
33313     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33314 };
33315
33316 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33317     
33318     /**
33319      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33320      */
33321     allowDecimals : true,
33322     /**
33323      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33324      */
33325     decimalSeparator : ".",
33326     /**
33327      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33328      */
33329     decimalPrecision : 2,
33330     /**
33331      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33332      */
33333     allowNegative : true,
33334     
33335     /**
33336      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33337      */
33338     allowZero: true,
33339     /**
33340      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33341      */
33342     minValue : Number.NEGATIVE_INFINITY,
33343     /**
33344      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33345      */
33346     maxValue : Number.MAX_VALUE,
33347     /**
33348      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33349      */
33350     minText : "The minimum value for this field is {0}",
33351     /**
33352      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33353      */
33354     maxText : "The maximum value for this field is {0}",
33355     /**
33356      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33357      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33358      */
33359     nanText : "{0} is not a valid number",
33360     /**
33361      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33362      */
33363     thousandsDelimiter : false,
33364     /**
33365      * @cfg {String} valueAlign alignment of value
33366      */
33367     valueAlign : "left",
33368
33369     getAutoCreate : function()
33370     {
33371         var hiddenInput = {
33372             tag: 'input',
33373             type: 'hidden',
33374             id: Roo.id(),
33375             cls: 'hidden-number-input'
33376         };
33377         
33378         if (this.name) {
33379             hiddenInput.name = this.name;
33380         }
33381         
33382         this.name = '';
33383         
33384         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33385         
33386         this.name = hiddenInput.name;
33387         
33388         if(cfg.cn.length > 0) {
33389             cfg.cn.push(hiddenInput);
33390         }
33391         
33392         return cfg;
33393     },
33394
33395     // private
33396     initEvents : function()
33397     {   
33398         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33399         
33400         var allowed = "0123456789";
33401         
33402         if(this.allowDecimals){
33403             allowed += this.decimalSeparator;
33404         }
33405         
33406         if(this.allowNegative){
33407             allowed += "-";
33408         }
33409         
33410         if(this.thousandsDelimiter) {
33411             allowed += ",";
33412         }
33413         
33414         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33415         
33416         var keyPress = function(e){
33417             
33418             var k = e.getKey();
33419             
33420             var c = e.getCharCode();
33421             
33422             if(
33423                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33424                     allowed.indexOf(String.fromCharCode(c)) === -1
33425             ){
33426                 e.stopEvent();
33427                 return;
33428             }
33429             
33430             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33431                 return;
33432             }
33433             
33434             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33435                 e.stopEvent();
33436             }
33437         };
33438         
33439         this.el.on("keypress", keyPress, this);
33440     },
33441     
33442     validateValue : function(value)
33443     {
33444         
33445         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33446             return false;
33447         }
33448         
33449         var num = this.parseValue(value);
33450         
33451         if(isNaN(num)){
33452             this.markInvalid(String.format(this.nanText, value));
33453             return false;
33454         }
33455         
33456         if(num < this.minValue){
33457             this.markInvalid(String.format(this.minText, this.minValue));
33458             return false;
33459         }
33460         
33461         if(num > this.maxValue){
33462             this.markInvalid(String.format(this.maxText, this.maxValue));
33463             return false;
33464         }
33465         
33466         return true;
33467     },
33468
33469     getValue : function()
33470     {
33471         var v = this.hiddenEl().getValue();
33472         
33473         return this.fixPrecision(this.parseValue(v));
33474     },
33475
33476     parseValue : function(value)
33477     {
33478         if(this.thousandsDelimiter) {
33479             value += "";
33480             r = new RegExp(",", "g");
33481             value = value.replace(r, "");
33482         }
33483         
33484         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33485         return isNaN(value) ? '' : value;
33486     },
33487
33488     fixPrecision : function(value)
33489     {
33490         if(this.thousandsDelimiter) {
33491             value += "";
33492             r = new RegExp(",", "g");
33493             value = value.replace(r, "");
33494         }
33495         
33496         var nan = isNaN(value);
33497         
33498         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33499             return nan ? '' : value;
33500         }
33501         return parseFloat(value).toFixed(this.decimalPrecision);
33502     },
33503
33504     setValue : function(v)
33505     {
33506         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33507         
33508         this.value = v;
33509         
33510         if(this.rendered){
33511             
33512             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33513             
33514             this.inputEl().dom.value = (v == '') ? '' :
33515                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33516             
33517             if(!this.allowZero && v === '0') {
33518                 this.hiddenEl().dom.value = '';
33519                 this.inputEl().dom.value = '';
33520             }
33521             
33522             this.validate();
33523         }
33524     },
33525
33526     decimalPrecisionFcn : function(v)
33527     {
33528         return Math.floor(v);
33529     },
33530
33531     beforeBlur : function()
33532     {
33533         var v = this.parseValue(this.getRawValue());
33534         
33535         if(v || v === 0 || v === ''){
33536             this.setValue(v);
33537         }
33538     },
33539     
33540     hiddenEl : function()
33541     {
33542         return this.el.select('input.hidden-number-input',true).first();
33543     }
33544     
33545 });
33546
33547  
33548
33549 /*
33550 * Licence: LGPL
33551 */
33552
33553 /**
33554  * @class Roo.bootstrap.DocumentSlider
33555  * @extends Roo.bootstrap.Component
33556  * Bootstrap DocumentSlider class
33557  * 
33558  * @constructor
33559  * Create a new DocumentViewer
33560  * @param {Object} config The config object
33561  */
33562
33563 Roo.bootstrap.DocumentSlider = function(config){
33564     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33565     
33566     this.files = [];
33567     
33568     this.addEvents({
33569         /**
33570          * @event initial
33571          * Fire after initEvent
33572          * @param {Roo.bootstrap.DocumentSlider} this
33573          */
33574         "initial" : true,
33575         /**
33576          * @event update
33577          * Fire after update
33578          * @param {Roo.bootstrap.DocumentSlider} this
33579          */
33580         "update" : true,
33581         /**
33582          * @event click
33583          * Fire after click
33584          * @param {Roo.bootstrap.DocumentSlider} this
33585          */
33586         "click" : true
33587     });
33588 };
33589
33590 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33591     
33592     files : false,
33593     
33594     indicator : 0,
33595     
33596     getAutoCreate : function()
33597     {
33598         var cfg = {
33599             tag : 'div',
33600             cls : 'roo-document-slider',
33601             cn : [
33602                 {
33603                     tag : 'div',
33604                     cls : 'roo-document-slider-header',
33605                     cn : [
33606                         {
33607                             tag : 'div',
33608                             cls : 'roo-document-slider-header-title'
33609                         }
33610                     ]
33611                 },
33612                 {
33613                     tag : 'div',
33614                     cls : 'roo-document-slider-body',
33615                     cn : [
33616                         {
33617                             tag : 'div',
33618                             cls : 'roo-document-slider-prev',
33619                             cn : [
33620                                 {
33621                                     tag : 'i',
33622                                     cls : 'fa fa-chevron-left'
33623                                 }
33624                             ]
33625                         },
33626                         {
33627                             tag : 'div',
33628                             cls : 'roo-document-slider-thumb',
33629                             cn : [
33630                                 {
33631                                     tag : 'img',
33632                                     cls : 'roo-document-slider-image'
33633                                 }
33634                             ]
33635                         },
33636                         {
33637                             tag : 'div',
33638                             cls : 'roo-document-slider-next',
33639                             cn : [
33640                                 {
33641                                     tag : 'i',
33642                                     cls : 'fa fa-chevron-right'
33643                                 }
33644                             ]
33645                         }
33646                     ]
33647                 }
33648             ]
33649         };
33650         
33651         return cfg;
33652     },
33653     
33654     initEvents : function()
33655     {
33656         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33657         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33658         
33659         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33660         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33661         
33662         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33663         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33664         
33665         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33666         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33667         
33668         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33669         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33670         
33671         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33672         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33673         
33674         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33675         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33676         
33677         this.thumbEl.on('click', this.onClick, this);
33678         
33679         this.prevIndicator.on('click', this.prev, this);
33680         
33681         this.nextIndicator.on('click', this.next, this);
33682         
33683     },
33684     
33685     initial : function()
33686     {
33687         if(this.files.length){
33688             this.indicator = 1;
33689             this.update()
33690         }
33691         
33692         this.fireEvent('initial', this);
33693     },
33694     
33695     update : function()
33696     {
33697         this.imageEl.attr('src', this.files[this.indicator - 1]);
33698         
33699         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33700         
33701         this.prevIndicator.show();
33702         
33703         if(this.indicator == 1){
33704             this.prevIndicator.hide();
33705         }
33706         
33707         this.nextIndicator.show();
33708         
33709         if(this.indicator == this.files.length){
33710             this.nextIndicator.hide();
33711         }
33712         
33713         this.thumbEl.scrollTo('top');
33714         
33715         this.fireEvent('update', this);
33716     },
33717     
33718     onClick : function(e)
33719     {
33720         e.preventDefault();
33721         
33722         this.fireEvent('click', this);
33723     },
33724     
33725     prev : function(e)
33726     {
33727         e.preventDefault();
33728         
33729         this.indicator = Math.max(1, this.indicator - 1);
33730         
33731         this.update();
33732     },
33733     
33734     next : function(e)
33735     {
33736         e.preventDefault();
33737         
33738         this.indicator = Math.min(this.files.length, this.indicator + 1);
33739         
33740         this.update();
33741     }
33742 });
33743 /*
33744  * - LGPL
33745  *
33746  * RadioSet
33747  *
33748  *
33749  */
33750
33751 /**
33752  * @class Roo.bootstrap.RadioSet
33753  * @extends Roo.bootstrap.Input
33754  * Bootstrap RadioSet class
33755  * @cfg {String} indicatorpos (left|right) default left
33756  * @cfg {Boolean} inline (true|false) inline the element (default true)
33757  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33758  * @constructor
33759  * Create a new RadioSet
33760  * @param {Object} config The config object
33761  */
33762
33763 Roo.bootstrap.RadioSet = function(config){
33764     
33765     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33766     
33767     this.radioes = [];
33768     
33769     Roo.bootstrap.RadioSet.register(this);
33770     
33771     this.addEvents({
33772         /**
33773         * @event check
33774         * Fires when the element is checked or unchecked.
33775         * @param {Roo.bootstrap.RadioSet} this This radio
33776         * @param {Roo.bootstrap.Radio} item The checked item
33777         */
33778        check : true,
33779        /**
33780         * @event click
33781         * Fires when the element is click.
33782         * @param {Roo.bootstrap.RadioSet} this This radio set
33783         * @param {Roo.bootstrap.Radio} item The checked item
33784         * @param {Roo.EventObject} e The event object
33785         */
33786        click : true
33787     });
33788     
33789 };
33790
33791 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33792
33793     radioes : false,
33794     
33795     inline : true,
33796     
33797     weight : '',
33798     
33799     indicatorpos : 'left',
33800     
33801     getAutoCreate : function()
33802     {
33803         var label = {
33804             tag : 'label',
33805             cls : 'roo-radio-set-label',
33806             cn : [
33807                 {
33808                     tag : 'span',
33809                     html : this.fieldLabel
33810                 }
33811             ]
33812         };
33813         
33814         if(this.indicatorpos == 'left'){
33815             label.cn.unshift({
33816                 tag : 'i',
33817                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33818                 tooltip : 'This field is required'
33819             });
33820         } else {
33821             label.cn.push({
33822                 tag : 'i',
33823                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33824                 tooltip : 'This field is required'
33825             });
33826         }
33827         
33828         var items = {
33829             tag : 'div',
33830             cls : 'roo-radio-set-items'
33831         };
33832         
33833         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33834         
33835         if (align === 'left' && this.fieldLabel.length) {
33836             
33837             items = {
33838                 cls : "roo-radio-set-right", 
33839                 cn: [
33840                     items
33841                 ]
33842             };
33843             
33844             if(this.labelWidth > 12){
33845                 label.style = "width: " + this.labelWidth + 'px';
33846             }
33847             
33848             if(this.labelWidth < 13 && this.labelmd == 0){
33849                 this.labelmd = this.labelWidth;
33850             }
33851             
33852             if(this.labellg > 0){
33853                 label.cls += ' col-lg-' + this.labellg;
33854                 items.cls += ' col-lg-' + (12 - this.labellg);
33855             }
33856             
33857             if(this.labelmd > 0){
33858                 label.cls += ' col-md-' + this.labelmd;
33859                 items.cls += ' col-md-' + (12 - this.labelmd);
33860             }
33861             
33862             if(this.labelsm > 0){
33863                 label.cls += ' col-sm-' + this.labelsm;
33864                 items.cls += ' col-sm-' + (12 - this.labelsm);
33865             }
33866             
33867             if(this.labelxs > 0){
33868                 label.cls += ' col-xs-' + this.labelxs;
33869                 items.cls += ' col-xs-' + (12 - this.labelxs);
33870             }
33871         }
33872         
33873         var cfg = {
33874             tag : 'div',
33875             cls : 'roo-radio-set',
33876             cn : [
33877                 {
33878                     tag : 'input',
33879                     cls : 'roo-radio-set-input',
33880                     type : 'hidden',
33881                     name : this.name,
33882                     value : this.value ? this.value :  ''
33883                 },
33884                 label,
33885                 items
33886             ]
33887         };
33888         
33889         if(this.weight.length){
33890             cfg.cls += ' roo-radio-' + this.weight;
33891         }
33892         
33893         if(this.inline) {
33894             cfg.cls += ' roo-radio-set-inline';
33895         }
33896         
33897         var settings=this;
33898         ['xs','sm','md','lg'].map(function(size){
33899             if (settings[size]) {
33900                 cfg.cls += ' col-' + size + '-' + settings[size];
33901             }
33902         });
33903         
33904         return cfg;
33905         
33906     },
33907
33908     initEvents : function()
33909     {
33910         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33911         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33912         
33913         if(!this.fieldLabel.length){
33914             this.labelEl.hide();
33915         }
33916         
33917         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33918         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33919         
33920         this.indicator = this.indicatorEl();
33921         
33922         if(this.indicator){
33923             this.indicator.addClass('invisible');
33924         }
33925         
33926         this.originalValue = this.getValue();
33927         
33928     },
33929     
33930     inputEl: function ()
33931     {
33932         return this.el.select('.roo-radio-set-input', true).first();
33933     },
33934     
33935     getChildContainer : function()
33936     {
33937         return this.itemsEl;
33938     },
33939     
33940     register : function(item)
33941     {
33942         this.radioes.push(item);
33943         
33944     },
33945     
33946     validate : function()
33947     {   
33948         if(this.getVisibilityEl().hasClass('hidden')){
33949             return true;
33950         }
33951         
33952         var valid = false;
33953         
33954         Roo.each(this.radioes, function(i){
33955             if(!i.checked){
33956                 return;
33957             }
33958             
33959             valid = true;
33960             return false;
33961         });
33962         
33963         if(this.allowBlank) {
33964             return true;
33965         }
33966         
33967         if(this.disabled || valid){
33968             this.markValid();
33969             return true;
33970         }
33971         
33972         this.markInvalid();
33973         return false;
33974         
33975     },
33976     
33977     markValid : function()
33978     {
33979         if(this.labelEl.isVisible(true)){
33980             this.indicatorEl().removeClass('visible');
33981             this.indicatorEl().addClass('invisible');
33982         }
33983         
33984         this.el.removeClass([this.invalidClass, this.validClass]);
33985         this.el.addClass(this.validClass);
33986         
33987         this.fireEvent('valid', this);
33988     },
33989     
33990     markInvalid : function(msg)
33991     {
33992         if(this.allowBlank || this.disabled){
33993             return;
33994         }
33995         
33996         if(this.labelEl.isVisible(true)){
33997             this.indicatorEl().removeClass('invisible');
33998             this.indicatorEl().addClass('visible');
33999         }
34000         
34001         this.el.removeClass([this.invalidClass, this.validClass]);
34002         this.el.addClass(this.invalidClass);
34003         
34004         this.fireEvent('invalid', this, msg);
34005         
34006     },
34007     
34008     setValue : function(v, suppressEvent)
34009     {   
34010         if(this.value === v){
34011             return;
34012         }
34013         
34014         this.value = v;
34015         
34016         if(this.rendered){
34017             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34018         }
34019         
34020         Roo.each(this.radioes, function(i){
34021             i.checked = false;
34022             i.el.removeClass('checked');
34023         });
34024         
34025         Roo.each(this.radioes, function(i){
34026             
34027             if(i.value === v || i.value.toString() === v.toString()){
34028                 i.checked = true;
34029                 i.el.addClass('checked');
34030                 
34031                 if(suppressEvent !== true){
34032                     this.fireEvent('check', this, i);
34033                 }
34034                 
34035                 return false;
34036             }
34037             
34038         }, this);
34039         
34040         this.validate();
34041     },
34042     
34043     clearInvalid : function(){
34044         
34045         if(!this.el || this.preventMark){
34046             return;
34047         }
34048         
34049         this.el.removeClass([this.invalidClass]);
34050         
34051         this.fireEvent('valid', this);
34052     }
34053     
34054 });
34055
34056 Roo.apply(Roo.bootstrap.RadioSet, {
34057     
34058     groups: {},
34059     
34060     register : function(set)
34061     {
34062         this.groups[set.name] = set;
34063     },
34064     
34065     get: function(name) 
34066     {
34067         if (typeof(this.groups[name]) == 'undefined') {
34068             return false;
34069         }
34070         
34071         return this.groups[name] ;
34072     }
34073     
34074 });
34075 /*
34076  * Based on:
34077  * Ext JS Library 1.1.1
34078  * Copyright(c) 2006-2007, Ext JS, LLC.
34079  *
34080  * Originally Released Under LGPL - original licence link has changed is not relivant.
34081  *
34082  * Fork - LGPL
34083  * <script type="text/javascript">
34084  */
34085
34086
34087 /**
34088  * @class Roo.bootstrap.SplitBar
34089  * @extends Roo.util.Observable
34090  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34091  * <br><br>
34092  * Usage:
34093  * <pre><code>
34094 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34095                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34096 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34097 split.minSize = 100;
34098 split.maxSize = 600;
34099 split.animate = true;
34100 split.on('moved', splitterMoved);
34101 </code></pre>
34102  * @constructor
34103  * Create a new SplitBar
34104  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34105  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34106  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34107  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34108                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34109                         position of the SplitBar).
34110  */
34111 Roo.bootstrap.SplitBar = function(cfg){
34112     
34113     /** @private */
34114     
34115     //{
34116     //  dragElement : elm
34117     //  resizingElement: el,
34118         // optional..
34119     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34120     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34121         // existingProxy ???
34122     //}
34123     
34124     this.el = Roo.get(cfg.dragElement, true);
34125     this.el.dom.unselectable = "on";
34126     /** @private */
34127     this.resizingEl = Roo.get(cfg.resizingElement, true);
34128
34129     /**
34130      * @private
34131      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34132      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34133      * @type Number
34134      */
34135     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34136     
34137     /**
34138      * The minimum size of the resizing element. (Defaults to 0)
34139      * @type Number
34140      */
34141     this.minSize = 0;
34142     
34143     /**
34144      * The maximum size of the resizing element. (Defaults to 2000)
34145      * @type Number
34146      */
34147     this.maxSize = 2000;
34148     
34149     /**
34150      * Whether to animate the transition to the new size
34151      * @type Boolean
34152      */
34153     this.animate = false;
34154     
34155     /**
34156      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34157      * @type Boolean
34158      */
34159     this.useShim = false;
34160     
34161     /** @private */
34162     this.shim = null;
34163     
34164     if(!cfg.existingProxy){
34165         /** @private */
34166         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34167     }else{
34168         this.proxy = Roo.get(cfg.existingProxy).dom;
34169     }
34170     /** @private */
34171     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34172     
34173     /** @private */
34174     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34175     
34176     /** @private */
34177     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34178     
34179     /** @private */
34180     this.dragSpecs = {};
34181     
34182     /**
34183      * @private The adapter to use to positon and resize elements
34184      */
34185     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34186     this.adapter.init(this);
34187     
34188     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34189         /** @private */
34190         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34191         this.el.addClass("roo-splitbar-h");
34192     }else{
34193         /** @private */
34194         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34195         this.el.addClass("roo-splitbar-v");
34196     }
34197     
34198     this.addEvents({
34199         /**
34200          * @event resize
34201          * Fires when the splitter is moved (alias for {@link #event-moved})
34202          * @param {Roo.bootstrap.SplitBar} this
34203          * @param {Number} newSize the new width or height
34204          */
34205         "resize" : true,
34206         /**
34207          * @event moved
34208          * Fires when the splitter is moved
34209          * @param {Roo.bootstrap.SplitBar} this
34210          * @param {Number} newSize the new width or height
34211          */
34212         "moved" : true,
34213         /**
34214          * @event beforeresize
34215          * Fires before the splitter is dragged
34216          * @param {Roo.bootstrap.SplitBar} this
34217          */
34218         "beforeresize" : true,
34219
34220         "beforeapply" : true
34221     });
34222
34223     Roo.util.Observable.call(this);
34224 };
34225
34226 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34227     onStartProxyDrag : function(x, y){
34228         this.fireEvent("beforeresize", this);
34229         if(!this.overlay){
34230             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34231             o.unselectable();
34232             o.enableDisplayMode("block");
34233             // all splitbars share the same overlay
34234             Roo.bootstrap.SplitBar.prototype.overlay = o;
34235         }
34236         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34237         this.overlay.show();
34238         Roo.get(this.proxy).setDisplayed("block");
34239         var size = this.adapter.getElementSize(this);
34240         this.activeMinSize = this.getMinimumSize();;
34241         this.activeMaxSize = this.getMaximumSize();;
34242         var c1 = size - this.activeMinSize;
34243         var c2 = Math.max(this.activeMaxSize - size, 0);
34244         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34245             this.dd.resetConstraints();
34246             this.dd.setXConstraint(
34247                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34248                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34249             );
34250             this.dd.setYConstraint(0, 0);
34251         }else{
34252             this.dd.resetConstraints();
34253             this.dd.setXConstraint(0, 0);
34254             this.dd.setYConstraint(
34255                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34256                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34257             );
34258          }
34259         this.dragSpecs.startSize = size;
34260         this.dragSpecs.startPoint = [x, y];
34261         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34262     },
34263     
34264     /** 
34265      * @private Called after the drag operation by the DDProxy
34266      */
34267     onEndProxyDrag : function(e){
34268         Roo.get(this.proxy).setDisplayed(false);
34269         var endPoint = Roo.lib.Event.getXY(e);
34270         if(this.overlay){
34271             this.overlay.hide();
34272         }
34273         var newSize;
34274         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34275             newSize = this.dragSpecs.startSize + 
34276                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34277                     endPoint[0] - this.dragSpecs.startPoint[0] :
34278                     this.dragSpecs.startPoint[0] - endPoint[0]
34279                 );
34280         }else{
34281             newSize = this.dragSpecs.startSize + 
34282                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34283                     endPoint[1] - this.dragSpecs.startPoint[1] :
34284                     this.dragSpecs.startPoint[1] - endPoint[1]
34285                 );
34286         }
34287         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34288         if(newSize != this.dragSpecs.startSize){
34289             if(this.fireEvent('beforeapply', this, newSize) !== false){
34290                 this.adapter.setElementSize(this, newSize);
34291                 this.fireEvent("moved", this, newSize);
34292                 this.fireEvent("resize", this, newSize);
34293             }
34294         }
34295     },
34296     
34297     /**
34298      * Get the adapter this SplitBar uses
34299      * @return The adapter object
34300      */
34301     getAdapter : function(){
34302         return this.adapter;
34303     },
34304     
34305     /**
34306      * Set the adapter this SplitBar uses
34307      * @param {Object} adapter A SplitBar adapter object
34308      */
34309     setAdapter : function(adapter){
34310         this.adapter = adapter;
34311         this.adapter.init(this);
34312     },
34313     
34314     /**
34315      * Gets the minimum size for the resizing element
34316      * @return {Number} The minimum size
34317      */
34318     getMinimumSize : function(){
34319         return this.minSize;
34320     },
34321     
34322     /**
34323      * Sets the minimum size for the resizing element
34324      * @param {Number} minSize The minimum size
34325      */
34326     setMinimumSize : function(minSize){
34327         this.minSize = minSize;
34328     },
34329     
34330     /**
34331      * Gets the maximum size for the resizing element
34332      * @return {Number} The maximum size
34333      */
34334     getMaximumSize : function(){
34335         return this.maxSize;
34336     },
34337     
34338     /**
34339      * Sets the maximum size for the resizing element
34340      * @param {Number} maxSize The maximum size
34341      */
34342     setMaximumSize : function(maxSize){
34343         this.maxSize = maxSize;
34344     },
34345     
34346     /**
34347      * Sets the initialize size for the resizing element
34348      * @param {Number} size The initial size
34349      */
34350     setCurrentSize : function(size){
34351         var oldAnimate = this.animate;
34352         this.animate = false;
34353         this.adapter.setElementSize(this, size);
34354         this.animate = oldAnimate;
34355     },
34356     
34357     /**
34358      * Destroy this splitbar. 
34359      * @param {Boolean} removeEl True to remove the element
34360      */
34361     destroy : function(removeEl){
34362         if(this.shim){
34363             this.shim.remove();
34364         }
34365         this.dd.unreg();
34366         this.proxy.parentNode.removeChild(this.proxy);
34367         if(removeEl){
34368             this.el.remove();
34369         }
34370     }
34371 });
34372
34373 /**
34374  * @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.
34375  */
34376 Roo.bootstrap.SplitBar.createProxy = function(dir){
34377     var proxy = new Roo.Element(document.createElement("div"));
34378     proxy.unselectable();
34379     var cls = 'roo-splitbar-proxy';
34380     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34381     document.body.appendChild(proxy.dom);
34382     return proxy.dom;
34383 };
34384
34385 /** 
34386  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34387  * Default Adapter. It assumes the splitter and resizing element are not positioned
34388  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34389  */
34390 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34391 };
34392
34393 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34394     // do nothing for now
34395     init : function(s){
34396     
34397     },
34398     /**
34399      * Called before drag operations to get the current size of the resizing element. 
34400      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34401      */
34402      getElementSize : function(s){
34403         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34404             return s.resizingEl.getWidth();
34405         }else{
34406             return s.resizingEl.getHeight();
34407         }
34408     },
34409     
34410     /**
34411      * Called after drag operations to set the size of the resizing element.
34412      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34413      * @param {Number} newSize The new size to set
34414      * @param {Function} onComplete A function to be invoked when resizing is complete
34415      */
34416     setElementSize : function(s, newSize, onComplete){
34417         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34418             if(!s.animate){
34419                 s.resizingEl.setWidth(newSize);
34420                 if(onComplete){
34421                     onComplete(s, newSize);
34422                 }
34423             }else{
34424                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34425             }
34426         }else{
34427             
34428             if(!s.animate){
34429                 s.resizingEl.setHeight(newSize);
34430                 if(onComplete){
34431                     onComplete(s, newSize);
34432                 }
34433             }else{
34434                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34435             }
34436         }
34437     }
34438 };
34439
34440 /** 
34441  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34442  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34443  * Adapter that  moves the splitter element to align with the resized sizing element. 
34444  * Used with an absolute positioned SplitBar.
34445  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34446  * document.body, make sure you assign an id to the body element.
34447  */
34448 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34449     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34450     this.container = Roo.get(container);
34451 };
34452
34453 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34454     init : function(s){
34455         this.basic.init(s);
34456     },
34457     
34458     getElementSize : function(s){
34459         return this.basic.getElementSize(s);
34460     },
34461     
34462     setElementSize : function(s, newSize, onComplete){
34463         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34464     },
34465     
34466     moveSplitter : function(s){
34467         var yes = Roo.bootstrap.SplitBar;
34468         switch(s.placement){
34469             case yes.LEFT:
34470                 s.el.setX(s.resizingEl.getRight());
34471                 break;
34472             case yes.RIGHT:
34473                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34474                 break;
34475             case yes.TOP:
34476                 s.el.setY(s.resizingEl.getBottom());
34477                 break;
34478             case yes.BOTTOM:
34479                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34480                 break;
34481         }
34482     }
34483 };
34484
34485 /**
34486  * Orientation constant - Create a vertical SplitBar
34487  * @static
34488  * @type Number
34489  */
34490 Roo.bootstrap.SplitBar.VERTICAL = 1;
34491
34492 /**
34493  * Orientation constant - Create a horizontal SplitBar
34494  * @static
34495  * @type Number
34496  */
34497 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34498
34499 /**
34500  * Placement constant - The resizing element is to the left of the splitter element
34501  * @static
34502  * @type Number
34503  */
34504 Roo.bootstrap.SplitBar.LEFT = 1;
34505
34506 /**
34507  * Placement constant - The resizing element is to the right of the splitter element
34508  * @static
34509  * @type Number
34510  */
34511 Roo.bootstrap.SplitBar.RIGHT = 2;
34512
34513 /**
34514  * Placement constant - The resizing element is positioned above the splitter element
34515  * @static
34516  * @type Number
34517  */
34518 Roo.bootstrap.SplitBar.TOP = 3;
34519
34520 /**
34521  * Placement constant - The resizing element is positioned under splitter element
34522  * @static
34523  * @type Number
34524  */
34525 Roo.bootstrap.SplitBar.BOTTOM = 4;
34526 Roo.namespace("Roo.bootstrap.layout");/*
34527  * Based on:
34528  * Ext JS Library 1.1.1
34529  * Copyright(c) 2006-2007, Ext JS, LLC.
34530  *
34531  * Originally Released Under LGPL - original licence link has changed is not relivant.
34532  *
34533  * Fork - LGPL
34534  * <script type="text/javascript">
34535  */
34536
34537 /**
34538  * @class Roo.bootstrap.layout.Manager
34539  * @extends Roo.bootstrap.Component
34540  * Base class for layout managers.
34541  */
34542 Roo.bootstrap.layout.Manager = function(config)
34543 {
34544     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34545
34546
34547
34548
34549
34550     /** false to disable window resize monitoring @type Boolean */
34551     this.monitorWindowResize = true;
34552     this.regions = {};
34553     this.addEvents({
34554         /**
34555          * @event layout
34556          * Fires when a layout is performed.
34557          * @param {Roo.LayoutManager} this
34558          */
34559         "layout" : true,
34560         /**
34561          * @event regionresized
34562          * Fires when the user resizes a region.
34563          * @param {Roo.LayoutRegion} region The resized region
34564          * @param {Number} newSize The new size (width for east/west, height for north/south)
34565          */
34566         "regionresized" : true,
34567         /**
34568          * @event regioncollapsed
34569          * Fires when a region is collapsed.
34570          * @param {Roo.LayoutRegion} region The collapsed region
34571          */
34572         "regioncollapsed" : true,
34573         /**
34574          * @event regionexpanded
34575          * Fires when a region is expanded.
34576          * @param {Roo.LayoutRegion} region The expanded region
34577          */
34578         "regionexpanded" : true
34579     });
34580     this.updating = false;
34581
34582     if (config.el) {
34583         this.el = Roo.get(config.el);
34584         this.initEvents();
34585     }
34586
34587 };
34588
34589 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34590
34591
34592     regions : null,
34593
34594     monitorWindowResize : true,
34595
34596
34597     updating : false,
34598
34599
34600     onRender : function(ct, position)
34601     {
34602         if(!this.el){
34603             this.el = Roo.get(ct);
34604             this.initEvents();
34605         }
34606         //this.fireEvent('render',this);
34607     },
34608
34609
34610     initEvents: function()
34611     {
34612
34613
34614         // ie scrollbar fix
34615         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34616             document.body.scroll = "no";
34617         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34618             this.el.position('relative');
34619         }
34620         this.id = this.el.id;
34621         this.el.addClass("roo-layout-container");
34622         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34623         if(this.el.dom != document.body ) {
34624             this.el.on('resize', this.layout,this);
34625             this.el.on('show', this.layout,this);
34626         }
34627
34628     },
34629
34630     /**
34631      * Returns true if this layout is currently being updated
34632      * @return {Boolean}
34633      */
34634     isUpdating : function(){
34635         return this.updating;
34636     },
34637
34638     /**
34639      * Suspend the LayoutManager from doing auto-layouts while
34640      * making multiple add or remove calls
34641      */
34642     beginUpdate : function(){
34643         this.updating = true;
34644     },
34645
34646     /**
34647      * Restore auto-layouts and optionally disable the manager from performing a layout
34648      * @param {Boolean} noLayout true to disable a layout update
34649      */
34650     endUpdate : function(noLayout){
34651         this.updating = false;
34652         if(!noLayout){
34653             this.layout();
34654         }
34655     },
34656
34657     layout: function(){
34658         // abstract...
34659     },
34660
34661     onRegionResized : function(region, newSize){
34662         this.fireEvent("regionresized", region, newSize);
34663         this.layout();
34664     },
34665
34666     onRegionCollapsed : function(region){
34667         this.fireEvent("regioncollapsed", region);
34668     },
34669
34670     onRegionExpanded : function(region){
34671         this.fireEvent("regionexpanded", region);
34672     },
34673
34674     /**
34675      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34676      * performs box-model adjustments.
34677      * @return {Object} The size as an object {width: (the width), height: (the height)}
34678      */
34679     getViewSize : function()
34680     {
34681         var size;
34682         if(this.el.dom != document.body){
34683             size = this.el.getSize();
34684         }else{
34685             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34686         }
34687         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34688         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34689         return size;
34690     },
34691
34692     /**
34693      * Returns the Element this layout is bound to.
34694      * @return {Roo.Element}
34695      */
34696     getEl : function(){
34697         return this.el;
34698     },
34699
34700     /**
34701      * Returns the specified region.
34702      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34703      * @return {Roo.LayoutRegion}
34704      */
34705     getRegion : function(target){
34706         return this.regions[target.toLowerCase()];
34707     },
34708
34709     onWindowResize : function(){
34710         if(this.monitorWindowResize){
34711             this.layout();
34712         }
34713     }
34714 });
34715 /*
34716  * Based on:
34717  * Ext JS Library 1.1.1
34718  * Copyright(c) 2006-2007, Ext JS, LLC.
34719  *
34720  * Originally Released Under LGPL - original licence link has changed is not relivant.
34721  *
34722  * Fork - LGPL
34723  * <script type="text/javascript">
34724  */
34725 /**
34726  * @class Roo.bootstrap.layout.Border
34727  * @extends Roo.bootstrap.layout.Manager
34728  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34729  * please see: examples/bootstrap/nested.html<br><br>
34730  
34731 <b>The container the layout is rendered into can be either the body element or any other element.
34732 If it is not the body element, the container needs to either be an absolute positioned element,
34733 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34734 the container size if it is not the body element.</b>
34735
34736 * @constructor
34737 * Create a new Border
34738 * @param {Object} config Configuration options
34739  */
34740 Roo.bootstrap.layout.Border = function(config){
34741     config = config || {};
34742     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34743     
34744     
34745     
34746     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34747         if(config[region]){
34748             config[region].region = region;
34749             this.addRegion(config[region]);
34750         }
34751     },this);
34752     
34753 };
34754
34755 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34756
34757 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34758     /**
34759      * Creates and adds a new region if it doesn't already exist.
34760      * @param {String} target The target region key (north, south, east, west or center).
34761      * @param {Object} config The regions config object
34762      * @return {BorderLayoutRegion} The new region
34763      */
34764     addRegion : function(config)
34765     {
34766         if(!this.regions[config.region]){
34767             var r = this.factory(config);
34768             this.bindRegion(r);
34769         }
34770         return this.regions[config.region];
34771     },
34772
34773     // private (kinda)
34774     bindRegion : function(r){
34775         this.regions[r.config.region] = r;
34776         
34777         r.on("visibilitychange",    this.layout, this);
34778         r.on("paneladded",          this.layout, this);
34779         r.on("panelremoved",        this.layout, this);
34780         r.on("invalidated",         this.layout, this);
34781         r.on("resized",             this.onRegionResized, this);
34782         r.on("collapsed",           this.onRegionCollapsed, this);
34783         r.on("expanded",            this.onRegionExpanded, this);
34784     },
34785
34786     /**
34787      * Performs a layout update.
34788      */
34789     layout : function()
34790     {
34791         if(this.updating) {
34792             return;
34793         }
34794         
34795         // render all the rebions if they have not been done alreayd?
34796         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34797             if(this.regions[region] && !this.regions[region].bodyEl){
34798                 this.regions[region].onRender(this.el)
34799             }
34800         },this);
34801         
34802         var size = this.getViewSize();
34803         var w = size.width;
34804         var h = size.height;
34805         var centerW = w;
34806         var centerH = h;
34807         var centerY = 0;
34808         var centerX = 0;
34809         //var x = 0, y = 0;
34810
34811         var rs = this.regions;
34812         var north = rs["north"];
34813         var south = rs["south"]; 
34814         var west = rs["west"];
34815         var east = rs["east"];
34816         var center = rs["center"];
34817         //if(this.hideOnLayout){ // not supported anymore
34818             //c.el.setStyle("display", "none");
34819         //}
34820         if(north && north.isVisible()){
34821             var b = north.getBox();
34822             var m = north.getMargins();
34823             b.width = w - (m.left+m.right);
34824             b.x = m.left;
34825             b.y = m.top;
34826             centerY = b.height + b.y + m.bottom;
34827             centerH -= centerY;
34828             north.updateBox(this.safeBox(b));
34829         }
34830         if(south && south.isVisible()){
34831             var b = south.getBox();
34832             var m = south.getMargins();
34833             b.width = w - (m.left+m.right);
34834             b.x = m.left;
34835             var totalHeight = (b.height + m.top + m.bottom);
34836             b.y = h - totalHeight + m.top;
34837             centerH -= totalHeight;
34838             south.updateBox(this.safeBox(b));
34839         }
34840         if(west && west.isVisible()){
34841             var b = west.getBox();
34842             var m = west.getMargins();
34843             b.height = centerH - (m.top+m.bottom);
34844             b.x = m.left;
34845             b.y = centerY + m.top;
34846             var totalWidth = (b.width + m.left + m.right);
34847             centerX += totalWidth;
34848             centerW -= totalWidth;
34849             west.updateBox(this.safeBox(b));
34850         }
34851         if(east && east.isVisible()){
34852             var b = east.getBox();
34853             var m = east.getMargins();
34854             b.height = centerH - (m.top+m.bottom);
34855             var totalWidth = (b.width + m.left + m.right);
34856             b.x = w - totalWidth + m.left;
34857             b.y = centerY + m.top;
34858             centerW -= totalWidth;
34859             east.updateBox(this.safeBox(b));
34860         }
34861         if(center){
34862             var m = center.getMargins();
34863             var centerBox = {
34864                 x: centerX + m.left,
34865                 y: centerY + m.top,
34866                 width: centerW - (m.left+m.right),
34867                 height: centerH - (m.top+m.bottom)
34868             };
34869             //if(this.hideOnLayout){
34870                 //center.el.setStyle("display", "block");
34871             //}
34872             center.updateBox(this.safeBox(centerBox));
34873         }
34874         this.el.repaint();
34875         this.fireEvent("layout", this);
34876     },
34877
34878     // private
34879     safeBox : function(box){
34880         box.width = Math.max(0, box.width);
34881         box.height = Math.max(0, box.height);
34882         return box;
34883     },
34884
34885     /**
34886      * Adds a ContentPanel (or subclass) to this layout.
34887      * @param {String} target The target region key (north, south, east, west or center).
34888      * @param {Roo.ContentPanel} panel The panel to add
34889      * @return {Roo.ContentPanel} The added panel
34890      */
34891     add : function(target, panel){
34892          
34893         target = target.toLowerCase();
34894         return this.regions[target].add(panel);
34895     },
34896
34897     /**
34898      * Remove a ContentPanel (or subclass) to this layout.
34899      * @param {String} target The target region key (north, south, east, west or center).
34900      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34901      * @return {Roo.ContentPanel} The removed panel
34902      */
34903     remove : function(target, panel){
34904         target = target.toLowerCase();
34905         return this.regions[target].remove(panel);
34906     },
34907
34908     /**
34909      * Searches all regions for a panel with the specified id
34910      * @param {String} panelId
34911      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34912      */
34913     findPanel : function(panelId){
34914         var rs = this.regions;
34915         for(var target in rs){
34916             if(typeof rs[target] != "function"){
34917                 var p = rs[target].getPanel(panelId);
34918                 if(p){
34919                     return p;
34920                 }
34921             }
34922         }
34923         return null;
34924     },
34925
34926     /**
34927      * Searches all regions for a panel with the specified id and activates (shows) it.
34928      * @param {String/ContentPanel} panelId The panels id or the panel itself
34929      * @return {Roo.ContentPanel} The shown panel or null
34930      */
34931     showPanel : function(panelId) {
34932       var rs = this.regions;
34933       for(var target in rs){
34934          var r = rs[target];
34935          if(typeof r != "function"){
34936             if(r.hasPanel(panelId)){
34937                return r.showPanel(panelId);
34938             }
34939          }
34940       }
34941       return null;
34942    },
34943
34944    /**
34945      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34946      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34947      */
34948    /*
34949     restoreState : function(provider){
34950         if(!provider){
34951             provider = Roo.state.Manager;
34952         }
34953         var sm = new Roo.LayoutStateManager();
34954         sm.init(this, provider);
34955     },
34956 */
34957  
34958  
34959     /**
34960      * Adds a xtype elements to the layout.
34961      * <pre><code>
34962
34963 layout.addxtype({
34964        xtype : 'ContentPanel',
34965        region: 'west',
34966        items: [ .... ]
34967    }
34968 );
34969
34970 layout.addxtype({
34971         xtype : 'NestedLayoutPanel',
34972         region: 'west',
34973         layout: {
34974            center: { },
34975            west: { }   
34976         },
34977         items : [ ... list of content panels or nested layout panels.. ]
34978    }
34979 );
34980 </code></pre>
34981      * @param {Object} cfg Xtype definition of item to add.
34982      */
34983     addxtype : function(cfg)
34984     {
34985         // basically accepts a pannel...
34986         // can accept a layout region..!?!?
34987         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34988         
34989         
34990         // theory?  children can only be panels??
34991         
34992         //if (!cfg.xtype.match(/Panel$/)) {
34993         //    return false;
34994         //}
34995         var ret = false;
34996         
34997         if (typeof(cfg.region) == 'undefined') {
34998             Roo.log("Failed to add Panel, region was not set");
34999             Roo.log(cfg);
35000             return false;
35001         }
35002         var region = cfg.region;
35003         delete cfg.region;
35004         
35005           
35006         var xitems = [];
35007         if (cfg.items) {
35008             xitems = cfg.items;
35009             delete cfg.items;
35010         }
35011         var nb = false;
35012         
35013         switch(cfg.xtype) 
35014         {
35015             case 'Content':  // ContentPanel (el, cfg)
35016             case 'Scroll':  // ContentPanel (el, cfg)
35017             case 'View': 
35018                 cfg.autoCreate = true;
35019                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35020                 //} else {
35021                 //    var el = this.el.createChild();
35022                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35023                 //}
35024                 
35025                 this.add(region, ret);
35026                 break;
35027             
35028             /*
35029             case 'TreePanel': // our new panel!
35030                 cfg.el = this.el.createChild();
35031                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35032                 this.add(region, ret);
35033                 break;
35034             */
35035             
35036             case 'Nest': 
35037                 // create a new Layout (which is  a Border Layout...
35038                 
35039                 var clayout = cfg.layout;
35040                 clayout.el  = this.el.createChild();
35041                 clayout.items   = clayout.items  || [];
35042                 
35043                 delete cfg.layout;
35044                 
35045                 // replace this exitems with the clayout ones..
35046                 xitems = clayout.items;
35047                  
35048                 // force background off if it's in center...
35049                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35050                     cfg.background = false;
35051                 }
35052                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35053                 
35054                 
35055                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35056                 //console.log('adding nested layout panel '  + cfg.toSource());
35057                 this.add(region, ret);
35058                 nb = {}; /// find first...
35059                 break;
35060             
35061             case 'Grid':
35062                 
35063                 // needs grid and region
35064                 
35065                 //var el = this.getRegion(region).el.createChild();
35066                 /*
35067                  *var el = this.el.createChild();
35068                 // create the grid first...
35069                 cfg.grid.container = el;
35070                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35071                 */
35072                 
35073                 if (region == 'center' && this.active ) {
35074                     cfg.background = false;
35075                 }
35076                 
35077                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35078                 
35079                 this.add(region, ret);
35080                 /*
35081                 if (cfg.background) {
35082                     // render grid on panel activation (if panel background)
35083                     ret.on('activate', function(gp) {
35084                         if (!gp.grid.rendered) {
35085                     //        gp.grid.render(el);
35086                         }
35087                     });
35088                 } else {
35089                   //  cfg.grid.render(el);
35090                 }
35091                 */
35092                 break;
35093            
35094            
35095             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35096                 // it was the old xcomponent building that caused this before.
35097                 // espeically if border is the top element in the tree.
35098                 ret = this;
35099                 break; 
35100                 
35101                     
35102                 
35103                 
35104                 
35105             default:
35106                 /*
35107                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35108                     
35109                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35110                     this.add(region, ret);
35111                 } else {
35112                 */
35113                     Roo.log(cfg);
35114                     throw "Can not add '" + cfg.xtype + "' to Border";
35115                     return null;
35116              
35117                                 
35118              
35119         }
35120         this.beginUpdate();
35121         // add children..
35122         var region = '';
35123         var abn = {};
35124         Roo.each(xitems, function(i)  {
35125             region = nb && i.region ? i.region : false;
35126             
35127             var add = ret.addxtype(i);
35128            
35129             if (region) {
35130                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35131                 if (!i.background) {
35132                     abn[region] = nb[region] ;
35133                 }
35134             }
35135             
35136         });
35137         this.endUpdate();
35138
35139         // make the last non-background panel active..
35140         //if (nb) { Roo.log(abn); }
35141         if (nb) {
35142             
35143             for(var r in abn) {
35144                 region = this.getRegion(r);
35145                 if (region) {
35146                     // tried using nb[r], but it does not work..
35147                      
35148                     region.showPanel(abn[r]);
35149                    
35150                 }
35151             }
35152         }
35153         return ret;
35154         
35155     },
35156     
35157     
35158 // private
35159     factory : function(cfg)
35160     {
35161         
35162         var validRegions = Roo.bootstrap.layout.Border.regions;
35163
35164         var target = cfg.region;
35165         cfg.mgr = this;
35166         
35167         var r = Roo.bootstrap.layout;
35168         Roo.log(target);
35169         switch(target){
35170             case "north":
35171                 return new r.North(cfg);
35172             case "south":
35173                 return new r.South(cfg);
35174             case "east":
35175                 return new r.East(cfg);
35176             case "west":
35177                 return new r.West(cfg);
35178             case "center":
35179                 return new r.Center(cfg);
35180         }
35181         throw 'Layout region "'+target+'" not supported.';
35182     }
35183     
35184     
35185 });
35186  /*
35187  * Based on:
35188  * Ext JS Library 1.1.1
35189  * Copyright(c) 2006-2007, Ext JS, LLC.
35190  *
35191  * Originally Released Under LGPL - original licence link has changed is not relivant.
35192  *
35193  * Fork - LGPL
35194  * <script type="text/javascript">
35195  */
35196  
35197 /**
35198  * @class Roo.bootstrap.layout.Basic
35199  * @extends Roo.util.Observable
35200  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35201  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35202  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35203  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35204  * @cfg {string}   region  the region that it inhabits..
35205  * @cfg {bool}   skipConfig skip config?
35206  * 
35207
35208  */
35209 Roo.bootstrap.layout.Basic = function(config){
35210     
35211     this.mgr = config.mgr;
35212     
35213     this.position = config.region;
35214     
35215     var skipConfig = config.skipConfig;
35216     
35217     this.events = {
35218         /**
35219          * @scope Roo.BasicLayoutRegion
35220          */
35221         
35222         /**
35223          * @event beforeremove
35224          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35225          * @param {Roo.LayoutRegion} this
35226          * @param {Roo.ContentPanel} panel The panel
35227          * @param {Object} e The cancel event object
35228          */
35229         "beforeremove" : true,
35230         /**
35231          * @event invalidated
35232          * Fires when the layout for this region is changed.
35233          * @param {Roo.LayoutRegion} this
35234          */
35235         "invalidated" : true,
35236         /**
35237          * @event visibilitychange
35238          * Fires when this region is shown or hidden 
35239          * @param {Roo.LayoutRegion} this
35240          * @param {Boolean} visibility true or false
35241          */
35242         "visibilitychange" : true,
35243         /**
35244          * @event paneladded
35245          * Fires when a panel is added. 
35246          * @param {Roo.LayoutRegion} this
35247          * @param {Roo.ContentPanel} panel The panel
35248          */
35249         "paneladded" : true,
35250         /**
35251          * @event panelremoved
35252          * Fires when a panel is removed. 
35253          * @param {Roo.LayoutRegion} this
35254          * @param {Roo.ContentPanel} panel The panel
35255          */
35256         "panelremoved" : true,
35257         /**
35258          * @event beforecollapse
35259          * Fires when this region before collapse.
35260          * @param {Roo.LayoutRegion} this
35261          */
35262         "beforecollapse" : true,
35263         /**
35264          * @event collapsed
35265          * Fires when this region is collapsed.
35266          * @param {Roo.LayoutRegion} this
35267          */
35268         "collapsed" : true,
35269         /**
35270          * @event expanded
35271          * Fires when this region is expanded.
35272          * @param {Roo.LayoutRegion} this
35273          */
35274         "expanded" : true,
35275         /**
35276          * @event slideshow
35277          * Fires when this region is slid into view.
35278          * @param {Roo.LayoutRegion} this
35279          */
35280         "slideshow" : true,
35281         /**
35282          * @event slidehide
35283          * Fires when this region slides out of view. 
35284          * @param {Roo.LayoutRegion} this
35285          */
35286         "slidehide" : true,
35287         /**
35288          * @event panelactivated
35289          * Fires when a panel is activated. 
35290          * @param {Roo.LayoutRegion} this
35291          * @param {Roo.ContentPanel} panel The activated panel
35292          */
35293         "panelactivated" : true,
35294         /**
35295          * @event resized
35296          * Fires when the user resizes this region. 
35297          * @param {Roo.LayoutRegion} this
35298          * @param {Number} newSize The new size (width for east/west, height for north/south)
35299          */
35300         "resized" : true
35301     };
35302     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35303     this.panels = new Roo.util.MixedCollection();
35304     this.panels.getKey = this.getPanelId.createDelegate(this);
35305     this.box = null;
35306     this.activePanel = null;
35307     // ensure listeners are added...
35308     
35309     if (config.listeners || config.events) {
35310         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35311             listeners : config.listeners || {},
35312             events : config.events || {}
35313         });
35314     }
35315     
35316     if(skipConfig !== true){
35317         this.applyConfig(config);
35318     }
35319 };
35320
35321 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35322 {
35323     getPanelId : function(p){
35324         return p.getId();
35325     },
35326     
35327     applyConfig : function(config){
35328         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35329         this.config = config;
35330         
35331     },
35332     
35333     /**
35334      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35335      * the width, for horizontal (north, south) the height.
35336      * @param {Number} newSize The new width or height
35337      */
35338     resizeTo : function(newSize){
35339         var el = this.el ? this.el :
35340                  (this.activePanel ? this.activePanel.getEl() : null);
35341         if(el){
35342             switch(this.position){
35343                 case "east":
35344                 case "west":
35345                     el.setWidth(newSize);
35346                     this.fireEvent("resized", this, newSize);
35347                 break;
35348                 case "north":
35349                 case "south":
35350                     el.setHeight(newSize);
35351                     this.fireEvent("resized", this, newSize);
35352                 break;                
35353             }
35354         }
35355     },
35356     
35357     getBox : function(){
35358         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35359     },
35360     
35361     getMargins : function(){
35362         return this.margins;
35363     },
35364     
35365     updateBox : function(box){
35366         this.box = box;
35367         var el = this.activePanel.getEl();
35368         el.dom.style.left = box.x + "px";
35369         el.dom.style.top = box.y + "px";
35370         this.activePanel.setSize(box.width, box.height);
35371     },
35372     
35373     /**
35374      * Returns the container element for this region.
35375      * @return {Roo.Element}
35376      */
35377     getEl : function(){
35378         return this.activePanel;
35379     },
35380     
35381     /**
35382      * Returns true if this region is currently visible.
35383      * @return {Boolean}
35384      */
35385     isVisible : function(){
35386         return this.activePanel ? true : false;
35387     },
35388     
35389     setActivePanel : function(panel){
35390         panel = this.getPanel(panel);
35391         if(this.activePanel && this.activePanel != panel){
35392             this.activePanel.setActiveState(false);
35393             this.activePanel.getEl().setLeftTop(-10000,-10000);
35394         }
35395         this.activePanel = panel;
35396         panel.setActiveState(true);
35397         if(this.box){
35398             panel.setSize(this.box.width, this.box.height);
35399         }
35400         this.fireEvent("panelactivated", this, panel);
35401         this.fireEvent("invalidated");
35402     },
35403     
35404     /**
35405      * Show the specified panel.
35406      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35407      * @return {Roo.ContentPanel} The shown panel or null
35408      */
35409     showPanel : function(panel){
35410         panel = this.getPanel(panel);
35411         if(panel){
35412             this.setActivePanel(panel);
35413         }
35414         return panel;
35415     },
35416     
35417     /**
35418      * Get the active panel for this region.
35419      * @return {Roo.ContentPanel} The active panel or null
35420      */
35421     getActivePanel : function(){
35422         return this.activePanel;
35423     },
35424     
35425     /**
35426      * Add the passed ContentPanel(s)
35427      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35428      * @return {Roo.ContentPanel} The panel added (if only one was added)
35429      */
35430     add : function(panel){
35431         if(arguments.length > 1){
35432             for(var i = 0, len = arguments.length; i < len; i++) {
35433                 this.add(arguments[i]);
35434             }
35435             return null;
35436         }
35437         if(this.hasPanel(panel)){
35438             this.showPanel(panel);
35439             return panel;
35440         }
35441         var el = panel.getEl();
35442         if(el.dom.parentNode != this.mgr.el.dom){
35443             this.mgr.el.dom.appendChild(el.dom);
35444         }
35445         if(panel.setRegion){
35446             panel.setRegion(this);
35447         }
35448         this.panels.add(panel);
35449         el.setStyle("position", "absolute");
35450         if(!panel.background){
35451             this.setActivePanel(panel);
35452             if(this.config.initialSize && this.panels.getCount()==1){
35453                 this.resizeTo(this.config.initialSize);
35454             }
35455         }
35456         this.fireEvent("paneladded", this, panel);
35457         return panel;
35458     },
35459     
35460     /**
35461      * Returns true if the panel is in this region.
35462      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35463      * @return {Boolean}
35464      */
35465     hasPanel : function(panel){
35466         if(typeof panel == "object"){ // must be panel obj
35467             panel = panel.getId();
35468         }
35469         return this.getPanel(panel) ? true : false;
35470     },
35471     
35472     /**
35473      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35474      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35475      * @param {Boolean} preservePanel Overrides the config preservePanel option
35476      * @return {Roo.ContentPanel} The panel that was removed
35477      */
35478     remove : function(panel, preservePanel){
35479         panel = this.getPanel(panel);
35480         if(!panel){
35481             return null;
35482         }
35483         var e = {};
35484         this.fireEvent("beforeremove", this, panel, e);
35485         if(e.cancel === true){
35486             return null;
35487         }
35488         var panelId = panel.getId();
35489         this.panels.removeKey(panelId);
35490         return panel;
35491     },
35492     
35493     /**
35494      * Returns the panel specified or null if it's not in this region.
35495      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35496      * @return {Roo.ContentPanel}
35497      */
35498     getPanel : function(id){
35499         if(typeof id == "object"){ // must be panel obj
35500             return id;
35501         }
35502         return this.panels.get(id);
35503     },
35504     
35505     /**
35506      * Returns this regions position (north/south/east/west/center).
35507      * @return {String} 
35508      */
35509     getPosition: function(){
35510         return this.position;    
35511     }
35512 });/*
35513  * Based on:
35514  * Ext JS Library 1.1.1
35515  * Copyright(c) 2006-2007, Ext JS, LLC.
35516  *
35517  * Originally Released Under LGPL - original licence link has changed is not relivant.
35518  *
35519  * Fork - LGPL
35520  * <script type="text/javascript">
35521  */
35522  
35523 /**
35524  * @class Roo.bootstrap.layout.Region
35525  * @extends Roo.bootstrap.layout.Basic
35526  * This class represents a region in a layout manager.
35527  
35528  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35529  * @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})
35530  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35531  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35532  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35533  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35534  * @cfg {String}    title           The title for the region (overrides panel titles)
35535  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35536  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35537  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35538  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35539  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35540  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35541  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35542  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35543  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35544  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35545
35546  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35547  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35548  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35549  * @cfg {Number}    width           For East/West panels
35550  * @cfg {Number}    height          For North/South panels
35551  * @cfg {Boolean}   split           To show the splitter
35552  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35553  * 
35554  * @cfg {string}   cls             Extra CSS classes to add to region
35555  * 
35556  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35557  * @cfg {string}   region  the region that it inhabits..
35558  *
35559
35560  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35561  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35562
35563  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35564  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35565  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35566  */
35567 Roo.bootstrap.layout.Region = function(config)
35568 {
35569     this.applyConfig(config);
35570
35571     var mgr = config.mgr;
35572     var pos = config.region;
35573     config.skipConfig = true;
35574     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35575     
35576     if (mgr.el) {
35577         this.onRender(mgr.el);   
35578     }
35579      
35580     this.visible = true;
35581     this.collapsed = false;
35582     this.unrendered_panels = [];
35583 };
35584
35585 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35586
35587     position: '', // set by wrapper (eg. north/south etc..)
35588     unrendered_panels : null,  // unrendered panels.
35589     createBody : function(){
35590         /** This region's body element 
35591         * @type Roo.Element */
35592         this.bodyEl = this.el.createChild({
35593                 tag: "div",
35594                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35595         });
35596     },
35597
35598     onRender: function(ctr, pos)
35599     {
35600         var dh = Roo.DomHelper;
35601         /** This region's container element 
35602         * @type Roo.Element */
35603         this.el = dh.append(ctr.dom, {
35604                 tag: "div",
35605                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35606             }, true);
35607         /** This region's title element 
35608         * @type Roo.Element */
35609     
35610         this.titleEl = dh.append(this.el.dom,
35611             {
35612                     tag: "div",
35613                     unselectable: "on",
35614                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35615                     children:[
35616                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35617                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35618                     ]}, true);
35619         
35620         this.titleEl.enableDisplayMode();
35621         /** This region's title text element 
35622         * @type HTMLElement */
35623         this.titleTextEl = this.titleEl.dom.firstChild;
35624         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35625         /*
35626         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35627         this.closeBtn.enableDisplayMode();
35628         this.closeBtn.on("click", this.closeClicked, this);
35629         this.closeBtn.hide();
35630     */
35631         this.createBody(this.config);
35632         if(this.config.hideWhenEmpty){
35633             this.hide();
35634             this.on("paneladded", this.validateVisibility, this);
35635             this.on("panelremoved", this.validateVisibility, this);
35636         }
35637         if(this.autoScroll){
35638             this.bodyEl.setStyle("overflow", "auto");
35639         }else{
35640             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35641         }
35642         //if(c.titlebar !== false){
35643             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35644                 this.titleEl.hide();
35645             }else{
35646                 this.titleEl.show();
35647                 if(this.config.title){
35648                     this.titleTextEl.innerHTML = this.config.title;
35649                 }
35650             }
35651         //}
35652         if(this.config.collapsed){
35653             this.collapse(true);
35654         }
35655         if(this.config.hidden){
35656             this.hide();
35657         }
35658         
35659         if (this.unrendered_panels && this.unrendered_panels.length) {
35660             for (var i =0;i< this.unrendered_panels.length; i++) {
35661                 this.add(this.unrendered_panels[i]);
35662             }
35663             this.unrendered_panels = null;
35664             
35665         }
35666         
35667     },
35668     
35669     applyConfig : function(c)
35670     {
35671         /*
35672          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35673             var dh = Roo.DomHelper;
35674             if(c.titlebar !== false){
35675                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35676                 this.collapseBtn.on("click", this.collapse, this);
35677                 this.collapseBtn.enableDisplayMode();
35678                 /*
35679                 if(c.showPin === true || this.showPin){
35680                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35681                     this.stickBtn.enableDisplayMode();
35682                     this.stickBtn.on("click", this.expand, this);
35683                     this.stickBtn.hide();
35684                 }
35685                 
35686             }
35687             */
35688             /** This region's collapsed element
35689             * @type Roo.Element */
35690             /*
35691              *
35692             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35693                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35694             ]}, true);
35695             
35696             if(c.floatable !== false){
35697                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35698                this.collapsedEl.on("click", this.collapseClick, this);
35699             }
35700
35701             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35702                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35703                    id: "message", unselectable: "on", style:{"float":"left"}});
35704                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35705              }
35706             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35707             this.expandBtn.on("click", this.expand, this);
35708             
35709         }
35710         
35711         if(this.collapseBtn){
35712             this.collapseBtn.setVisible(c.collapsible == true);
35713         }
35714         
35715         this.cmargins = c.cmargins || this.cmargins ||
35716                          (this.position == "west" || this.position == "east" ?
35717                              {top: 0, left: 2, right:2, bottom: 0} :
35718                              {top: 2, left: 0, right:0, bottom: 2});
35719         */
35720         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35721         
35722         
35723         this.bottomTabs = c.tabPosition != "top";
35724         
35725         this.autoScroll = c.autoScroll || false;
35726         
35727         
35728        
35729         
35730         this.duration = c.duration || .30;
35731         this.slideDuration = c.slideDuration || .45;
35732         this.config = c;
35733        
35734     },
35735     /**
35736      * Returns true if this region is currently visible.
35737      * @return {Boolean}
35738      */
35739     isVisible : function(){
35740         return this.visible;
35741     },
35742
35743     /**
35744      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35745      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35746      */
35747     //setCollapsedTitle : function(title){
35748     //    title = title || "&#160;";
35749      //   if(this.collapsedTitleTextEl){
35750       //      this.collapsedTitleTextEl.innerHTML = title;
35751        // }
35752     //},
35753
35754     getBox : function(){
35755         var b;
35756       //  if(!this.collapsed){
35757             b = this.el.getBox(false, true);
35758        // }else{
35759           //  b = this.collapsedEl.getBox(false, true);
35760         //}
35761         return b;
35762     },
35763
35764     getMargins : function(){
35765         return this.margins;
35766         //return this.collapsed ? this.cmargins : this.margins;
35767     },
35768 /*
35769     highlight : function(){
35770         this.el.addClass("x-layout-panel-dragover");
35771     },
35772
35773     unhighlight : function(){
35774         this.el.removeClass("x-layout-panel-dragover");
35775     },
35776 */
35777     updateBox : function(box)
35778     {
35779         if (!this.bodyEl) {
35780             return; // not rendered yet..
35781         }
35782         
35783         this.box = box;
35784         if(!this.collapsed){
35785             this.el.dom.style.left = box.x + "px";
35786             this.el.dom.style.top = box.y + "px";
35787             this.updateBody(box.width, box.height);
35788         }else{
35789             this.collapsedEl.dom.style.left = box.x + "px";
35790             this.collapsedEl.dom.style.top = box.y + "px";
35791             this.collapsedEl.setSize(box.width, box.height);
35792         }
35793         if(this.tabs){
35794             this.tabs.autoSizeTabs();
35795         }
35796     },
35797
35798     updateBody : function(w, h)
35799     {
35800         if(w !== null){
35801             this.el.setWidth(w);
35802             w -= this.el.getBorderWidth("rl");
35803             if(this.config.adjustments){
35804                 w += this.config.adjustments[0];
35805             }
35806         }
35807         if(h !== null && h > 0){
35808             this.el.setHeight(h);
35809             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35810             h -= this.el.getBorderWidth("tb");
35811             if(this.config.adjustments){
35812                 h += this.config.adjustments[1];
35813             }
35814             this.bodyEl.setHeight(h);
35815             if(this.tabs){
35816                 h = this.tabs.syncHeight(h);
35817             }
35818         }
35819         if(this.panelSize){
35820             w = w !== null ? w : this.panelSize.width;
35821             h = h !== null ? h : this.panelSize.height;
35822         }
35823         if(this.activePanel){
35824             var el = this.activePanel.getEl();
35825             w = w !== null ? w : el.getWidth();
35826             h = h !== null ? h : el.getHeight();
35827             this.panelSize = {width: w, height: h};
35828             this.activePanel.setSize(w, h);
35829         }
35830         if(Roo.isIE && this.tabs){
35831             this.tabs.el.repaint();
35832         }
35833     },
35834
35835     /**
35836      * Returns the container element for this region.
35837      * @return {Roo.Element}
35838      */
35839     getEl : function(){
35840         return this.el;
35841     },
35842
35843     /**
35844      * Hides this region.
35845      */
35846     hide : function(){
35847         //if(!this.collapsed){
35848             this.el.dom.style.left = "-2000px";
35849             this.el.hide();
35850         //}else{
35851          //   this.collapsedEl.dom.style.left = "-2000px";
35852          //   this.collapsedEl.hide();
35853        // }
35854         this.visible = false;
35855         this.fireEvent("visibilitychange", this, false);
35856     },
35857
35858     /**
35859      * Shows this region if it was previously hidden.
35860      */
35861     show : function(){
35862         //if(!this.collapsed){
35863             this.el.show();
35864         //}else{
35865         //    this.collapsedEl.show();
35866        // }
35867         this.visible = true;
35868         this.fireEvent("visibilitychange", this, true);
35869     },
35870 /*
35871     closeClicked : function(){
35872         if(this.activePanel){
35873             this.remove(this.activePanel);
35874         }
35875     },
35876
35877     collapseClick : function(e){
35878         if(this.isSlid){
35879            e.stopPropagation();
35880            this.slideIn();
35881         }else{
35882            e.stopPropagation();
35883            this.slideOut();
35884         }
35885     },
35886 */
35887     /**
35888      * Collapses this region.
35889      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35890      */
35891     /*
35892     collapse : function(skipAnim, skipCheck = false){
35893         if(this.collapsed) {
35894             return;
35895         }
35896         
35897         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35898             
35899             this.collapsed = true;
35900             if(this.split){
35901                 this.split.el.hide();
35902             }
35903             if(this.config.animate && skipAnim !== true){
35904                 this.fireEvent("invalidated", this);
35905                 this.animateCollapse();
35906             }else{
35907                 this.el.setLocation(-20000,-20000);
35908                 this.el.hide();
35909                 this.collapsedEl.show();
35910                 this.fireEvent("collapsed", this);
35911                 this.fireEvent("invalidated", this);
35912             }
35913         }
35914         
35915     },
35916 */
35917     animateCollapse : function(){
35918         // overridden
35919     },
35920
35921     /**
35922      * Expands this region if it was previously collapsed.
35923      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35924      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35925      */
35926     /*
35927     expand : function(e, skipAnim){
35928         if(e) {
35929             e.stopPropagation();
35930         }
35931         if(!this.collapsed || this.el.hasActiveFx()) {
35932             return;
35933         }
35934         if(this.isSlid){
35935             this.afterSlideIn();
35936             skipAnim = true;
35937         }
35938         this.collapsed = false;
35939         if(this.config.animate && skipAnim !== true){
35940             this.animateExpand();
35941         }else{
35942             this.el.show();
35943             if(this.split){
35944                 this.split.el.show();
35945             }
35946             this.collapsedEl.setLocation(-2000,-2000);
35947             this.collapsedEl.hide();
35948             this.fireEvent("invalidated", this);
35949             this.fireEvent("expanded", this);
35950         }
35951     },
35952 */
35953     animateExpand : function(){
35954         // overridden
35955     },
35956
35957     initTabs : function()
35958     {
35959         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35960         
35961         var ts = new Roo.bootstrap.panel.Tabs({
35962                 el: this.bodyEl.dom,
35963                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35964                 disableTooltips: this.config.disableTabTips,
35965                 toolbar : this.config.toolbar
35966             });
35967         
35968         if(this.config.hideTabs){
35969             ts.stripWrap.setDisplayed(false);
35970         }
35971         this.tabs = ts;
35972         ts.resizeTabs = this.config.resizeTabs === true;
35973         ts.minTabWidth = this.config.minTabWidth || 40;
35974         ts.maxTabWidth = this.config.maxTabWidth || 250;
35975         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35976         ts.monitorResize = false;
35977         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35978         ts.bodyEl.addClass('roo-layout-tabs-body');
35979         this.panels.each(this.initPanelAsTab, this);
35980     },
35981
35982     initPanelAsTab : function(panel){
35983         var ti = this.tabs.addTab(
35984             panel.getEl().id,
35985             panel.getTitle(),
35986             null,
35987             this.config.closeOnTab && panel.isClosable(),
35988             panel.tpl
35989         );
35990         if(panel.tabTip !== undefined){
35991             ti.setTooltip(panel.tabTip);
35992         }
35993         ti.on("activate", function(){
35994               this.setActivePanel(panel);
35995         }, this);
35996         
35997         if(this.config.closeOnTab){
35998             ti.on("beforeclose", function(t, e){
35999                 e.cancel = true;
36000                 this.remove(panel);
36001             }, this);
36002         }
36003         
36004         panel.tabItem = ti;
36005         
36006         return ti;
36007     },
36008
36009     updatePanelTitle : function(panel, title)
36010     {
36011         if(this.activePanel == panel){
36012             this.updateTitle(title);
36013         }
36014         if(this.tabs){
36015             var ti = this.tabs.getTab(panel.getEl().id);
36016             ti.setText(title);
36017             if(panel.tabTip !== undefined){
36018                 ti.setTooltip(panel.tabTip);
36019             }
36020         }
36021     },
36022
36023     updateTitle : function(title){
36024         if(this.titleTextEl && !this.config.title){
36025             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36026         }
36027     },
36028
36029     setActivePanel : function(panel)
36030     {
36031         panel = this.getPanel(panel);
36032         if(this.activePanel && this.activePanel != panel){
36033             if(this.activePanel.setActiveState(false) === false){
36034                 return;
36035             }
36036         }
36037         this.activePanel = panel;
36038         panel.setActiveState(true);
36039         if(this.panelSize){
36040             panel.setSize(this.panelSize.width, this.panelSize.height);
36041         }
36042         if(this.closeBtn){
36043             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36044         }
36045         this.updateTitle(panel.getTitle());
36046         if(this.tabs){
36047             this.fireEvent("invalidated", this);
36048         }
36049         this.fireEvent("panelactivated", this, panel);
36050     },
36051
36052     /**
36053      * Shows the specified panel.
36054      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36055      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36056      */
36057     showPanel : function(panel)
36058     {
36059         panel = this.getPanel(panel);
36060         if(panel){
36061             if(this.tabs){
36062                 var tab = this.tabs.getTab(panel.getEl().id);
36063                 if(tab.isHidden()){
36064                     this.tabs.unhideTab(tab.id);
36065                 }
36066                 tab.activate();
36067             }else{
36068                 this.setActivePanel(panel);
36069             }
36070         }
36071         return panel;
36072     },
36073
36074     /**
36075      * Get the active panel for this region.
36076      * @return {Roo.ContentPanel} The active panel or null
36077      */
36078     getActivePanel : function(){
36079         return this.activePanel;
36080     },
36081
36082     validateVisibility : function(){
36083         if(this.panels.getCount() < 1){
36084             this.updateTitle("&#160;");
36085             this.closeBtn.hide();
36086             this.hide();
36087         }else{
36088             if(!this.isVisible()){
36089                 this.show();
36090             }
36091         }
36092     },
36093
36094     /**
36095      * Adds the passed ContentPanel(s) to this region.
36096      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36097      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36098      */
36099     add : function(panel)
36100     {
36101         if(arguments.length > 1){
36102             for(var i = 0, len = arguments.length; i < len; i++) {
36103                 this.add(arguments[i]);
36104             }
36105             return null;
36106         }
36107         
36108         // if we have not been rendered yet, then we can not really do much of this..
36109         if (!this.bodyEl) {
36110             this.unrendered_panels.push(panel);
36111             return panel;
36112         }
36113         
36114         
36115         
36116         
36117         if(this.hasPanel(panel)){
36118             this.showPanel(panel);
36119             return panel;
36120         }
36121         panel.setRegion(this);
36122         this.panels.add(panel);
36123        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36124             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36125             // and hide them... ???
36126             this.bodyEl.dom.appendChild(panel.getEl().dom);
36127             if(panel.background !== true){
36128                 this.setActivePanel(panel);
36129             }
36130             this.fireEvent("paneladded", this, panel);
36131             return panel;
36132         }
36133         */
36134         if(!this.tabs){
36135             this.initTabs();
36136         }else{
36137             this.initPanelAsTab(panel);
36138         }
36139         
36140         
36141         if(panel.background !== true){
36142             this.tabs.activate(panel.getEl().id);
36143         }
36144         this.fireEvent("paneladded", this, panel);
36145         return panel;
36146     },
36147
36148     /**
36149      * Hides the tab for the specified panel.
36150      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36151      */
36152     hidePanel : function(panel){
36153         if(this.tabs && (panel = this.getPanel(panel))){
36154             this.tabs.hideTab(panel.getEl().id);
36155         }
36156     },
36157
36158     /**
36159      * Unhides the tab for a previously hidden panel.
36160      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36161      */
36162     unhidePanel : function(panel){
36163         if(this.tabs && (panel = this.getPanel(panel))){
36164             this.tabs.unhideTab(panel.getEl().id);
36165         }
36166     },
36167
36168     clearPanels : function(){
36169         while(this.panels.getCount() > 0){
36170              this.remove(this.panels.first());
36171         }
36172     },
36173
36174     /**
36175      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36176      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36177      * @param {Boolean} preservePanel Overrides the config preservePanel option
36178      * @return {Roo.ContentPanel} The panel that was removed
36179      */
36180     remove : function(panel, preservePanel)
36181     {
36182         panel = this.getPanel(panel);
36183         if(!panel){
36184             return null;
36185         }
36186         var e = {};
36187         this.fireEvent("beforeremove", this, panel, e);
36188         if(e.cancel === true){
36189             return null;
36190         }
36191         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36192         var panelId = panel.getId();
36193         this.panels.removeKey(panelId);
36194         if(preservePanel){
36195             document.body.appendChild(panel.getEl().dom);
36196         }
36197         if(this.tabs){
36198             this.tabs.removeTab(panel.getEl().id);
36199         }else if (!preservePanel){
36200             this.bodyEl.dom.removeChild(panel.getEl().dom);
36201         }
36202         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36203             var p = this.panels.first();
36204             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36205             tempEl.appendChild(p.getEl().dom);
36206             this.bodyEl.update("");
36207             this.bodyEl.dom.appendChild(p.getEl().dom);
36208             tempEl = null;
36209             this.updateTitle(p.getTitle());
36210             this.tabs = null;
36211             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36212             this.setActivePanel(p);
36213         }
36214         panel.setRegion(null);
36215         if(this.activePanel == panel){
36216             this.activePanel = null;
36217         }
36218         if(this.config.autoDestroy !== false && preservePanel !== true){
36219             try{panel.destroy();}catch(e){}
36220         }
36221         this.fireEvent("panelremoved", this, panel);
36222         return panel;
36223     },
36224
36225     /**
36226      * Returns the TabPanel component used by this region
36227      * @return {Roo.TabPanel}
36228      */
36229     getTabs : function(){
36230         return this.tabs;
36231     },
36232
36233     createTool : function(parentEl, className){
36234         var btn = Roo.DomHelper.append(parentEl, {
36235             tag: "div",
36236             cls: "x-layout-tools-button",
36237             children: [ {
36238                 tag: "div",
36239                 cls: "roo-layout-tools-button-inner " + className,
36240                 html: "&#160;"
36241             }]
36242         }, true);
36243         btn.addClassOnOver("roo-layout-tools-button-over");
36244         return btn;
36245     }
36246 });/*
36247  * Based on:
36248  * Ext JS Library 1.1.1
36249  * Copyright(c) 2006-2007, Ext JS, LLC.
36250  *
36251  * Originally Released Under LGPL - original licence link has changed is not relivant.
36252  *
36253  * Fork - LGPL
36254  * <script type="text/javascript">
36255  */
36256  
36257
36258
36259 /**
36260  * @class Roo.SplitLayoutRegion
36261  * @extends Roo.LayoutRegion
36262  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36263  */
36264 Roo.bootstrap.layout.Split = function(config){
36265     this.cursor = config.cursor;
36266     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36267 };
36268
36269 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36270 {
36271     splitTip : "Drag to resize.",
36272     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36273     useSplitTips : false,
36274
36275     applyConfig : function(config){
36276         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36277     },
36278     
36279     onRender : function(ctr,pos) {
36280         
36281         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36282         if(!this.config.split){
36283             return;
36284         }
36285         if(!this.split){
36286             
36287             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36288                             tag: "div",
36289                             id: this.el.id + "-split",
36290                             cls: "roo-layout-split roo-layout-split-"+this.position,
36291                             html: "&#160;"
36292             });
36293             /** The SplitBar for this region 
36294             * @type Roo.SplitBar */
36295             // does not exist yet...
36296             Roo.log([this.position, this.orientation]);
36297             
36298             this.split = new Roo.bootstrap.SplitBar({
36299                 dragElement : splitEl,
36300                 resizingElement: this.el,
36301                 orientation : this.orientation
36302             });
36303             
36304             this.split.on("moved", this.onSplitMove, this);
36305             this.split.useShim = this.config.useShim === true;
36306             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36307             if(this.useSplitTips){
36308                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36309             }
36310             //if(config.collapsible){
36311             //    this.split.el.on("dblclick", this.collapse,  this);
36312             //}
36313         }
36314         if(typeof this.config.minSize != "undefined"){
36315             this.split.minSize = this.config.minSize;
36316         }
36317         if(typeof this.config.maxSize != "undefined"){
36318             this.split.maxSize = this.config.maxSize;
36319         }
36320         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36321             this.hideSplitter();
36322         }
36323         
36324     },
36325
36326     getHMaxSize : function(){
36327          var cmax = this.config.maxSize || 10000;
36328          var center = this.mgr.getRegion("center");
36329          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36330     },
36331
36332     getVMaxSize : function(){
36333          var cmax = this.config.maxSize || 10000;
36334          var center = this.mgr.getRegion("center");
36335          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36336     },
36337
36338     onSplitMove : function(split, newSize){
36339         this.fireEvent("resized", this, newSize);
36340     },
36341     
36342     /** 
36343      * Returns the {@link Roo.SplitBar} for this region.
36344      * @return {Roo.SplitBar}
36345      */
36346     getSplitBar : function(){
36347         return this.split;
36348     },
36349     
36350     hide : function(){
36351         this.hideSplitter();
36352         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36353     },
36354
36355     hideSplitter : function(){
36356         if(this.split){
36357             this.split.el.setLocation(-2000,-2000);
36358             this.split.el.hide();
36359         }
36360     },
36361
36362     show : function(){
36363         if(this.split){
36364             this.split.el.show();
36365         }
36366         Roo.bootstrap.layout.Split.superclass.show.call(this);
36367     },
36368     
36369     beforeSlide: function(){
36370         if(Roo.isGecko){// firefox overflow auto bug workaround
36371             this.bodyEl.clip();
36372             if(this.tabs) {
36373                 this.tabs.bodyEl.clip();
36374             }
36375             if(this.activePanel){
36376                 this.activePanel.getEl().clip();
36377                 
36378                 if(this.activePanel.beforeSlide){
36379                     this.activePanel.beforeSlide();
36380                 }
36381             }
36382         }
36383     },
36384     
36385     afterSlide : function(){
36386         if(Roo.isGecko){// firefox overflow auto bug workaround
36387             this.bodyEl.unclip();
36388             if(this.tabs) {
36389                 this.tabs.bodyEl.unclip();
36390             }
36391             if(this.activePanel){
36392                 this.activePanel.getEl().unclip();
36393                 if(this.activePanel.afterSlide){
36394                     this.activePanel.afterSlide();
36395                 }
36396             }
36397         }
36398     },
36399
36400     initAutoHide : function(){
36401         if(this.autoHide !== false){
36402             if(!this.autoHideHd){
36403                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36404                 this.autoHideHd = {
36405                     "mouseout": function(e){
36406                         if(!e.within(this.el, true)){
36407                             st.delay(500);
36408                         }
36409                     },
36410                     "mouseover" : function(e){
36411                         st.cancel();
36412                     },
36413                     scope : this
36414                 };
36415             }
36416             this.el.on(this.autoHideHd);
36417         }
36418     },
36419
36420     clearAutoHide : function(){
36421         if(this.autoHide !== false){
36422             this.el.un("mouseout", this.autoHideHd.mouseout);
36423             this.el.un("mouseover", this.autoHideHd.mouseover);
36424         }
36425     },
36426
36427     clearMonitor : function(){
36428         Roo.get(document).un("click", this.slideInIf, this);
36429     },
36430
36431     // these names are backwards but not changed for compat
36432     slideOut : function(){
36433         if(this.isSlid || this.el.hasActiveFx()){
36434             return;
36435         }
36436         this.isSlid = true;
36437         if(this.collapseBtn){
36438             this.collapseBtn.hide();
36439         }
36440         this.closeBtnState = this.closeBtn.getStyle('display');
36441         this.closeBtn.hide();
36442         if(this.stickBtn){
36443             this.stickBtn.show();
36444         }
36445         this.el.show();
36446         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36447         this.beforeSlide();
36448         this.el.setStyle("z-index", 10001);
36449         this.el.slideIn(this.getSlideAnchor(), {
36450             callback: function(){
36451                 this.afterSlide();
36452                 this.initAutoHide();
36453                 Roo.get(document).on("click", this.slideInIf, this);
36454                 this.fireEvent("slideshow", this);
36455             },
36456             scope: this,
36457             block: true
36458         });
36459     },
36460
36461     afterSlideIn : function(){
36462         this.clearAutoHide();
36463         this.isSlid = false;
36464         this.clearMonitor();
36465         this.el.setStyle("z-index", "");
36466         if(this.collapseBtn){
36467             this.collapseBtn.show();
36468         }
36469         this.closeBtn.setStyle('display', this.closeBtnState);
36470         if(this.stickBtn){
36471             this.stickBtn.hide();
36472         }
36473         this.fireEvent("slidehide", this);
36474     },
36475
36476     slideIn : function(cb){
36477         if(!this.isSlid || this.el.hasActiveFx()){
36478             Roo.callback(cb);
36479             return;
36480         }
36481         this.isSlid = false;
36482         this.beforeSlide();
36483         this.el.slideOut(this.getSlideAnchor(), {
36484             callback: function(){
36485                 this.el.setLeftTop(-10000, -10000);
36486                 this.afterSlide();
36487                 this.afterSlideIn();
36488                 Roo.callback(cb);
36489             },
36490             scope: this,
36491             block: true
36492         });
36493     },
36494     
36495     slideInIf : function(e){
36496         if(!e.within(this.el)){
36497             this.slideIn();
36498         }
36499     },
36500
36501     animateCollapse : function(){
36502         this.beforeSlide();
36503         this.el.setStyle("z-index", 20000);
36504         var anchor = this.getSlideAnchor();
36505         this.el.slideOut(anchor, {
36506             callback : function(){
36507                 this.el.setStyle("z-index", "");
36508                 this.collapsedEl.slideIn(anchor, {duration:.3});
36509                 this.afterSlide();
36510                 this.el.setLocation(-10000,-10000);
36511                 this.el.hide();
36512                 this.fireEvent("collapsed", this);
36513             },
36514             scope: this,
36515             block: true
36516         });
36517     },
36518
36519     animateExpand : function(){
36520         this.beforeSlide();
36521         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36522         this.el.setStyle("z-index", 20000);
36523         this.collapsedEl.hide({
36524             duration:.1
36525         });
36526         this.el.slideIn(this.getSlideAnchor(), {
36527             callback : function(){
36528                 this.el.setStyle("z-index", "");
36529                 this.afterSlide();
36530                 if(this.split){
36531                     this.split.el.show();
36532                 }
36533                 this.fireEvent("invalidated", this);
36534                 this.fireEvent("expanded", this);
36535             },
36536             scope: this,
36537             block: true
36538         });
36539     },
36540
36541     anchors : {
36542         "west" : "left",
36543         "east" : "right",
36544         "north" : "top",
36545         "south" : "bottom"
36546     },
36547
36548     sanchors : {
36549         "west" : "l",
36550         "east" : "r",
36551         "north" : "t",
36552         "south" : "b"
36553     },
36554
36555     canchors : {
36556         "west" : "tl-tr",
36557         "east" : "tr-tl",
36558         "north" : "tl-bl",
36559         "south" : "bl-tl"
36560     },
36561
36562     getAnchor : function(){
36563         return this.anchors[this.position];
36564     },
36565
36566     getCollapseAnchor : function(){
36567         return this.canchors[this.position];
36568     },
36569
36570     getSlideAnchor : function(){
36571         return this.sanchors[this.position];
36572     },
36573
36574     getAlignAdj : function(){
36575         var cm = this.cmargins;
36576         switch(this.position){
36577             case "west":
36578                 return [0, 0];
36579             break;
36580             case "east":
36581                 return [0, 0];
36582             break;
36583             case "north":
36584                 return [0, 0];
36585             break;
36586             case "south":
36587                 return [0, 0];
36588             break;
36589         }
36590     },
36591
36592     getExpandAdj : function(){
36593         var c = this.collapsedEl, cm = this.cmargins;
36594         switch(this.position){
36595             case "west":
36596                 return [-(cm.right+c.getWidth()+cm.left), 0];
36597             break;
36598             case "east":
36599                 return [cm.right+c.getWidth()+cm.left, 0];
36600             break;
36601             case "north":
36602                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36603             break;
36604             case "south":
36605                 return [0, cm.top+cm.bottom+c.getHeight()];
36606             break;
36607         }
36608     }
36609 });/*
36610  * Based on:
36611  * Ext JS Library 1.1.1
36612  * Copyright(c) 2006-2007, Ext JS, LLC.
36613  *
36614  * Originally Released Under LGPL - original licence link has changed is not relivant.
36615  *
36616  * Fork - LGPL
36617  * <script type="text/javascript">
36618  */
36619 /*
36620  * These classes are private internal classes
36621  */
36622 Roo.bootstrap.layout.Center = function(config){
36623     config.region = "center";
36624     Roo.bootstrap.layout.Region.call(this, config);
36625     this.visible = true;
36626     this.minWidth = config.minWidth || 20;
36627     this.minHeight = config.minHeight || 20;
36628 };
36629
36630 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36631     hide : function(){
36632         // center panel can't be hidden
36633     },
36634     
36635     show : function(){
36636         // center panel can't be hidden
36637     },
36638     
36639     getMinWidth: function(){
36640         return this.minWidth;
36641     },
36642     
36643     getMinHeight: function(){
36644         return this.minHeight;
36645     }
36646 });
36647
36648
36649
36650
36651  
36652
36653
36654
36655
36656
36657 Roo.bootstrap.layout.North = function(config)
36658 {
36659     config.region = 'north';
36660     config.cursor = 'n-resize';
36661     
36662     Roo.bootstrap.layout.Split.call(this, config);
36663     
36664     
36665     if(this.split){
36666         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36667         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36668         this.split.el.addClass("roo-layout-split-v");
36669     }
36670     var size = config.initialSize || config.height;
36671     if(typeof size != "undefined"){
36672         this.el.setHeight(size);
36673     }
36674 };
36675 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36676 {
36677     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36678     
36679     
36680     
36681     getBox : function(){
36682         if(this.collapsed){
36683             return this.collapsedEl.getBox();
36684         }
36685         var box = this.el.getBox();
36686         if(this.split){
36687             box.height += this.split.el.getHeight();
36688         }
36689         return box;
36690     },
36691     
36692     updateBox : function(box){
36693         if(this.split && !this.collapsed){
36694             box.height -= this.split.el.getHeight();
36695             this.split.el.setLeft(box.x);
36696             this.split.el.setTop(box.y+box.height);
36697             this.split.el.setWidth(box.width);
36698         }
36699         if(this.collapsed){
36700             this.updateBody(box.width, null);
36701         }
36702         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36703     }
36704 });
36705
36706
36707
36708
36709
36710 Roo.bootstrap.layout.South = function(config){
36711     config.region = 'south';
36712     config.cursor = 's-resize';
36713     Roo.bootstrap.layout.Split.call(this, config);
36714     if(this.split){
36715         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36716         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36717         this.split.el.addClass("roo-layout-split-v");
36718     }
36719     var size = config.initialSize || config.height;
36720     if(typeof size != "undefined"){
36721         this.el.setHeight(size);
36722     }
36723 };
36724
36725 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36726     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36727     getBox : function(){
36728         if(this.collapsed){
36729             return this.collapsedEl.getBox();
36730         }
36731         var box = this.el.getBox();
36732         if(this.split){
36733             var sh = this.split.el.getHeight();
36734             box.height += sh;
36735             box.y -= sh;
36736         }
36737         return box;
36738     },
36739     
36740     updateBox : function(box){
36741         if(this.split && !this.collapsed){
36742             var sh = this.split.el.getHeight();
36743             box.height -= sh;
36744             box.y += sh;
36745             this.split.el.setLeft(box.x);
36746             this.split.el.setTop(box.y-sh);
36747             this.split.el.setWidth(box.width);
36748         }
36749         if(this.collapsed){
36750             this.updateBody(box.width, null);
36751         }
36752         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36753     }
36754 });
36755
36756 Roo.bootstrap.layout.East = function(config){
36757     config.region = "east";
36758     config.cursor = "e-resize";
36759     Roo.bootstrap.layout.Split.call(this, config);
36760     if(this.split){
36761         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36762         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36763         this.split.el.addClass("roo-layout-split-h");
36764     }
36765     var size = config.initialSize || config.width;
36766     if(typeof size != "undefined"){
36767         this.el.setWidth(size);
36768     }
36769 };
36770 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36771     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36772     getBox : function(){
36773         if(this.collapsed){
36774             return this.collapsedEl.getBox();
36775         }
36776         var box = this.el.getBox();
36777         if(this.split){
36778             var sw = this.split.el.getWidth();
36779             box.width += sw;
36780             box.x -= sw;
36781         }
36782         return box;
36783     },
36784
36785     updateBox : function(box){
36786         if(this.split && !this.collapsed){
36787             var sw = this.split.el.getWidth();
36788             box.width -= sw;
36789             this.split.el.setLeft(box.x);
36790             this.split.el.setTop(box.y);
36791             this.split.el.setHeight(box.height);
36792             box.x += sw;
36793         }
36794         if(this.collapsed){
36795             this.updateBody(null, box.height);
36796         }
36797         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36798     }
36799 });
36800
36801 Roo.bootstrap.layout.West = function(config){
36802     config.region = "west";
36803     config.cursor = "w-resize";
36804     
36805     Roo.bootstrap.layout.Split.call(this, config);
36806     if(this.split){
36807         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36808         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36809         this.split.el.addClass("roo-layout-split-h");
36810     }
36811     
36812 };
36813 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36814     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36815     
36816     onRender: function(ctr, pos)
36817     {
36818         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36819         var size = this.config.initialSize || this.config.width;
36820         if(typeof size != "undefined"){
36821             this.el.setWidth(size);
36822         }
36823     },
36824     
36825     getBox : function(){
36826         if(this.collapsed){
36827             return this.collapsedEl.getBox();
36828         }
36829         var box = this.el.getBox();
36830         if(this.split){
36831             box.width += this.split.el.getWidth();
36832         }
36833         return box;
36834     },
36835     
36836     updateBox : function(box){
36837         if(this.split && !this.collapsed){
36838             var sw = this.split.el.getWidth();
36839             box.width -= sw;
36840             this.split.el.setLeft(box.x+box.width);
36841             this.split.el.setTop(box.y);
36842             this.split.el.setHeight(box.height);
36843         }
36844         if(this.collapsed){
36845             this.updateBody(null, box.height);
36846         }
36847         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36848     }
36849 });
36850 Roo.namespace("Roo.bootstrap.panel");/*
36851  * Based on:
36852  * Ext JS Library 1.1.1
36853  * Copyright(c) 2006-2007, Ext JS, LLC.
36854  *
36855  * Originally Released Under LGPL - original licence link has changed is not relivant.
36856  *
36857  * Fork - LGPL
36858  * <script type="text/javascript">
36859  */
36860 /**
36861  * @class Roo.ContentPanel
36862  * @extends Roo.util.Observable
36863  * A basic ContentPanel element.
36864  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36865  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36866  * @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
36867  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36868  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36869  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36870  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36871  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36872  * @cfg {String} title          The title for this panel
36873  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36874  * @cfg {String} url            Calls {@link #setUrl} with this value
36875  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36876  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36877  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36878  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36879  * @cfg {Boolean} badges render the badges
36880
36881  * @constructor
36882  * Create a new ContentPanel.
36883  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36884  * @param {String/Object} config A string to set only the title or a config object
36885  * @param {String} content (optional) Set the HTML content for this panel
36886  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36887  */
36888 Roo.bootstrap.panel.Content = function( config){
36889     
36890     this.tpl = config.tpl || false;
36891     
36892     var el = config.el;
36893     var content = config.content;
36894
36895     if(config.autoCreate){ // xtype is available if this is called from factory
36896         el = Roo.id();
36897     }
36898     this.el = Roo.get(el);
36899     if(!this.el && config && config.autoCreate){
36900         if(typeof config.autoCreate == "object"){
36901             if(!config.autoCreate.id){
36902                 config.autoCreate.id = config.id||el;
36903             }
36904             this.el = Roo.DomHelper.append(document.body,
36905                         config.autoCreate, true);
36906         }else{
36907             var elcfg =  {   tag: "div",
36908                             cls: "roo-layout-inactive-content",
36909                             id: config.id||el
36910                             };
36911             if (config.html) {
36912                 elcfg.html = config.html;
36913                 
36914             }
36915                         
36916             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36917         }
36918     } 
36919     this.closable = false;
36920     this.loaded = false;
36921     this.active = false;
36922    
36923       
36924     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36925         
36926         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36927         
36928         this.wrapEl = this.el; //this.el.wrap();
36929         var ti = [];
36930         if (config.toolbar.items) {
36931             ti = config.toolbar.items ;
36932             delete config.toolbar.items ;
36933         }
36934         
36935         var nitems = [];
36936         this.toolbar.render(this.wrapEl, 'before');
36937         for(var i =0;i < ti.length;i++) {
36938           //  Roo.log(['add child', items[i]]);
36939             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36940         }
36941         this.toolbar.items = nitems;
36942         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36943         delete config.toolbar;
36944         
36945     }
36946     /*
36947     // xtype created footer. - not sure if will work as we normally have to render first..
36948     if (this.footer && !this.footer.el && this.footer.xtype) {
36949         if (!this.wrapEl) {
36950             this.wrapEl = this.el.wrap();
36951         }
36952     
36953         this.footer.container = this.wrapEl.createChild();
36954          
36955         this.footer = Roo.factory(this.footer, Roo);
36956         
36957     }
36958     */
36959     
36960      if(typeof config == "string"){
36961         this.title = config;
36962     }else{
36963         Roo.apply(this, config);
36964     }
36965     
36966     if(this.resizeEl){
36967         this.resizeEl = Roo.get(this.resizeEl, true);
36968     }else{
36969         this.resizeEl = this.el;
36970     }
36971     // handle view.xtype
36972     
36973  
36974     
36975     
36976     this.addEvents({
36977         /**
36978          * @event activate
36979          * Fires when this panel is activated. 
36980          * @param {Roo.ContentPanel} this
36981          */
36982         "activate" : true,
36983         /**
36984          * @event deactivate
36985          * Fires when this panel is activated. 
36986          * @param {Roo.ContentPanel} this
36987          */
36988         "deactivate" : true,
36989
36990         /**
36991          * @event resize
36992          * Fires when this panel is resized if fitToFrame is true.
36993          * @param {Roo.ContentPanel} this
36994          * @param {Number} width The width after any component adjustments
36995          * @param {Number} height The height after any component adjustments
36996          */
36997         "resize" : true,
36998         
36999          /**
37000          * @event render
37001          * Fires when this tab is created
37002          * @param {Roo.ContentPanel} this
37003          */
37004         "render" : true
37005         
37006         
37007         
37008     });
37009     
37010
37011     
37012     
37013     if(this.autoScroll){
37014         this.resizeEl.setStyle("overflow", "auto");
37015     } else {
37016         // fix randome scrolling
37017         //this.el.on('scroll', function() {
37018         //    Roo.log('fix random scolling');
37019         //    this.scrollTo('top',0); 
37020         //});
37021     }
37022     content = content || this.content;
37023     if(content){
37024         this.setContent(content);
37025     }
37026     if(config && config.url){
37027         this.setUrl(this.url, this.params, this.loadOnce);
37028     }
37029     
37030     
37031     
37032     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37033     
37034     if (this.view && typeof(this.view.xtype) != 'undefined') {
37035         this.view.el = this.el.appendChild(document.createElement("div"));
37036         this.view = Roo.factory(this.view); 
37037         this.view.render  &&  this.view.render(false, '');  
37038     }
37039     
37040     
37041     this.fireEvent('render', this);
37042 };
37043
37044 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37045     
37046     tabTip : '',
37047     
37048     setRegion : function(region){
37049         this.region = region;
37050         this.setActiveClass(region && !this.background);
37051     },
37052     
37053     
37054     setActiveClass: function(state)
37055     {
37056         if(state){
37057            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37058            this.el.setStyle('position','relative');
37059         }else{
37060            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37061            this.el.setStyle('position', 'absolute');
37062         } 
37063     },
37064     
37065     /**
37066      * Returns the toolbar for this Panel if one was configured. 
37067      * @return {Roo.Toolbar} 
37068      */
37069     getToolbar : function(){
37070         return this.toolbar;
37071     },
37072     
37073     setActiveState : function(active)
37074     {
37075         this.active = active;
37076         this.setActiveClass(active);
37077         if(!active){
37078             if(this.fireEvent("deactivate", this) === false){
37079                 return false;
37080             }
37081             return true;
37082         }
37083         this.fireEvent("activate", this);
37084         return true;
37085     },
37086     /**
37087      * Updates this panel's element
37088      * @param {String} content The new content
37089      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37090     */
37091     setContent : function(content, loadScripts){
37092         this.el.update(content, loadScripts);
37093     },
37094
37095     ignoreResize : function(w, h){
37096         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37097             return true;
37098         }else{
37099             this.lastSize = {width: w, height: h};
37100             return false;
37101         }
37102     },
37103     /**
37104      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37105      * @return {Roo.UpdateManager} The UpdateManager
37106      */
37107     getUpdateManager : function(){
37108         return this.el.getUpdateManager();
37109     },
37110      /**
37111      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37112      * @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:
37113 <pre><code>
37114 panel.load({
37115     url: "your-url.php",
37116     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37117     callback: yourFunction,
37118     scope: yourObject, //(optional scope)
37119     discardUrl: false,
37120     nocache: false,
37121     text: "Loading...",
37122     timeout: 30,
37123     scripts: false
37124 });
37125 </code></pre>
37126      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37127      * 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.
37128      * @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}
37129      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37130      * @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.
37131      * @return {Roo.ContentPanel} this
37132      */
37133     load : function(){
37134         var um = this.el.getUpdateManager();
37135         um.update.apply(um, arguments);
37136         return this;
37137     },
37138
37139
37140     /**
37141      * 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.
37142      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37143      * @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)
37144      * @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)
37145      * @return {Roo.UpdateManager} The UpdateManager
37146      */
37147     setUrl : function(url, params, loadOnce){
37148         if(this.refreshDelegate){
37149             this.removeListener("activate", this.refreshDelegate);
37150         }
37151         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37152         this.on("activate", this.refreshDelegate);
37153         return this.el.getUpdateManager();
37154     },
37155     
37156     _handleRefresh : function(url, params, loadOnce){
37157         if(!loadOnce || !this.loaded){
37158             var updater = this.el.getUpdateManager();
37159             updater.update(url, params, this._setLoaded.createDelegate(this));
37160         }
37161     },
37162     
37163     _setLoaded : function(){
37164         this.loaded = true;
37165     }, 
37166     
37167     /**
37168      * Returns this panel's id
37169      * @return {String} 
37170      */
37171     getId : function(){
37172         return this.el.id;
37173     },
37174     
37175     /** 
37176      * Returns this panel's element - used by regiosn to add.
37177      * @return {Roo.Element} 
37178      */
37179     getEl : function(){
37180         return this.wrapEl || this.el;
37181     },
37182     
37183    
37184     
37185     adjustForComponents : function(width, height)
37186     {
37187         //Roo.log('adjustForComponents ');
37188         if(this.resizeEl != this.el){
37189             width -= this.el.getFrameWidth('lr');
37190             height -= this.el.getFrameWidth('tb');
37191         }
37192         if(this.toolbar){
37193             var te = this.toolbar.getEl();
37194             te.setWidth(width);
37195             height -= te.getHeight();
37196         }
37197         if(this.footer){
37198             var te = this.footer.getEl();
37199             te.setWidth(width);
37200             height -= te.getHeight();
37201         }
37202         
37203         
37204         if(this.adjustments){
37205             width += this.adjustments[0];
37206             height += this.adjustments[1];
37207         }
37208         return {"width": width, "height": height};
37209     },
37210     
37211     setSize : function(width, height){
37212         if(this.fitToFrame && !this.ignoreResize(width, height)){
37213             if(this.fitContainer && this.resizeEl != this.el){
37214                 this.el.setSize(width, height);
37215             }
37216             var size = this.adjustForComponents(width, height);
37217             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37218             this.fireEvent('resize', this, size.width, size.height);
37219         }
37220     },
37221     
37222     /**
37223      * Returns this panel's title
37224      * @return {String} 
37225      */
37226     getTitle : function(){
37227         
37228         if (typeof(this.title) != 'object') {
37229             return this.title;
37230         }
37231         
37232         var t = '';
37233         for (var k in this.title) {
37234             if (!this.title.hasOwnProperty(k)) {
37235                 continue;
37236             }
37237             
37238             if (k.indexOf('-') >= 0) {
37239                 var s = k.split('-');
37240                 for (var i = 0; i<s.length; i++) {
37241                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37242                 }
37243             } else {
37244                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37245             }
37246         }
37247         return t;
37248     },
37249     
37250     /**
37251      * Set this panel's title
37252      * @param {String} title
37253      */
37254     setTitle : function(title){
37255         this.title = title;
37256         if(this.region){
37257             this.region.updatePanelTitle(this, title);
37258         }
37259     },
37260     
37261     /**
37262      * Returns true is this panel was configured to be closable
37263      * @return {Boolean} 
37264      */
37265     isClosable : function(){
37266         return this.closable;
37267     },
37268     
37269     beforeSlide : function(){
37270         this.el.clip();
37271         this.resizeEl.clip();
37272     },
37273     
37274     afterSlide : function(){
37275         this.el.unclip();
37276         this.resizeEl.unclip();
37277     },
37278     
37279     /**
37280      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37281      *   Will fail silently if the {@link #setUrl} method has not been called.
37282      *   This does not activate the panel, just updates its content.
37283      */
37284     refresh : function(){
37285         if(this.refreshDelegate){
37286            this.loaded = false;
37287            this.refreshDelegate();
37288         }
37289     },
37290     
37291     /**
37292      * Destroys this panel
37293      */
37294     destroy : function(){
37295         this.el.removeAllListeners();
37296         var tempEl = document.createElement("span");
37297         tempEl.appendChild(this.el.dom);
37298         tempEl.innerHTML = "";
37299         this.el.remove();
37300         this.el = null;
37301     },
37302     
37303     /**
37304      * form - if the content panel contains a form - this is a reference to it.
37305      * @type {Roo.form.Form}
37306      */
37307     form : false,
37308     /**
37309      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37310      *    This contains a reference to it.
37311      * @type {Roo.View}
37312      */
37313     view : false,
37314     
37315       /**
37316      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37317      * <pre><code>
37318
37319 layout.addxtype({
37320        xtype : 'Form',
37321        items: [ .... ]
37322    }
37323 );
37324
37325 </code></pre>
37326      * @param {Object} cfg Xtype definition of item to add.
37327      */
37328     
37329     
37330     getChildContainer: function () {
37331         return this.getEl();
37332     }
37333     
37334     
37335     /*
37336         var  ret = new Roo.factory(cfg);
37337         return ret;
37338         
37339         
37340         // add form..
37341         if (cfg.xtype.match(/^Form$/)) {
37342             
37343             var el;
37344             //if (this.footer) {
37345             //    el = this.footer.container.insertSibling(false, 'before');
37346             //} else {
37347                 el = this.el.createChild();
37348             //}
37349
37350             this.form = new  Roo.form.Form(cfg);
37351             
37352             
37353             if ( this.form.allItems.length) {
37354                 this.form.render(el.dom);
37355             }
37356             return this.form;
37357         }
37358         // should only have one of theses..
37359         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37360             // views.. should not be just added - used named prop 'view''
37361             
37362             cfg.el = this.el.appendChild(document.createElement("div"));
37363             // factory?
37364             
37365             var ret = new Roo.factory(cfg);
37366              
37367              ret.render && ret.render(false, ''); // render blank..
37368             this.view = ret;
37369             return ret;
37370         }
37371         return false;
37372     }
37373     \*/
37374 });
37375  
37376 /**
37377  * @class Roo.bootstrap.panel.Grid
37378  * @extends Roo.bootstrap.panel.Content
37379  * @constructor
37380  * Create a new GridPanel.
37381  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37382  * @param {Object} config A the config object
37383   
37384  */
37385
37386
37387
37388 Roo.bootstrap.panel.Grid = function(config)
37389 {
37390     
37391       
37392     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37393         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37394
37395     config.el = this.wrapper;
37396     //this.el = this.wrapper;
37397     
37398       if (config.container) {
37399         // ctor'ed from a Border/panel.grid
37400         
37401         
37402         this.wrapper.setStyle("overflow", "hidden");
37403         this.wrapper.addClass('roo-grid-container');
37404
37405     }
37406     
37407     
37408     if(config.toolbar){
37409         var tool_el = this.wrapper.createChild();    
37410         this.toolbar = Roo.factory(config.toolbar);
37411         var ti = [];
37412         if (config.toolbar.items) {
37413             ti = config.toolbar.items ;
37414             delete config.toolbar.items ;
37415         }
37416         
37417         var nitems = [];
37418         this.toolbar.render(tool_el);
37419         for(var i =0;i < ti.length;i++) {
37420           //  Roo.log(['add child', items[i]]);
37421             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37422         }
37423         this.toolbar.items = nitems;
37424         
37425         delete config.toolbar;
37426     }
37427     
37428     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37429     config.grid.scrollBody = true;;
37430     config.grid.monitorWindowResize = false; // turn off autosizing
37431     config.grid.autoHeight = false;
37432     config.grid.autoWidth = false;
37433     
37434     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37435     
37436     if (config.background) {
37437         // render grid on panel activation (if panel background)
37438         this.on('activate', function(gp) {
37439             if (!gp.grid.rendered) {
37440                 gp.grid.render(this.wrapper);
37441                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37442             }
37443         });
37444             
37445     } else {
37446         this.grid.render(this.wrapper);
37447         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37448
37449     }
37450     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37451     // ??? needed ??? config.el = this.wrapper;
37452     
37453     
37454     
37455   
37456     // xtype created footer. - not sure if will work as we normally have to render first..
37457     if (this.footer && !this.footer.el && this.footer.xtype) {
37458         
37459         var ctr = this.grid.getView().getFooterPanel(true);
37460         this.footer.dataSource = this.grid.dataSource;
37461         this.footer = Roo.factory(this.footer, Roo);
37462         this.footer.render(ctr);
37463         
37464     }
37465     
37466     
37467     
37468     
37469      
37470 };
37471
37472 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37473     getId : function(){
37474         return this.grid.id;
37475     },
37476     
37477     /**
37478      * Returns the grid for this panel
37479      * @return {Roo.bootstrap.Table} 
37480      */
37481     getGrid : function(){
37482         return this.grid;    
37483     },
37484     
37485     setSize : function(width, height){
37486         if(!this.ignoreResize(width, height)){
37487             var grid = this.grid;
37488             var size = this.adjustForComponents(width, height);
37489             var gridel = grid.getGridEl();
37490             gridel.setSize(size.width, size.height);
37491             /*
37492             var thd = grid.getGridEl().select('thead',true).first();
37493             var tbd = grid.getGridEl().select('tbody', true).first();
37494             if (tbd) {
37495                 tbd.setSize(width, height - thd.getHeight());
37496             }
37497             */
37498             grid.autoSize();
37499         }
37500     },
37501      
37502     
37503     
37504     beforeSlide : function(){
37505         this.grid.getView().scroller.clip();
37506     },
37507     
37508     afterSlide : function(){
37509         this.grid.getView().scroller.unclip();
37510     },
37511     
37512     destroy : function(){
37513         this.grid.destroy();
37514         delete this.grid;
37515         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37516     }
37517 });
37518
37519 /**
37520  * @class Roo.bootstrap.panel.Nest
37521  * @extends Roo.bootstrap.panel.Content
37522  * @constructor
37523  * Create a new Panel, that can contain a layout.Border.
37524  * 
37525  * 
37526  * @param {Roo.BorderLayout} layout The layout for this panel
37527  * @param {String/Object} config A string to set only the title or a config object
37528  */
37529 Roo.bootstrap.panel.Nest = function(config)
37530 {
37531     // construct with only one argument..
37532     /* FIXME - implement nicer consturctors
37533     if (layout.layout) {
37534         config = layout;
37535         layout = config.layout;
37536         delete config.layout;
37537     }
37538     if (layout.xtype && !layout.getEl) {
37539         // then layout needs constructing..
37540         layout = Roo.factory(layout, Roo);
37541     }
37542     */
37543     
37544     config.el =  config.layout.getEl();
37545     
37546     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37547     
37548     config.layout.monitorWindowResize = false; // turn off autosizing
37549     this.layout = config.layout;
37550     this.layout.getEl().addClass("roo-layout-nested-layout");
37551     
37552     
37553     
37554     
37555 };
37556
37557 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37558
37559     setSize : function(width, height){
37560         if(!this.ignoreResize(width, height)){
37561             var size = this.adjustForComponents(width, height);
37562             var el = this.layout.getEl();
37563             if (size.height < 1) {
37564                 el.setWidth(size.width);   
37565             } else {
37566                 el.setSize(size.width, size.height);
37567             }
37568             var touch = el.dom.offsetWidth;
37569             this.layout.layout();
37570             // ie requires a double layout on the first pass
37571             if(Roo.isIE && !this.initialized){
37572                 this.initialized = true;
37573                 this.layout.layout();
37574             }
37575         }
37576     },
37577     
37578     // activate all subpanels if not currently active..
37579     
37580     setActiveState : function(active){
37581         this.active = active;
37582         this.setActiveClass(active);
37583         
37584         if(!active){
37585             this.fireEvent("deactivate", this);
37586             return;
37587         }
37588         
37589         this.fireEvent("activate", this);
37590         // not sure if this should happen before or after..
37591         if (!this.layout) {
37592             return; // should not happen..
37593         }
37594         var reg = false;
37595         for (var r in this.layout.regions) {
37596             reg = this.layout.getRegion(r);
37597             if (reg.getActivePanel()) {
37598                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37599                 reg.setActivePanel(reg.getActivePanel());
37600                 continue;
37601             }
37602             if (!reg.panels.length) {
37603                 continue;
37604             }
37605             reg.showPanel(reg.getPanel(0));
37606         }
37607         
37608         
37609         
37610         
37611     },
37612     
37613     /**
37614      * Returns the nested BorderLayout for this panel
37615      * @return {Roo.BorderLayout} 
37616      */
37617     getLayout : function(){
37618         return this.layout;
37619     },
37620     
37621      /**
37622      * Adds a xtype elements to the layout of the nested panel
37623      * <pre><code>
37624
37625 panel.addxtype({
37626        xtype : 'ContentPanel',
37627        region: 'west',
37628        items: [ .... ]
37629    }
37630 );
37631
37632 panel.addxtype({
37633         xtype : 'NestedLayoutPanel',
37634         region: 'west',
37635         layout: {
37636            center: { },
37637            west: { }   
37638         },
37639         items : [ ... list of content panels or nested layout panels.. ]
37640    }
37641 );
37642 </code></pre>
37643      * @param {Object} cfg Xtype definition of item to add.
37644      */
37645     addxtype : function(cfg) {
37646         return this.layout.addxtype(cfg);
37647     
37648     }
37649 });        /*
37650  * Based on:
37651  * Ext JS Library 1.1.1
37652  * Copyright(c) 2006-2007, Ext JS, LLC.
37653  *
37654  * Originally Released Under LGPL - original licence link has changed is not relivant.
37655  *
37656  * Fork - LGPL
37657  * <script type="text/javascript">
37658  */
37659 /**
37660  * @class Roo.TabPanel
37661  * @extends Roo.util.Observable
37662  * A lightweight tab container.
37663  * <br><br>
37664  * Usage:
37665  * <pre><code>
37666 // basic tabs 1, built from existing content
37667 var tabs = new Roo.TabPanel("tabs1");
37668 tabs.addTab("script", "View Script");
37669 tabs.addTab("markup", "View Markup");
37670 tabs.activate("script");
37671
37672 // more advanced tabs, built from javascript
37673 var jtabs = new Roo.TabPanel("jtabs");
37674 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37675
37676 // set up the UpdateManager
37677 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37678 var updater = tab2.getUpdateManager();
37679 updater.setDefaultUrl("ajax1.htm");
37680 tab2.on('activate', updater.refresh, updater, true);
37681
37682 // Use setUrl for Ajax loading
37683 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37684 tab3.setUrl("ajax2.htm", null, true);
37685
37686 // Disabled tab
37687 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37688 tab4.disable();
37689
37690 jtabs.activate("jtabs-1");
37691  * </code></pre>
37692  * @constructor
37693  * Create a new TabPanel.
37694  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37695  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37696  */
37697 Roo.bootstrap.panel.Tabs = function(config){
37698     /**
37699     * The container element for this TabPanel.
37700     * @type Roo.Element
37701     */
37702     this.el = Roo.get(config.el);
37703     delete config.el;
37704     if(config){
37705         if(typeof config == "boolean"){
37706             this.tabPosition = config ? "bottom" : "top";
37707         }else{
37708             Roo.apply(this, config);
37709         }
37710     }
37711     
37712     if(this.tabPosition == "bottom"){
37713         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37714         this.el.addClass("roo-tabs-bottom");
37715     }
37716     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37717     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37718     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37719     if(Roo.isIE){
37720         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37721     }
37722     if(this.tabPosition != "bottom"){
37723         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37724          * @type Roo.Element
37725          */
37726         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37727         this.el.addClass("roo-tabs-top");
37728     }
37729     this.items = [];
37730
37731     this.bodyEl.setStyle("position", "relative");
37732
37733     this.active = null;
37734     this.activateDelegate = this.activate.createDelegate(this);
37735
37736     this.addEvents({
37737         /**
37738          * @event tabchange
37739          * Fires when the active tab changes
37740          * @param {Roo.TabPanel} this
37741          * @param {Roo.TabPanelItem} activePanel The new active tab
37742          */
37743         "tabchange": true,
37744         /**
37745          * @event beforetabchange
37746          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37747          * @param {Roo.TabPanel} this
37748          * @param {Object} e Set cancel to true on this object to cancel the tab change
37749          * @param {Roo.TabPanelItem} tab The tab being changed to
37750          */
37751         "beforetabchange" : true
37752     });
37753
37754     Roo.EventManager.onWindowResize(this.onResize, this);
37755     this.cpad = this.el.getPadding("lr");
37756     this.hiddenCount = 0;
37757
37758
37759     // toolbar on the tabbar support...
37760     if (this.toolbar) {
37761         alert("no toolbar support yet");
37762         this.toolbar  = false;
37763         /*
37764         var tcfg = this.toolbar;
37765         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37766         this.toolbar = new Roo.Toolbar(tcfg);
37767         if (Roo.isSafari) {
37768             var tbl = tcfg.container.child('table', true);
37769             tbl.setAttribute('width', '100%');
37770         }
37771         */
37772         
37773     }
37774    
37775
37776
37777     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37778 };
37779
37780 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37781     /*
37782      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37783      */
37784     tabPosition : "top",
37785     /*
37786      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37787      */
37788     currentTabWidth : 0,
37789     /*
37790      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37791      */
37792     minTabWidth : 40,
37793     /*
37794      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37795      */
37796     maxTabWidth : 250,
37797     /*
37798      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37799      */
37800     preferredTabWidth : 175,
37801     /*
37802      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37803      */
37804     resizeTabs : false,
37805     /*
37806      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37807      */
37808     monitorResize : true,
37809     /*
37810      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37811      */
37812     toolbar : false,
37813
37814     /**
37815      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37816      * @param {String} id The id of the div to use <b>or create</b>
37817      * @param {String} text The text for the tab
37818      * @param {String} content (optional) Content to put in the TabPanelItem body
37819      * @param {Boolean} closable (optional) True to create a close icon on the tab
37820      * @return {Roo.TabPanelItem} The created TabPanelItem
37821      */
37822     addTab : function(id, text, content, closable, tpl)
37823     {
37824         var item = new Roo.bootstrap.panel.TabItem({
37825             panel: this,
37826             id : id,
37827             text : text,
37828             closable : closable,
37829             tpl : tpl
37830         });
37831         this.addTabItem(item);
37832         if(content){
37833             item.setContent(content);
37834         }
37835         return item;
37836     },
37837
37838     /**
37839      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37840      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37841      * @return {Roo.TabPanelItem}
37842      */
37843     getTab : function(id){
37844         return this.items[id];
37845     },
37846
37847     /**
37848      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37849      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37850      */
37851     hideTab : function(id){
37852         var t = this.items[id];
37853         if(!t.isHidden()){
37854            t.setHidden(true);
37855            this.hiddenCount++;
37856            this.autoSizeTabs();
37857         }
37858     },
37859
37860     /**
37861      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37862      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37863      */
37864     unhideTab : function(id){
37865         var t = this.items[id];
37866         if(t.isHidden()){
37867            t.setHidden(false);
37868            this.hiddenCount--;
37869            this.autoSizeTabs();
37870         }
37871     },
37872
37873     /**
37874      * Adds an existing {@link Roo.TabPanelItem}.
37875      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37876      */
37877     addTabItem : function(item){
37878         this.items[item.id] = item;
37879         this.items.push(item);
37880       //  if(this.resizeTabs){
37881     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37882   //         this.autoSizeTabs();
37883 //        }else{
37884 //            item.autoSize();
37885        // }
37886     },
37887
37888     /**
37889      * Removes a {@link Roo.TabPanelItem}.
37890      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37891      */
37892     removeTab : function(id){
37893         var items = this.items;
37894         var tab = items[id];
37895         if(!tab) { return; }
37896         var index = items.indexOf(tab);
37897         if(this.active == tab && items.length > 1){
37898             var newTab = this.getNextAvailable(index);
37899             if(newTab) {
37900                 newTab.activate();
37901             }
37902         }
37903         this.stripEl.dom.removeChild(tab.pnode.dom);
37904         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37905             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37906         }
37907         items.splice(index, 1);
37908         delete this.items[tab.id];
37909         tab.fireEvent("close", tab);
37910         tab.purgeListeners();
37911         this.autoSizeTabs();
37912     },
37913
37914     getNextAvailable : function(start){
37915         var items = this.items;
37916         var index = start;
37917         // look for a next tab that will slide over to
37918         // replace the one being removed
37919         while(index < items.length){
37920             var item = items[++index];
37921             if(item && !item.isHidden()){
37922                 return item;
37923             }
37924         }
37925         // if one isn't found select the previous tab (on the left)
37926         index = start;
37927         while(index >= 0){
37928             var item = items[--index];
37929             if(item && !item.isHidden()){
37930                 return item;
37931             }
37932         }
37933         return null;
37934     },
37935
37936     /**
37937      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37938      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37939      */
37940     disableTab : function(id){
37941         var tab = this.items[id];
37942         if(tab && this.active != tab){
37943             tab.disable();
37944         }
37945     },
37946
37947     /**
37948      * Enables a {@link Roo.TabPanelItem} that is disabled.
37949      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37950      */
37951     enableTab : function(id){
37952         var tab = this.items[id];
37953         tab.enable();
37954     },
37955
37956     /**
37957      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37958      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37959      * @return {Roo.TabPanelItem} The TabPanelItem.
37960      */
37961     activate : function(id){
37962         var tab = this.items[id];
37963         if(!tab){
37964             return null;
37965         }
37966         if(tab == this.active || tab.disabled){
37967             return tab;
37968         }
37969         var e = {};
37970         this.fireEvent("beforetabchange", this, e, tab);
37971         if(e.cancel !== true && !tab.disabled){
37972             if(this.active){
37973                 this.active.hide();
37974             }
37975             this.active = this.items[id];
37976             this.active.show();
37977             this.fireEvent("tabchange", this, this.active);
37978         }
37979         return tab;
37980     },
37981
37982     /**
37983      * Gets the active {@link Roo.TabPanelItem}.
37984      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37985      */
37986     getActiveTab : function(){
37987         return this.active;
37988     },
37989
37990     /**
37991      * Updates the tab body element to fit the height of the container element
37992      * for overflow scrolling
37993      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37994      */
37995     syncHeight : function(targetHeight){
37996         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37997         var bm = this.bodyEl.getMargins();
37998         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37999         this.bodyEl.setHeight(newHeight);
38000         return newHeight;
38001     },
38002
38003     onResize : function(){
38004         if(this.monitorResize){
38005             this.autoSizeTabs();
38006         }
38007     },
38008
38009     /**
38010      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38011      */
38012     beginUpdate : function(){
38013         this.updating = true;
38014     },
38015
38016     /**
38017      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38018      */
38019     endUpdate : function(){
38020         this.updating = false;
38021         this.autoSizeTabs();
38022     },
38023
38024     /**
38025      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38026      */
38027     autoSizeTabs : function(){
38028         var count = this.items.length;
38029         var vcount = count - this.hiddenCount;
38030         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38031             return;
38032         }
38033         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38034         var availWidth = Math.floor(w / vcount);
38035         var b = this.stripBody;
38036         if(b.getWidth() > w){
38037             var tabs = this.items;
38038             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38039             if(availWidth < this.minTabWidth){
38040                 /*if(!this.sleft){    // incomplete scrolling code
38041                     this.createScrollButtons();
38042                 }
38043                 this.showScroll();
38044                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38045             }
38046         }else{
38047             if(this.currentTabWidth < this.preferredTabWidth){
38048                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38049             }
38050         }
38051     },
38052
38053     /**
38054      * Returns the number of tabs in this TabPanel.
38055      * @return {Number}
38056      */
38057      getCount : function(){
38058          return this.items.length;
38059      },
38060
38061     /**
38062      * Resizes all the tabs to the passed width
38063      * @param {Number} The new width
38064      */
38065     setTabWidth : function(width){
38066         this.currentTabWidth = width;
38067         for(var i = 0, len = this.items.length; i < len; i++) {
38068                 if(!this.items[i].isHidden()) {
38069                 this.items[i].setWidth(width);
38070             }
38071         }
38072     },
38073
38074     /**
38075      * Destroys this TabPanel
38076      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38077      */
38078     destroy : function(removeEl){
38079         Roo.EventManager.removeResizeListener(this.onResize, this);
38080         for(var i = 0, len = this.items.length; i < len; i++){
38081             this.items[i].purgeListeners();
38082         }
38083         if(removeEl === true){
38084             this.el.update("");
38085             this.el.remove();
38086         }
38087     },
38088     
38089     createStrip : function(container)
38090     {
38091         var strip = document.createElement("nav");
38092         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38093         container.appendChild(strip);
38094         return strip;
38095     },
38096     
38097     createStripList : function(strip)
38098     {
38099         // div wrapper for retard IE
38100         // returns the "tr" element.
38101         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38102         //'<div class="x-tabs-strip-wrap">'+
38103           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38104           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38105         return strip.firstChild; //.firstChild.firstChild.firstChild;
38106     },
38107     createBody : function(container)
38108     {
38109         var body = document.createElement("div");
38110         Roo.id(body, "tab-body");
38111         //Roo.fly(body).addClass("x-tabs-body");
38112         Roo.fly(body).addClass("tab-content");
38113         container.appendChild(body);
38114         return body;
38115     },
38116     createItemBody :function(bodyEl, id){
38117         var body = Roo.getDom(id);
38118         if(!body){
38119             body = document.createElement("div");
38120             body.id = id;
38121         }
38122         //Roo.fly(body).addClass("x-tabs-item-body");
38123         Roo.fly(body).addClass("tab-pane");
38124          bodyEl.insertBefore(body, bodyEl.firstChild);
38125         return body;
38126     },
38127     /** @private */
38128     createStripElements :  function(stripEl, text, closable, tpl)
38129     {
38130         var td = document.createElement("li"); // was td..
38131         
38132         
38133         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38134         
38135         
38136         stripEl.appendChild(td);
38137         /*if(closable){
38138             td.className = "x-tabs-closable";
38139             if(!this.closeTpl){
38140                 this.closeTpl = new Roo.Template(
38141                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38142                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38143                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38144                 );
38145             }
38146             var el = this.closeTpl.overwrite(td, {"text": text});
38147             var close = el.getElementsByTagName("div")[0];
38148             var inner = el.getElementsByTagName("em")[0];
38149             return {"el": el, "close": close, "inner": inner};
38150         } else {
38151         */
38152         // not sure what this is..
38153 //            if(!this.tabTpl){
38154                 //this.tabTpl = new Roo.Template(
38155                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38156                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38157                 //);
38158 //                this.tabTpl = new Roo.Template(
38159 //                   '<a href="#">' +
38160 //                   '<span unselectable="on"' +
38161 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38162 //                            ' >{text}</span></a>'
38163 //                );
38164 //                
38165 //            }
38166
38167
38168             var template = tpl || this.tabTpl || false;
38169             
38170             if(!template){
38171                 
38172                 template = new Roo.Template(
38173                    '<a href="#">' +
38174                    '<span unselectable="on"' +
38175                             (this.disableTooltips ? '' : ' title="{text}"') +
38176                             ' >{text}</span></a>'
38177                 );
38178             }
38179             
38180             switch (typeof(template)) {
38181                 case 'object' :
38182                     break;
38183                 case 'string' :
38184                     template = new Roo.Template(template);
38185                     break;
38186                 default :
38187                     break;
38188             }
38189             
38190             var el = template.overwrite(td, {"text": text});
38191             
38192             var inner = el.getElementsByTagName("span")[0];
38193             
38194             return {"el": el, "inner": inner};
38195             
38196     }
38197         
38198     
38199 });
38200
38201 /**
38202  * @class Roo.TabPanelItem
38203  * @extends Roo.util.Observable
38204  * Represents an individual item (tab plus body) in a TabPanel.
38205  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38206  * @param {String} id The id of this TabPanelItem
38207  * @param {String} text The text for the tab of this TabPanelItem
38208  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38209  */
38210 Roo.bootstrap.panel.TabItem = function(config){
38211     /**
38212      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38213      * @type Roo.TabPanel
38214      */
38215     this.tabPanel = config.panel;
38216     /**
38217      * The id for this TabPanelItem
38218      * @type String
38219      */
38220     this.id = config.id;
38221     /** @private */
38222     this.disabled = false;
38223     /** @private */
38224     this.text = config.text;
38225     /** @private */
38226     this.loaded = false;
38227     this.closable = config.closable;
38228
38229     /**
38230      * The body element for this TabPanelItem.
38231      * @type Roo.Element
38232      */
38233     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38234     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38235     this.bodyEl.setStyle("display", "block");
38236     this.bodyEl.setStyle("zoom", "1");
38237     //this.hideAction();
38238
38239     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38240     /** @private */
38241     this.el = Roo.get(els.el);
38242     this.inner = Roo.get(els.inner, true);
38243     this.textEl = Roo.get(this.el.dom.firstChild, true);
38244     this.pnode = Roo.get(els.el.parentNode, true);
38245 //    this.el.on("mousedown", this.onTabMouseDown, this);
38246     this.el.on("click", this.onTabClick, this);
38247     /** @private */
38248     if(config.closable){
38249         var c = Roo.get(els.close, true);
38250         c.dom.title = this.closeText;
38251         c.addClassOnOver("close-over");
38252         c.on("click", this.closeClick, this);
38253      }
38254
38255     this.addEvents({
38256          /**
38257          * @event activate
38258          * Fires when this tab becomes the active tab.
38259          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38260          * @param {Roo.TabPanelItem} this
38261          */
38262         "activate": true,
38263         /**
38264          * @event beforeclose
38265          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38266          * @param {Roo.TabPanelItem} this
38267          * @param {Object} e Set cancel to true on this object to cancel the close.
38268          */
38269         "beforeclose": true,
38270         /**
38271          * @event close
38272          * Fires when this tab is closed.
38273          * @param {Roo.TabPanelItem} this
38274          */
38275          "close": true,
38276         /**
38277          * @event deactivate
38278          * Fires when this tab is no longer the active tab.
38279          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38280          * @param {Roo.TabPanelItem} this
38281          */
38282          "deactivate" : true
38283     });
38284     this.hidden = false;
38285
38286     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38287 };
38288
38289 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38290            {
38291     purgeListeners : function(){
38292        Roo.util.Observable.prototype.purgeListeners.call(this);
38293        this.el.removeAllListeners();
38294     },
38295     /**
38296      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38297      */
38298     show : function(){
38299         this.pnode.addClass("active");
38300         this.showAction();
38301         if(Roo.isOpera){
38302             this.tabPanel.stripWrap.repaint();
38303         }
38304         this.fireEvent("activate", this.tabPanel, this);
38305     },
38306
38307     /**
38308      * Returns true if this tab is the active tab.
38309      * @return {Boolean}
38310      */
38311     isActive : function(){
38312         return this.tabPanel.getActiveTab() == this;
38313     },
38314
38315     /**
38316      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38317      */
38318     hide : function(){
38319         this.pnode.removeClass("active");
38320         this.hideAction();
38321         this.fireEvent("deactivate", this.tabPanel, this);
38322     },
38323
38324     hideAction : function(){
38325         this.bodyEl.hide();
38326         this.bodyEl.setStyle("position", "absolute");
38327         this.bodyEl.setLeft("-20000px");
38328         this.bodyEl.setTop("-20000px");
38329     },
38330
38331     showAction : function(){
38332         this.bodyEl.setStyle("position", "relative");
38333         this.bodyEl.setTop("");
38334         this.bodyEl.setLeft("");
38335         this.bodyEl.show();
38336     },
38337
38338     /**
38339      * Set the tooltip for the tab.
38340      * @param {String} tooltip The tab's tooltip
38341      */
38342     setTooltip : function(text){
38343         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38344             this.textEl.dom.qtip = text;
38345             this.textEl.dom.removeAttribute('title');
38346         }else{
38347             this.textEl.dom.title = text;
38348         }
38349     },
38350
38351     onTabClick : function(e){
38352         e.preventDefault();
38353         this.tabPanel.activate(this.id);
38354     },
38355
38356     onTabMouseDown : function(e){
38357         e.preventDefault();
38358         this.tabPanel.activate(this.id);
38359     },
38360 /*
38361     getWidth : function(){
38362         return this.inner.getWidth();
38363     },
38364
38365     setWidth : function(width){
38366         var iwidth = width - this.pnode.getPadding("lr");
38367         this.inner.setWidth(iwidth);
38368         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38369         this.pnode.setWidth(width);
38370     },
38371 */
38372     /**
38373      * Show or hide the tab
38374      * @param {Boolean} hidden True to hide or false to show.
38375      */
38376     setHidden : function(hidden){
38377         this.hidden = hidden;
38378         this.pnode.setStyle("display", hidden ? "none" : "");
38379     },
38380
38381     /**
38382      * Returns true if this tab is "hidden"
38383      * @return {Boolean}
38384      */
38385     isHidden : function(){
38386         return this.hidden;
38387     },
38388
38389     /**
38390      * Returns the text for this tab
38391      * @return {String}
38392      */
38393     getText : function(){
38394         return this.text;
38395     },
38396     /*
38397     autoSize : function(){
38398         //this.el.beginMeasure();
38399         this.textEl.setWidth(1);
38400         /*
38401          *  #2804 [new] Tabs in Roojs
38402          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38403          */
38404         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38405         //this.el.endMeasure();
38406     //},
38407
38408     /**
38409      * Sets the text for the tab (Note: this also sets the tooltip text)
38410      * @param {String} text The tab's text and tooltip
38411      */
38412     setText : function(text){
38413         this.text = text;
38414         this.textEl.update(text);
38415         this.setTooltip(text);
38416         //if(!this.tabPanel.resizeTabs){
38417         //    this.autoSize();
38418         //}
38419     },
38420     /**
38421      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38422      */
38423     activate : function(){
38424         this.tabPanel.activate(this.id);
38425     },
38426
38427     /**
38428      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38429      */
38430     disable : function(){
38431         if(this.tabPanel.active != this){
38432             this.disabled = true;
38433             this.pnode.addClass("disabled");
38434         }
38435     },
38436
38437     /**
38438      * Enables this TabPanelItem if it was previously disabled.
38439      */
38440     enable : function(){
38441         this.disabled = false;
38442         this.pnode.removeClass("disabled");
38443     },
38444
38445     /**
38446      * Sets the content for this TabPanelItem.
38447      * @param {String} content The content
38448      * @param {Boolean} loadScripts true to look for and load scripts
38449      */
38450     setContent : function(content, loadScripts){
38451         this.bodyEl.update(content, loadScripts);
38452     },
38453
38454     /**
38455      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38456      * @return {Roo.UpdateManager} The UpdateManager
38457      */
38458     getUpdateManager : function(){
38459         return this.bodyEl.getUpdateManager();
38460     },
38461
38462     /**
38463      * Set a URL to be used to load the content for this TabPanelItem.
38464      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38465      * @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)
38466      * @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)
38467      * @return {Roo.UpdateManager} The UpdateManager
38468      */
38469     setUrl : function(url, params, loadOnce){
38470         if(this.refreshDelegate){
38471             this.un('activate', this.refreshDelegate);
38472         }
38473         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38474         this.on("activate", this.refreshDelegate);
38475         return this.bodyEl.getUpdateManager();
38476     },
38477
38478     /** @private */
38479     _handleRefresh : function(url, params, loadOnce){
38480         if(!loadOnce || !this.loaded){
38481             var updater = this.bodyEl.getUpdateManager();
38482             updater.update(url, params, this._setLoaded.createDelegate(this));
38483         }
38484     },
38485
38486     /**
38487      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38488      *   Will fail silently if the setUrl method has not been called.
38489      *   This does not activate the panel, just updates its content.
38490      */
38491     refresh : function(){
38492         if(this.refreshDelegate){
38493            this.loaded = false;
38494            this.refreshDelegate();
38495         }
38496     },
38497
38498     /** @private */
38499     _setLoaded : function(){
38500         this.loaded = true;
38501     },
38502
38503     /** @private */
38504     closeClick : function(e){
38505         var o = {};
38506         e.stopEvent();
38507         this.fireEvent("beforeclose", this, o);
38508         if(o.cancel !== true){
38509             this.tabPanel.removeTab(this.id);
38510         }
38511     },
38512     /**
38513      * The text displayed in the tooltip for the close icon.
38514      * @type String
38515      */
38516     closeText : "Close this tab"
38517 });
38518 /**
38519 *    This script refer to:
38520 *    Title: International Telephone Input
38521 *    Author: Jack O'Connor
38522 *    Code version:  v12.1.12
38523 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38524 **/
38525
38526 Roo.bootstrap.PhoneInputData = function() {
38527     var d = [
38528       [
38529         "Afghanistan (‫افغانستان‬‎)",
38530         "af",
38531         "93"
38532       ],
38533       [
38534         "Albania (Shqipëri)",
38535         "al",
38536         "355"
38537       ],
38538       [
38539         "Algeria (‫الجزائر‬‎)",
38540         "dz",
38541         "213"
38542       ],
38543       [
38544         "American Samoa",
38545         "as",
38546         "1684"
38547       ],
38548       [
38549         "Andorra",
38550         "ad",
38551         "376"
38552       ],
38553       [
38554         "Angola",
38555         "ao",
38556         "244"
38557       ],
38558       [
38559         "Anguilla",
38560         "ai",
38561         "1264"
38562       ],
38563       [
38564         "Antigua and Barbuda",
38565         "ag",
38566         "1268"
38567       ],
38568       [
38569         "Argentina",
38570         "ar",
38571         "54"
38572       ],
38573       [
38574         "Armenia (Հայաստան)",
38575         "am",
38576         "374"
38577       ],
38578       [
38579         "Aruba",
38580         "aw",
38581         "297"
38582       ],
38583       [
38584         "Australia",
38585         "au",
38586         "61",
38587         0
38588       ],
38589       [
38590         "Austria (Österreich)",
38591         "at",
38592         "43"
38593       ],
38594       [
38595         "Azerbaijan (Azərbaycan)",
38596         "az",
38597         "994"
38598       ],
38599       [
38600         "Bahamas",
38601         "bs",
38602         "1242"
38603       ],
38604       [
38605         "Bahrain (‫البحرين‬‎)",
38606         "bh",
38607         "973"
38608       ],
38609       [
38610         "Bangladesh (বাংলাদেশ)",
38611         "bd",
38612         "880"
38613       ],
38614       [
38615         "Barbados",
38616         "bb",
38617         "1246"
38618       ],
38619       [
38620         "Belarus (Беларусь)",
38621         "by",
38622         "375"
38623       ],
38624       [
38625         "Belgium (België)",
38626         "be",
38627         "32"
38628       ],
38629       [
38630         "Belize",
38631         "bz",
38632         "501"
38633       ],
38634       [
38635         "Benin (Bénin)",
38636         "bj",
38637         "229"
38638       ],
38639       [
38640         "Bermuda",
38641         "bm",
38642         "1441"
38643       ],
38644       [
38645         "Bhutan (འབྲུག)",
38646         "bt",
38647         "975"
38648       ],
38649       [
38650         "Bolivia",
38651         "bo",
38652         "591"
38653       ],
38654       [
38655         "Bosnia and Herzegovina (Босна и Херцеговина)",
38656         "ba",
38657         "387"
38658       ],
38659       [
38660         "Botswana",
38661         "bw",
38662         "267"
38663       ],
38664       [
38665         "Brazil (Brasil)",
38666         "br",
38667         "55"
38668       ],
38669       [
38670         "British Indian Ocean Territory",
38671         "io",
38672         "246"
38673       ],
38674       [
38675         "British Virgin Islands",
38676         "vg",
38677         "1284"
38678       ],
38679       [
38680         "Brunei",
38681         "bn",
38682         "673"
38683       ],
38684       [
38685         "Bulgaria (България)",
38686         "bg",
38687         "359"
38688       ],
38689       [
38690         "Burkina Faso",
38691         "bf",
38692         "226"
38693       ],
38694       [
38695         "Burundi (Uburundi)",
38696         "bi",
38697         "257"
38698       ],
38699       [
38700         "Cambodia (កម្ពុជា)",
38701         "kh",
38702         "855"
38703       ],
38704       [
38705         "Cameroon (Cameroun)",
38706         "cm",
38707         "237"
38708       ],
38709       [
38710         "Canada",
38711         "ca",
38712         "1",
38713         1,
38714         ["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"]
38715       ],
38716       [
38717         "Cape Verde (Kabu Verdi)",
38718         "cv",
38719         "238"
38720       ],
38721       [
38722         "Caribbean Netherlands",
38723         "bq",
38724         "599",
38725         1
38726       ],
38727       [
38728         "Cayman Islands",
38729         "ky",
38730         "1345"
38731       ],
38732       [
38733         "Central African Republic (République centrafricaine)",
38734         "cf",
38735         "236"
38736       ],
38737       [
38738         "Chad (Tchad)",
38739         "td",
38740         "235"
38741       ],
38742       [
38743         "Chile",
38744         "cl",
38745         "56"
38746       ],
38747       [
38748         "China (中国)",
38749         "cn",
38750         "86"
38751       ],
38752       [
38753         "Christmas Island",
38754         "cx",
38755         "61",
38756         2
38757       ],
38758       [
38759         "Cocos (Keeling) Islands",
38760         "cc",
38761         "61",
38762         1
38763       ],
38764       [
38765         "Colombia",
38766         "co",
38767         "57"
38768       ],
38769       [
38770         "Comoros (‫جزر القمر‬‎)",
38771         "km",
38772         "269"
38773       ],
38774       [
38775         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38776         "cd",
38777         "243"
38778       ],
38779       [
38780         "Congo (Republic) (Congo-Brazzaville)",
38781         "cg",
38782         "242"
38783       ],
38784       [
38785         "Cook Islands",
38786         "ck",
38787         "682"
38788       ],
38789       [
38790         "Costa Rica",
38791         "cr",
38792         "506"
38793       ],
38794       [
38795         "Côte d’Ivoire",
38796         "ci",
38797         "225"
38798       ],
38799       [
38800         "Croatia (Hrvatska)",
38801         "hr",
38802         "385"
38803       ],
38804       [
38805         "Cuba",
38806         "cu",
38807         "53"
38808       ],
38809       [
38810         "Curaçao",
38811         "cw",
38812         "599",
38813         0
38814       ],
38815       [
38816         "Cyprus (Κύπρος)",
38817         "cy",
38818         "357"
38819       ],
38820       [
38821         "Czech Republic (Česká republika)",
38822         "cz",
38823         "420"
38824       ],
38825       [
38826         "Denmark (Danmark)",
38827         "dk",
38828         "45"
38829       ],
38830       [
38831         "Djibouti",
38832         "dj",
38833         "253"
38834       ],
38835       [
38836         "Dominica",
38837         "dm",
38838         "1767"
38839       ],
38840       [
38841         "Dominican Republic (República Dominicana)",
38842         "do",
38843         "1",
38844         2,
38845         ["809", "829", "849"]
38846       ],
38847       [
38848         "Ecuador",
38849         "ec",
38850         "593"
38851       ],
38852       [
38853         "Egypt (‫مصر‬‎)",
38854         "eg",
38855         "20"
38856       ],
38857       [
38858         "El Salvador",
38859         "sv",
38860         "503"
38861       ],
38862       [
38863         "Equatorial Guinea (Guinea Ecuatorial)",
38864         "gq",
38865         "240"
38866       ],
38867       [
38868         "Eritrea",
38869         "er",
38870         "291"
38871       ],
38872       [
38873         "Estonia (Eesti)",
38874         "ee",
38875         "372"
38876       ],
38877       [
38878         "Ethiopia",
38879         "et",
38880         "251"
38881       ],
38882       [
38883         "Falkland Islands (Islas Malvinas)",
38884         "fk",
38885         "500"
38886       ],
38887       [
38888         "Faroe Islands (Føroyar)",
38889         "fo",
38890         "298"
38891       ],
38892       [
38893         "Fiji",
38894         "fj",
38895         "679"
38896       ],
38897       [
38898         "Finland (Suomi)",
38899         "fi",
38900         "358",
38901         0
38902       ],
38903       [
38904         "France",
38905         "fr",
38906         "33"
38907       ],
38908       [
38909         "French Guiana (Guyane française)",
38910         "gf",
38911         "594"
38912       ],
38913       [
38914         "French Polynesia (Polynésie française)",
38915         "pf",
38916         "689"
38917       ],
38918       [
38919         "Gabon",
38920         "ga",
38921         "241"
38922       ],
38923       [
38924         "Gambia",
38925         "gm",
38926         "220"
38927       ],
38928       [
38929         "Georgia (საქართველო)",
38930         "ge",
38931         "995"
38932       ],
38933       [
38934         "Germany (Deutschland)",
38935         "de",
38936         "49"
38937       ],
38938       [
38939         "Ghana (Gaana)",
38940         "gh",
38941         "233"
38942       ],
38943       [
38944         "Gibraltar",
38945         "gi",
38946         "350"
38947       ],
38948       [
38949         "Greece (Ελλάδα)",
38950         "gr",
38951         "30"
38952       ],
38953       [
38954         "Greenland (Kalaallit Nunaat)",
38955         "gl",
38956         "299"
38957       ],
38958       [
38959         "Grenada",
38960         "gd",
38961         "1473"
38962       ],
38963       [
38964         "Guadeloupe",
38965         "gp",
38966         "590",
38967         0
38968       ],
38969       [
38970         "Guam",
38971         "gu",
38972         "1671"
38973       ],
38974       [
38975         "Guatemala",
38976         "gt",
38977         "502"
38978       ],
38979       [
38980         "Guernsey",
38981         "gg",
38982         "44",
38983         1
38984       ],
38985       [
38986         "Guinea (Guinée)",
38987         "gn",
38988         "224"
38989       ],
38990       [
38991         "Guinea-Bissau (Guiné Bissau)",
38992         "gw",
38993         "245"
38994       ],
38995       [
38996         "Guyana",
38997         "gy",
38998         "592"
38999       ],
39000       [
39001         "Haiti",
39002         "ht",
39003         "509"
39004       ],
39005       [
39006         "Honduras",
39007         "hn",
39008         "504"
39009       ],
39010       [
39011         "Hong Kong (香港)",
39012         "hk",
39013         "852"
39014       ],
39015       [
39016         "Hungary (Magyarország)",
39017         "hu",
39018         "36"
39019       ],
39020       [
39021         "Iceland (Ísland)",
39022         "is",
39023         "354"
39024       ],
39025       [
39026         "India (भारत)",
39027         "in",
39028         "91"
39029       ],
39030       [
39031         "Indonesia",
39032         "id",
39033         "62"
39034       ],
39035       [
39036         "Iran (‫ایران‬‎)",
39037         "ir",
39038         "98"
39039       ],
39040       [
39041         "Iraq (‫العراق‬‎)",
39042         "iq",
39043         "964"
39044       ],
39045       [
39046         "Ireland",
39047         "ie",
39048         "353"
39049       ],
39050       [
39051         "Isle of Man",
39052         "im",
39053         "44",
39054         2
39055       ],
39056       [
39057         "Israel (‫ישראל‬‎)",
39058         "il",
39059         "972"
39060       ],
39061       [
39062         "Italy (Italia)",
39063         "it",
39064         "39",
39065         0
39066       ],
39067       [
39068         "Jamaica",
39069         "jm",
39070         "1876"
39071       ],
39072       [
39073         "Japan (日本)",
39074         "jp",
39075         "81"
39076       ],
39077       [
39078         "Jersey",
39079         "je",
39080         "44",
39081         3
39082       ],
39083       [
39084         "Jordan (‫الأردن‬‎)",
39085         "jo",
39086         "962"
39087       ],
39088       [
39089         "Kazakhstan (Казахстан)",
39090         "kz",
39091         "7",
39092         1
39093       ],
39094       [
39095         "Kenya",
39096         "ke",
39097         "254"
39098       ],
39099       [
39100         "Kiribati",
39101         "ki",
39102         "686"
39103       ],
39104       [
39105         "Kosovo",
39106         "xk",
39107         "383"
39108       ],
39109       [
39110         "Kuwait (‫الكويت‬‎)",
39111         "kw",
39112         "965"
39113       ],
39114       [
39115         "Kyrgyzstan (Кыргызстан)",
39116         "kg",
39117         "996"
39118       ],
39119       [
39120         "Laos (ລາວ)",
39121         "la",
39122         "856"
39123       ],
39124       [
39125         "Latvia (Latvija)",
39126         "lv",
39127         "371"
39128       ],
39129       [
39130         "Lebanon (‫لبنان‬‎)",
39131         "lb",
39132         "961"
39133       ],
39134       [
39135         "Lesotho",
39136         "ls",
39137         "266"
39138       ],
39139       [
39140         "Liberia",
39141         "lr",
39142         "231"
39143       ],
39144       [
39145         "Libya (‫ليبيا‬‎)",
39146         "ly",
39147         "218"
39148       ],
39149       [
39150         "Liechtenstein",
39151         "li",
39152         "423"
39153       ],
39154       [
39155         "Lithuania (Lietuva)",
39156         "lt",
39157         "370"
39158       ],
39159       [
39160         "Luxembourg",
39161         "lu",
39162         "352"
39163       ],
39164       [
39165         "Macau (澳門)",
39166         "mo",
39167         "853"
39168       ],
39169       [
39170         "Macedonia (FYROM) (Македонија)",
39171         "mk",
39172         "389"
39173       ],
39174       [
39175         "Madagascar (Madagasikara)",
39176         "mg",
39177         "261"
39178       ],
39179       [
39180         "Malawi",
39181         "mw",
39182         "265"
39183       ],
39184       [
39185         "Malaysia",
39186         "my",
39187         "60"
39188       ],
39189       [
39190         "Maldives",
39191         "mv",
39192         "960"
39193       ],
39194       [
39195         "Mali",
39196         "ml",
39197         "223"
39198       ],
39199       [
39200         "Malta",
39201         "mt",
39202         "356"
39203       ],
39204       [
39205         "Marshall Islands",
39206         "mh",
39207         "692"
39208       ],
39209       [
39210         "Martinique",
39211         "mq",
39212         "596"
39213       ],
39214       [
39215         "Mauritania (‫موريتانيا‬‎)",
39216         "mr",
39217         "222"
39218       ],
39219       [
39220         "Mauritius (Moris)",
39221         "mu",
39222         "230"
39223       ],
39224       [
39225         "Mayotte",
39226         "yt",
39227         "262",
39228         1
39229       ],
39230       [
39231         "Mexico (México)",
39232         "mx",
39233         "52"
39234       ],
39235       [
39236         "Micronesia",
39237         "fm",
39238         "691"
39239       ],
39240       [
39241         "Moldova (Republica Moldova)",
39242         "md",
39243         "373"
39244       ],
39245       [
39246         "Monaco",
39247         "mc",
39248         "377"
39249       ],
39250       [
39251         "Mongolia (Монгол)",
39252         "mn",
39253         "976"
39254       ],
39255       [
39256         "Montenegro (Crna Gora)",
39257         "me",
39258         "382"
39259       ],
39260       [
39261         "Montserrat",
39262         "ms",
39263         "1664"
39264       ],
39265       [
39266         "Morocco (‫المغرب‬‎)",
39267         "ma",
39268         "212",
39269         0
39270       ],
39271       [
39272         "Mozambique (Moçambique)",
39273         "mz",
39274         "258"
39275       ],
39276       [
39277         "Myanmar (Burma) (မြန်မာ)",
39278         "mm",
39279         "95"
39280       ],
39281       [
39282         "Namibia (Namibië)",
39283         "na",
39284         "264"
39285       ],
39286       [
39287         "Nauru",
39288         "nr",
39289         "674"
39290       ],
39291       [
39292         "Nepal (नेपाल)",
39293         "np",
39294         "977"
39295       ],
39296       [
39297         "Netherlands (Nederland)",
39298         "nl",
39299         "31"
39300       ],
39301       [
39302         "New Caledonia (Nouvelle-Calédonie)",
39303         "nc",
39304         "687"
39305       ],
39306       [
39307         "New Zealand",
39308         "nz",
39309         "64"
39310       ],
39311       [
39312         "Nicaragua",
39313         "ni",
39314         "505"
39315       ],
39316       [
39317         "Niger (Nijar)",
39318         "ne",
39319         "227"
39320       ],
39321       [
39322         "Nigeria",
39323         "ng",
39324         "234"
39325       ],
39326       [
39327         "Niue",
39328         "nu",
39329         "683"
39330       ],
39331       [
39332         "Norfolk Island",
39333         "nf",
39334         "672"
39335       ],
39336       [
39337         "North Korea (조선 민주주의 인민 공화국)",
39338         "kp",
39339         "850"
39340       ],
39341       [
39342         "Northern Mariana Islands",
39343         "mp",
39344         "1670"
39345       ],
39346       [
39347         "Norway (Norge)",
39348         "no",
39349         "47",
39350         0
39351       ],
39352       [
39353         "Oman (‫عُمان‬‎)",
39354         "om",
39355         "968"
39356       ],
39357       [
39358         "Pakistan (‫پاکستان‬‎)",
39359         "pk",
39360         "92"
39361       ],
39362       [
39363         "Palau",
39364         "pw",
39365         "680"
39366       ],
39367       [
39368         "Palestine (‫فلسطين‬‎)",
39369         "ps",
39370         "970"
39371       ],
39372       [
39373         "Panama (Panamá)",
39374         "pa",
39375         "507"
39376       ],
39377       [
39378         "Papua New Guinea",
39379         "pg",
39380         "675"
39381       ],
39382       [
39383         "Paraguay",
39384         "py",
39385         "595"
39386       ],
39387       [
39388         "Peru (Perú)",
39389         "pe",
39390         "51"
39391       ],
39392       [
39393         "Philippines",
39394         "ph",
39395         "63"
39396       ],
39397       [
39398         "Poland (Polska)",
39399         "pl",
39400         "48"
39401       ],
39402       [
39403         "Portugal",
39404         "pt",
39405         "351"
39406       ],
39407       [
39408         "Puerto Rico",
39409         "pr",
39410         "1",
39411         3,
39412         ["787", "939"]
39413       ],
39414       [
39415         "Qatar (‫قطر‬‎)",
39416         "qa",
39417         "974"
39418       ],
39419       [
39420         "Réunion (La Réunion)",
39421         "re",
39422         "262",
39423         0
39424       ],
39425       [
39426         "Romania (România)",
39427         "ro",
39428         "40"
39429       ],
39430       [
39431         "Russia (Россия)",
39432         "ru",
39433         "7",
39434         0
39435       ],
39436       [
39437         "Rwanda",
39438         "rw",
39439         "250"
39440       ],
39441       [
39442         "Saint Barthélemy",
39443         "bl",
39444         "590",
39445         1
39446       ],
39447       [
39448         "Saint Helena",
39449         "sh",
39450         "290"
39451       ],
39452       [
39453         "Saint Kitts and Nevis",
39454         "kn",
39455         "1869"
39456       ],
39457       [
39458         "Saint Lucia",
39459         "lc",
39460         "1758"
39461       ],
39462       [
39463         "Saint Martin (Saint-Martin (partie française))",
39464         "mf",
39465         "590",
39466         2
39467       ],
39468       [
39469         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39470         "pm",
39471         "508"
39472       ],
39473       [
39474         "Saint Vincent and the Grenadines",
39475         "vc",
39476         "1784"
39477       ],
39478       [
39479         "Samoa",
39480         "ws",
39481         "685"
39482       ],
39483       [
39484         "San Marino",
39485         "sm",
39486         "378"
39487       ],
39488       [
39489         "São Tomé and Príncipe (São Tomé e Príncipe)",
39490         "st",
39491         "239"
39492       ],
39493       [
39494         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39495         "sa",
39496         "966"
39497       ],
39498       [
39499         "Senegal (Sénégal)",
39500         "sn",
39501         "221"
39502       ],
39503       [
39504         "Serbia (Србија)",
39505         "rs",
39506         "381"
39507       ],
39508       [
39509         "Seychelles",
39510         "sc",
39511         "248"
39512       ],
39513       [
39514         "Sierra Leone",
39515         "sl",
39516         "232"
39517       ],
39518       [
39519         "Singapore",
39520         "sg",
39521         "65"
39522       ],
39523       [
39524         "Sint Maarten",
39525         "sx",
39526         "1721"
39527       ],
39528       [
39529         "Slovakia (Slovensko)",
39530         "sk",
39531         "421"
39532       ],
39533       [
39534         "Slovenia (Slovenija)",
39535         "si",
39536         "386"
39537       ],
39538       [
39539         "Solomon Islands",
39540         "sb",
39541         "677"
39542       ],
39543       [
39544         "Somalia (Soomaaliya)",
39545         "so",
39546         "252"
39547       ],
39548       [
39549         "South Africa",
39550         "za",
39551         "27"
39552       ],
39553       [
39554         "South Korea (대한민국)",
39555         "kr",
39556         "82"
39557       ],
39558       [
39559         "South Sudan (‫جنوب السودان‬‎)",
39560         "ss",
39561         "211"
39562       ],
39563       [
39564         "Spain (España)",
39565         "es",
39566         "34"
39567       ],
39568       [
39569         "Sri Lanka (ශ්‍රී ලංකාව)",
39570         "lk",
39571         "94"
39572       ],
39573       [
39574         "Sudan (‫السودان‬‎)",
39575         "sd",
39576         "249"
39577       ],
39578       [
39579         "Suriname",
39580         "sr",
39581         "597"
39582       ],
39583       [
39584         "Svalbard and Jan Mayen",
39585         "sj",
39586         "47",
39587         1
39588       ],
39589       [
39590         "Swaziland",
39591         "sz",
39592         "268"
39593       ],
39594       [
39595         "Sweden (Sverige)",
39596         "se",
39597         "46"
39598       ],
39599       [
39600         "Switzerland (Schweiz)",
39601         "ch",
39602         "41"
39603       ],
39604       [
39605         "Syria (‫سوريا‬‎)",
39606         "sy",
39607         "963"
39608       ],
39609       [
39610         "Taiwan (台灣)",
39611         "tw",
39612         "886"
39613       ],
39614       [
39615         "Tajikistan",
39616         "tj",
39617         "992"
39618       ],
39619       [
39620         "Tanzania",
39621         "tz",
39622         "255"
39623       ],
39624       [
39625         "Thailand (ไทย)",
39626         "th",
39627         "66"
39628       ],
39629       [
39630         "Timor-Leste",
39631         "tl",
39632         "670"
39633       ],
39634       [
39635         "Togo",
39636         "tg",
39637         "228"
39638       ],
39639       [
39640         "Tokelau",
39641         "tk",
39642         "690"
39643       ],
39644       [
39645         "Tonga",
39646         "to",
39647         "676"
39648       ],
39649       [
39650         "Trinidad and Tobago",
39651         "tt",
39652         "1868"
39653       ],
39654       [
39655         "Tunisia (‫تونس‬‎)",
39656         "tn",
39657         "216"
39658       ],
39659       [
39660         "Turkey (Türkiye)",
39661         "tr",
39662         "90"
39663       ],
39664       [
39665         "Turkmenistan",
39666         "tm",
39667         "993"
39668       ],
39669       [
39670         "Turks and Caicos Islands",
39671         "tc",
39672         "1649"
39673       ],
39674       [
39675         "Tuvalu",
39676         "tv",
39677         "688"
39678       ],
39679       [
39680         "U.S. Virgin Islands",
39681         "vi",
39682         "1340"
39683       ],
39684       [
39685         "Uganda",
39686         "ug",
39687         "256"
39688       ],
39689       [
39690         "Ukraine (Україна)",
39691         "ua",
39692         "380"
39693       ],
39694       [
39695         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39696         "ae",
39697         "971"
39698       ],
39699       [
39700         "United Kingdom",
39701         "gb",
39702         "44",
39703         0
39704       ],
39705       [
39706         "United States",
39707         "us",
39708         "1",
39709         0
39710       ],
39711       [
39712         "Uruguay",
39713         "uy",
39714         "598"
39715       ],
39716       [
39717         "Uzbekistan (Oʻzbekiston)",
39718         "uz",
39719         "998"
39720       ],
39721       [
39722         "Vanuatu",
39723         "vu",
39724         "678"
39725       ],
39726       [
39727         "Vatican City (Città del Vaticano)",
39728         "va",
39729         "39",
39730         1
39731       ],
39732       [
39733         "Venezuela",
39734         "ve",
39735         "58"
39736       ],
39737       [
39738         "Vietnam (Việt Nam)",
39739         "vn",
39740         "84"
39741       ],
39742       [
39743         "Wallis and Futuna (Wallis-et-Futuna)",
39744         "wf",
39745         "681"
39746       ],
39747       [
39748         "Western Sahara (‫الصحراء الغربية‬‎)",
39749         "eh",
39750         "212",
39751         1
39752       ],
39753       [
39754         "Yemen (‫اليمن‬‎)",
39755         "ye",
39756         "967"
39757       ],
39758       [
39759         "Zambia",
39760         "zm",
39761         "260"
39762       ],
39763       [
39764         "Zimbabwe",
39765         "zw",
39766         "263"
39767       ],
39768       [
39769         "Åland Islands",
39770         "ax",
39771         "358",
39772         1
39773       ]
39774   ];
39775   
39776   return d;
39777 }/**
39778 *    This script refer to:
39779 *    Title: International Telephone Input
39780 *    Author: Jack O'Connor
39781 *    Code version:  v12.1.12
39782 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39783 **/
39784
39785 /**
39786  * @class Roo.bootstrap.PhoneInput
39787  * @extends Roo.bootstrap.TriggerField
39788  * An input with International dial-code selection
39789  
39790  * @cfg {String} defaultDialCode default '+852'
39791  * @cfg {Array} preferedCountries default []
39792   
39793  * @constructor
39794  * Create a new PhoneInput.
39795  * @param {Object} config Configuration options
39796  */
39797
39798 Roo.bootstrap.PhoneInput = function(config) {
39799     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39800 };
39801
39802 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39803         
39804         listWidth: undefined,
39805         
39806         selectedClass: 'active',
39807         
39808         invalidClass : "has-warning",
39809         
39810         validClass: 'has-success',
39811         
39812         allowed: '0123456789',
39813         
39814         /**
39815          * @cfg {String} defaultDialCode The default dial code when initializing the input
39816          */
39817         defaultDialCode: '+852',
39818         
39819         /**
39820          * @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
39821          */
39822         preferedCountries: false,
39823         
39824         getAutoCreate : function()
39825         {
39826             var data = Roo.bootstrap.PhoneInputData();
39827             var align = this.labelAlign || this.parentLabelAlign();
39828             var id = Roo.id();
39829             
39830             this.allCountries = [];
39831             this.dialCodeMapping = [];
39832             
39833             for (var i = 0; i < data.length; i++) {
39834               var c = data[i];
39835               this.allCountries[i] = {
39836                 name: c[0],
39837                 iso2: c[1],
39838                 dialCode: c[2],
39839                 priority: c[3] || 0,
39840                 areaCodes: c[4] || null
39841               };
39842               this.dialCodeMapping[c[2]] = {
39843                   name: c[0],
39844                   iso2: c[1],
39845                   priority: c[3] || 0,
39846                   areaCodes: c[4] || null
39847               };
39848             }
39849             
39850             var cfg = {
39851                 cls: 'form-group',
39852                 cn: []
39853             };
39854             
39855             var input =  {
39856                 tag: 'input',
39857                 id : id,
39858                 cls : 'form-control tel-input',
39859                 autocomplete: 'new-password'
39860             };
39861             
39862             var hiddenInput = {
39863                 tag: 'input',
39864                 type: 'hidden',
39865                 cls: 'hidden-tel-input'
39866             };
39867             
39868             if (this.name) {
39869                 hiddenInput.name = this.name;
39870             }
39871             
39872             if (this.disabled) {
39873                 input.disabled = true;
39874             }
39875             
39876             var flag_container = {
39877                 tag: 'div',
39878                 cls: 'flag-box',
39879                 cn: [
39880                     {
39881                         tag: 'div',
39882                         cls: 'flag'
39883                     },
39884                     {
39885                         tag: 'div',
39886                         cls: 'caret'
39887                     }
39888                 ]
39889             };
39890             
39891             var box = {
39892                 tag: 'div',
39893                 cls: this.hasFeedback ? 'has-feedback' : '',
39894                 cn: [
39895                     hiddenInput,
39896                     input,
39897                     {
39898                         tag: 'input',
39899                         cls: 'dial-code-holder',
39900                         disabled: true
39901                     }
39902                 ]
39903             };
39904             
39905             var container = {
39906                 cls: 'roo-select2-container input-group',
39907                 cn: [
39908                     flag_container,
39909                     box
39910                 ]
39911             };
39912             
39913             if (this.fieldLabel.length) {
39914                 var indicator = {
39915                     tag: 'i',
39916                     tooltip: 'This field is required'
39917                 };
39918                 
39919                 var label = {
39920                     tag: 'label',
39921                     'for':  id,
39922                     cls: 'control-label',
39923                     cn: []
39924                 };
39925                 
39926                 var label_text = {
39927                     tag: 'span',
39928                     html: this.fieldLabel
39929                 };
39930                 
39931                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39932                 label.cn = [
39933                     indicator,
39934                     label_text
39935                 ];
39936                 
39937                 if(this.indicatorpos == 'right') {
39938                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39939                     label.cn = [
39940                         label_text,
39941                         indicator
39942                     ];
39943                 }
39944                 
39945                 if(align == 'left') {
39946                     container = {
39947                         tag: 'div',
39948                         cn: [
39949                             container
39950                         ]
39951                     };
39952                     
39953                     if(this.labelWidth > 12){
39954                         label.style = "width: " + this.labelWidth + 'px';
39955                     }
39956                     if(this.labelWidth < 13 && this.labelmd == 0){
39957                         this.labelmd = this.labelWidth;
39958                     }
39959                     if(this.labellg > 0){
39960                         label.cls += ' col-lg-' + this.labellg;
39961                         input.cls += ' col-lg-' + (12 - this.labellg);
39962                     }
39963                     if(this.labelmd > 0){
39964                         label.cls += ' col-md-' + this.labelmd;
39965                         container.cls += ' col-md-' + (12 - this.labelmd);
39966                     }
39967                     if(this.labelsm > 0){
39968                         label.cls += ' col-sm-' + this.labelsm;
39969                         container.cls += ' col-sm-' + (12 - this.labelsm);
39970                     }
39971                     if(this.labelxs > 0){
39972                         label.cls += ' col-xs-' + this.labelxs;
39973                         container.cls += ' col-xs-' + (12 - this.labelxs);
39974                     }
39975                 }
39976             }
39977             
39978             cfg.cn = [
39979                 label,
39980                 container
39981             ];
39982             
39983             var settings = this;
39984             
39985             ['xs','sm','md','lg'].map(function(size){
39986                 if (settings[size]) {
39987                     cfg.cls += ' col-' + size + '-' + settings[size];
39988                 }
39989             });
39990             
39991             this.store = new Roo.data.Store({
39992                 proxy : new Roo.data.MemoryProxy({}),
39993                 reader : new Roo.data.JsonReader({
39994                     fields : [
39995                         {
39996                             'name' : 'name',
39997                             'type' : 'string'
39998                         },
39999                         {
40000                             'name' : 'iso2',
40001                             'type' : 'string'
40002                         },
40003                         {
40004                             'name' : 'dialCode',
40005                             'type' : 'string'
40006                         },
40007                         {
40008                             'name' : 'priority',
40009                             'type' : 'string'
40010                         },
40011                         {
40012                             'name' : 'areaCodes',
40013                             'type' : 'string'
40014                         }
40015                     ]
40016                 })
40017             });
40018             
40019             if(!this.preferedCountries) {
40020                 this.preferedCountries = [
40021                     'hk',
40022                     'gb',
40023                     'us'
40024                 ];
40025             }
40026             
40027             var p = this.preferedCountries.reverse();
40028             
40029             if(p) {
40030                 for (var i = 0; i < p.length; i++) {
40031                     for (var j = 0; j < this.allCountries.length; j++) {
40032                         if(this.allCountries[j].iso2 == p[i]) {
40033                             var t = this.allCountries[j];
40034                             this.allCountries.splice(j,1);
40035                             this.allCountries.unshift(t);
40036                         }
40037                     } 
40038                 }
40039             }
40040             
40041             this.store.proxy.data = {
40042                 success: true,
40043                 data: this.allCountries
40044             };
40045             
40046             return cfg;
40047         },
40048         
40049         initEvents : function()
40050         {
40051             this.createList();
40052             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40053             
40054             this.indicator = this.indicatorEl();
40055             this.flag = this.flagEl();
40056             this.dialCodeHolder = this.dialCodeHolderEl();
40057             
40058             this.trigger = this.el.select('div.flag-box',true).first();
40059             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40060             
40061             var _this = this;
40062             
40063             (function(){
40064                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40065                 _this.list.setWidth(lw);
40066             }).defer(100);
40067             
40068             this.list.on('mouseover', this.onViewOver, this);
40069             this.list.on('mousemove', this.onViewMove, this);
40070             this.inputEl().on("keyup", this.onKeyUp, this);
40071             
40072             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40073
40074             this.view = new Roo.View(this.list, this.tpl, {
40075                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40076             });
40077             
40078             this.view.on('click', this.onViewClick, this);
40079             this.setValue(this.defaultDialCode);
40080         },
40081         
40082         onTriggerClick : function(e)
40083         {
40084             Roo.log('trigger click');
40085             if(this.disabled){
40086                 return;
40087             }
40088             
40089             if(this.isExpanded()){
40090                 this.collapse();
40091                 this.hasFocus = false;
40092             }else {
40093                 this.store.load({});
40094                 this.hasFocus = true;
40095                 this.expand();
40096             }
40097         },
40098         
40099         isExpanded : function()
40100         {
40101             return this.list.isVisible();
40102         },
40103         
40104         collapse : function()
40105         {
40106             if(!this.isExpanded()){
40107                 return;
40108             }
40109             this.list.hide();
40110             Roo.get(document).un('mousedown', this.collapseIf, this);
40111             Roo.get(document).un('mousewheel', this.collapseIf, this);
40112             this.fireEvent('collapse', this);
40113             this.validate();
40114         },
40115         
40116         expand : function()
40117         {
40118             Roo.log('expand');
40119
40120             if(this.isExpanded() || !this.hasFocus){
40121                 return;
40122             }
40123             
40124             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40125             this.list.setWidth(lw);
40126             
40127             this.list.show();
40128             this.restrictHeight();
40129             
40130             Roo.get(document).on('mousedown', this.collapseIf, this);
40131             Roo.get(document).on('mousewheel', this.collapseIf, this);
40132             
40133             this.fireEvent('expand', this);
40134         },
40135         
40136         restrictHeight : function()
40137         {
40138             this.list.alignTo(this.inputEl(), this.listAlign);
40139             this.list.alignTo(this.inputEl(), this.listAlign);
40140         },
40141         
40142         onViewOver : function(e, t)
40143         {
40144             if(this.inKeyMode){
40145                 return;
40146             }
40147             var item = this.view.findItemFromChild(t);
40148             
40149             if(item){
40150                 var index = this.view.indexOf(item);
40151                 this.select(index, false);
40152             }
40153         },
40154
40155         // private
40156         onViewClick : function(view, doFocus, el, e)
40157         {
40158             var index = this.view.getSelectedIndexes()[0];
40159             
40160             var r = this.store.getAt(index);
40161             
40162             if(r){
40163                 this.onSelect(r, index);
40164             }
40165             if(doFocus !== false && !this.blockFocus){
40166                 this.inputEl().focus();
40167             }
40168         },
40169         
40170         onViewMove : function(e, t)
40171         {
40172             this.inKeyMode = false;
40173         },
40174         
40175         select : function(index, scrollIntoView)
40176         {
40177             this.selectedIndex = index;
40178             this.view.select(index);
40179             if(scrollIntoView !== false){
40180                 var el = this.view.getNode(index);
40181                 if(el){
40182                     this.list.scrollChildIntoView(el, false);
40183                 }
40184             }
40185         },
40186         
40187         createList : function()
40188         {
40189             this.list = Roo.get(document.body).createChild({
40190                 tag: 'ul',
40191                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40192                 style: 'display:none'
40193             });
40194             
40195             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40196         },
40197         
40198         collapseIf : function(e)
40199         {
40200             var in_combo  = e.within(this.el);
40201             var in_list =  e.within(this.list);
40202             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40203             
40204             if (in_combo || in_list || is_list) {
40205                 return;
40206             }
40207             this.collapse();
40208         },
40209         
40210         onSelect : function(record, index)
40211         {
40212             if(this.fireEvent('beforeselect', this, record, index) !== false){
40213                 
40214                 this.setFlagClass(record.data.iso2);
40215                 this.setDialCode(record.data.dialCode);
40216                 this.hasFocus = false;
40217                 this.collapse();
40218                 this.fireEvent('select', this, record, index);
40219             }
40220         },
40221         
40222         flagEl : function()
40223         {
40224             var flag = this.el.select('div.flag',true).first();
40225             if(!flag){
40226                 return false;
40227             }
40228             return flag;
40229         },
40230         
40231         dialCodeHolderEl : function()
40232         {
40233             var d = this.el.select('input.dial-code-holder',true).first();
40234             if(!d){
40235                 return false;
40236             }
40237             return d;
40238         },
40239         
40240         setDialCode : function(v)
40241         {
40242             this.dialCodeHolder.dom.value = '+'+v;
40243         },
40244         
40245         setFlagClass : function(n)
40246         {
40247             this.flag.dom.className = 'flag '+n;
40248         },
40249         
40250         getValue : function()
40251         {
40252             var v = this.inputEl().getValue();
40253             if(this.dialCodeHolder) {
40254                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40255             }
40256             return v;
40257         },
40258         
40259         setValue : function(v)
40260         {
40261             var d = this.getDialCode(v);
40262             
40263             //invalid dial code
40264             if(v.length == 0 || !d || d.length == 0) {
40265                 if(this.rendered){
40266                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40267                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40268                 }
40269                 return;
40270             }
40271             
40272             //valid dial code
40273             this.setFlagClass(this.dialCodeMapping[d].iso2);
40274             this.setDialCode(d);
40275             this.inputEl().dom.value = v.replace('+'+d,'');
40276             this.hiddenEl().dom.value = this.getValue();
40277             
40278             this.validate();
40279         },
40280         
40281         getDialCode : function(v)
40282         {
40283             v = v ||  '';
40284             
40285             if (v.length == 0) {
40286                 return this.dialCodeHolder.dom.value;
40287             }
40288             
40289             var dialCode = "";
40290             if (v.charAt(0) != "+") {
40291                 return false;
40292             }
40293             var numericChars = "";
40294             for (var i = 1; i < v.length; i++) {
40295               var c = v.charAt(i);
40296               if (!isNaN(c)) {
40297                 numericChars += c;
40298                 if (this.dialCodeMapping[numericChars]) {
40299                   dialCode = v.substr(1, i);
40300                 }
40301                 if (numericChars.length == 4) {
40302                   break;
40303                 }
40304               }
40305             }
40306             return dialCode;
40307         },
40308         
40309         reset : function()
40310         {
40311             this.setValue(this.defaultDialCode);
40312             this.validate();
40313         },
40314         
40315         hiddenEl : function()
40316         {
40317             return this.el.select('input.hidden-tel-input',true).first();
40318         },
40319         
40320         onKeyUp : function(e){
40321             
40322             var k = e.getKey();
40323             var c = e.getCharCode();
40324             
40325             if(
40326                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40327                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40328             ){
40329                 e.stopEvent();
40330             }
40331             
40332             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40333             //     return;
40334             // }
40335             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40336                 e.stopEvent();
40337             }
40338             
40339             this.setValue(this.getValue());
40340         }
40341         
40342 });
40343 /**
40344  * @class Roo.bootstrap.MoneyField
40345  * @extends Roo.bootstrap.ComboBox
40346  * Bootstrap MoneyField class
40347  * 
40348  * @constructor
40349  * Create a new MoneyField.
40350  * @param {Object} config Configuration options
40351  */
40352
40353 Roo.bootstrap.MoneyField = function(config) {
40354     
40355     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40356     
40357 };
40358
40359 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40360     
40361     /**
40362      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40363      */
40364     allowDecimals : true,
40365     /**
40366      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40367      */
40368     decimalSeparator : ".",
40369     /**
40370      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40371      */
40372     decimalPrecision : 0,
40373     /**
40374      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40375      */
40376     allowNegative : true,
40377     /**
40378      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40379      */
40380     allowZero: true,
40381     /**
40382      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40383      */
40384     minValue : Number.NEGATIVE_INFINITY,
40385     /**
40386      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40387      */
40388     maxValue : Number.MAX_VALUE,
40389     /**
40390      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40391      */
40392     minText : "The minimum value for this field is {0}",
40393     /**
40394      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40395      */
40396     maxText : "The maximum value for this field is {0}",
40397     /**
40398      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40399      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40400      */
40401     nanText : "{0} is not a valid number",
40402     /**
40403      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40404      */
40405     castInt : true,
40406     /**
40407      * @cfg {String} defaults currency of the MoneyField
40408      * value should be in lkey
40409      */
40410     defaultCurrency : false,
40411     /**
40412      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40413      */
40414     thousandsDelimiter : false,
40415     
40416     
40417     inputlg : 9,
40418     inputmd : 9,
40419     inputsm : 9,
40420     inputxs : 6,
40421     
40422     store : false,
40423     
40424     getAutoCreate : function()
40425     {
40426         var align = this.labelAlign || this.parentLabelAlign();
40427         
40428         var id = Roo.id();
40429
40430         var cfg = {
40431             cls: 'form-group',
40432             cn: []
40433         };
40434
40435         var input =  {
40436             tag: 'input',
40437             id : id,
40438             cls : 'form-control roo-money-amount-input',
40439             autocomplete: 'new-password'
40440         };
40441         
40442         var hiddenInput = {
40443             tag: 'input',
40444             type: 'hidden',
40445             id: Roo.id(),
40446             cls: 'hidden-number-input'
40447         };
40448         
40449         if (this.name) {
40450             hiddenInput.name = this.name;
40451         }
40452
40453         if (this.disabled) {
40454             input.disabled = true;
40455         }
40456
40457         var clg = 12 - this.inputlg;
40458         var cmd = 12 - this.inputmd;
40459         var csm = 12 - this.inputsm;
40460         var cxs = 12 - this.inputxs;
40461         
40462         var container = {
40463             tag : 'div',
40464             cls : 'row roo-money-field',
40465             cn : [
40466                 {
40467                     tag : 'div',
40468                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40469                     cn : [
40470                         {
40471                             tag : 'div',
40472                             cls: 'roo-select2-container input-group',
40473                             cn: [
40474                                 {
40475                                     tag : 'input',
40476                                     cls : 'form-control roo-money-currency-input',
40477                                     autocomplete: 'new-password',
40478                                     readOnly : 1,
40479                                     name : this.currencyName
40480                                 },
40481                                 {
40482                                     tag :'span',
40483                                     cls : 'input-group-addon',
40484                                     cn : [
40485                                         {
40486                                             tag: 'span',
40487                                             cls: 'caret'
40488                                         }
40489                                     ]
40490                                 }
40491                             ]
40492                         }
40493                     ]
40494                 },
40495                 {
40496                     tag : 'div',
40497                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40498                     cn : [
40499                         {
40500                             tag: 'div',
40501                             cls: this.hasFeedback ? 'has-feedback' : '',
40502                             cn: [
40503                                 input
40504                             ]
40505                         }
40506                     ]
40507                 }
40508             ]
40509             
40510         };
40511         
40512         if (this.fieldLabel.length) {
40513             var indicator = {
40514                 tag: 'i',
40515                 tooltip: 'This field is required'
40516             };
40517
40518             var label = {
40519                 tag: 'label',
40520                 'for':  id,
40521                 cls: 'control-label',
40522                 cn: []
40523             };
40524
40525             var label_text = {
40526                 tag: 'span',
40527                 html: this.fieldLabel
40528             };
40529
40530             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40531             label.cn = [
40532                 indicator,
40533                 label_text
40534             ];
40535
40536             if(this.indicatorpos == 'right') {
40537                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40538                 label.cn = [
40539                     label_text,
40540                     indicator
40541                 ];
40542             }
40543
40544             if(align == 'left') {
40545                 container = {
40546                     tag: 'div',
40547                     cn: [
40548                         container
40549                     ]
40550                 };
40551
40552                 if(this.labelWidth > 12){
40553                     label.style = "width: " + this.labelWidth + 'px';
40554                 }
40555                 if(this.labelWidth < 13 && this.labelmd == 0){
40556                     this.labelmd = this.labelWidth;
40557                 }
40558                 if(this.labellg > 0){
40559                     label.cls += ' col-lg-' + this.labellg;
40560                     input.cls += ' col-lg-' + (12 - this.labellg);
40561                 }
40562                 if(this.labelmd > 0){
40563                     label.cls += ' col-md-' + this.labelmd;
40564                     container.cls += ' col-md-' + (12 - this.labelmd);
40565                 }
40566                 if(this.labelsm > 0){
40567                     label.cls += ' col-sm-' + this.labelsm;
40568                     container.cls += ' col-sm-' + (12 - this.labelsm);
40569                 }
40570                 if(this.labelxs > 0){
40571                     label.cls += ' col-xs-' + this.labelxs;
40572                     container.cls += ' col-xs-' + (12 - this.labelxs);
40573                 }
40574             }
40575         }
40576
40577         cfg.cn = [
40578             label,
40579             container,
40580             hiddenInput
40581         ];
40582         
40583         var settings = this;
40584
40585         ['xs','sm','md','lg'].map(function(size){
40586             if (settings[size]) {
40587                 cfg.cls += ' col-' + size + '-' + settings[size];
40588             }
40589         });
40590         
40591         return cfg;
40592     },
40593     
40594     initEvents : function()
40595     {
40596         this.indicator = this.indicatorEl();
40597         
40598         this.initCurrencyEvent();
40599         
40600         this.initNumberEvent();
40601     },
40602     
40603     initCurrencyEvent : function()
40604     {
40605         if (!this.store) {
40606             throw "can not find store for combo";
40607         }
40608         
40609         this.store = Roo.factory(this.store, Roo.data);
40610         this.store.parent = this;
40611         
40612         this.createList();
40613         
40614         this.triggerEl = this.el.select('.input-group-addon', true).first();
40615         
40616         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40617         
40618         var _this = this;
40619         
40620         (function(){
40621             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40622             _this.list.setWidth(lw);
40623         }).defer(100);
40624         
40625         this.list.on('mouseover', this.onViewOver, this);
40626         this.list.on('mousemove', this.onViewMove, this);
40627         this.list.on('scroll', this.onViewScroll, this);
40628         
40629         if(!this.tpl){
40630             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40631         }
40632         
40633         this.view = new Roo.View(this.list, this.tpl, {
40634             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40635         });
40636         
40637         this.view.on('click', this.onViewClick, this);
40638         
40639         this.store.on('beforeload', this.onBeforeLoad, this);
40640         this.store.on('load', this.onLoad, this);
40641         this.store.on('loadexception', this.onLoadException, this);
40642         
40643         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40644             "up" : function(e){
40645                 this.inKeyMode = true;
40646                 this.selectPrev();
40647             },
40648
40649             "down" : function(e){
40650                 if(!this.isExpanded()){
40651                     this.onTriggerClick();
40652                 }else{
40653                     this.inKeyMode = true;
40654                     this.selectNext();
40655                 }
40656             },
40657
40658             "enter" : function(e){
40659                 this.collapse();
40660                 
40661                 if(this.fireEvent("specialkey", this, e)){
40662                     this.onViewClick(false);
40663                 }
40664                 
40665                 return true;
40666             },
40667
40668             "esc" : function(e){
40669                 this.collapse();
40670             },
40671
40672             "tab" : function(e){
40673                 this.collapse();
40674                 
40675                 if(this.fireEvent("specialkey", this, e)){
40676                     this.onViewClick(false);
40677                 }
40678                 
40679                 return true;
40680             },
40681
40682             scope : this,
40683
40684             doRelay : function(foo, bar, hname){
40685                 if(hname == 'down' || this.scope.isExpanded()){
40686                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40687                 }
40688                 return true;
40689             },
40690
40691             forceKeyDown: true
40692         });
40693         
40694         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40695         
40696     },
40697     
40698     initNumberEvent : function(e)
40699     {
40700         this.inputEl().on("keydown" , this.fireKey,  this);
40701         this.inputEl().on("focus", this.onFocus,  this);
40702         this.inputEl().on("blur", this.onBlur,  this);
40703         
40704         this.inputEl().relayEvent('keyup', this);
40705         
40706         if(this.indicator){
40707             this.indicator.addClass('invisible');
40708         }
40709  
40710         this.originalValue = this.getValue();
40711         
40712         if(this.validationEvent == 'keyup'){
40713             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40714             this.inputEl().on('keyup', this.filterValidation, this);
40715         }
40716         else if(this.validationEvent !== false){
40717             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40718         }
40719         
40720         if(this.selectOnFocus){
40721             this.on("focus", this.preFocus, this);
40722             
40723         }
40724         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40725             this.inputEl().on("keypress", this.filterKeys, this);
40726         } else {
40727             this.inputEl().relayEvent('keypress', this);
40728         }
40729         
40730         var allowed = "0123456789";
40731         
40732         if(this.allowDecimals){
40733             allowed += this.decimalSeparator;
40734         }
40735         
40736         if(this.allowNegative){
40737             allowed += "-";
40738         }
40739         
40740         if(this.thousandsDelimiter) {
40741             allowed += ",";
40742         }
40743         
40744         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40745         
40746         var keyPress = function(e){
40747             
40748             var k = e.getKey();
40749             
40750             var c = e.getCharCode();
40751             
40752             if(
40753                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40754                     allowed.indexOf(String.fromCharCode(c)) === -1
40755             ){
40756                 e.stopEvent();
40757                 return;
40758             }
40759             
40760             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40761                 return;
40762             }
40763             
40764             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40765                 e.stopEvent();
40766             }
40767         };
40768         
40769         this.inputEl().on("keypress", keyPress, this);
40770         
40771     },
40772     
40773     onTriggerClick : function(e)
40774     {   
40775         if(this.disabled){
40776             return;
40777         }
40778         
40779         this.page = 0;
40780         this.loadNext = false;
40781         
40782         if(this.isExpanded()){
40783             this.collapse();
40784             return;
40785         }
40786         
40787         this.hasFocus = true;
40788         
40789         if(this.triggerAction == 'all') {
40790             this.doQuery(this.allQuery, true);
40791             return;
40792         }
40793         
40794         this.doQuery(this.getRawValue());
40795     },
40796     
40797     getCurrency : function()
40798     {   
40799         var v = this.currencyEl().getValue();
40800         
40801         return v;
40802     },
40803     
40804     restrictHeight : function()
40805     {
40806         this.list.alignTo(this.currencyEl(), this.listAlign);
40807         this.list.alignTo(this.currencyEl(), this.listAlign);
40808     },
40809     
40810     onViewClick : function(view, doFocus, el, e)
40811     {
40812         var index = this.view.getSelectedIndexes()[0];
40813         
40814         var r = this.store.getAt(index);
40815         
40816         if(r){
40817             this.onSelect(r, index);
40818         }
40819     },
40820     
40821     onSelect : function(record, index){
40822         
40823         if(this.fireEvent('beforeselect', this, record, index) !== false){
40824         
40825             this.setFromCurrencyData(index > -1 ? record.data : false);
40826             
40827             this.collapse();
40828             
40829             this.fireEvent('select', this, record, index);
40830         }
40831     },
40832     
40833     setFromCurrencyData : function(o)
40834     {
40835         var currency = '';
40836         
40837         this.lastCurrency = o;
40838         
40839         if (this.currencyField) {
40840             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40841         } else {
40842             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40843         }
40844         
40845         this.lastSelectionText = currency;
40846         
40847         //setting default currency
40848         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40849             this.setCurrency(this.defaultCurrency);
40850             return;
40851         }
40852         
40853         this.setCurrency(currency);
40854     },
40855     
40856     setFromData : function(o)
40857     {
40858         var c = {};
40859         
40860         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40861         
40862         this.setFromCurrencyData(c);
40863         
40864         var value = '';
40865         
40866         if (this.name) {
40867             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40868         } else {
40869             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40870         }
40871         
40872         this.setValue(value);
40873         
40874     },
40875     
40876     setCurrency : function(v)
40877     {   
40878         this.currencyValue = v;
40879         
40880         if(this.rendered){
40881             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40882             this.validate();
40883         }
40884     },
40885     
40886     setValue : function(v)
40887     {
40888         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40889         
40890         this.value = v;
40891         
40892         if(this.rendered){
40893             
40894             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40895             
40896             this.inputEl().dom.value = (v == '') ? '' :
40897                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40898             
40899             if(!this.allowZero && v === '0') {
40900                 this.hiddenEl().dom.value = '';
40901                 this.inputEl().dom.value = '';
40902             }
40903             
40904             this.validate();
40905         }
40906     },
40907     
40908     getRawValue : function()
40909     {
40910         var v = this.inputEl().getValue();
40911         
40912         return v;
40913     },
40914     
40915     getValue : function()
40916     {
40917         return this.fixPrecision(this.parseValue(this.getRawValue()));
40918     },
40919     
40920     parseValue : function(value)
40921     {
40922         if(this.thousandsDelimiter) {
40923             value += "";
40924             r = new RegExp(",", "g");
40925             value = value.replace(r, "");
40926         }
40927         
40928         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40929         return isNaN(value) ? '' : value;
40930         
40931     },
40932     
40933     fixPrecision : function(value)
40934     {
40935         if(this.thousandsDelimiter) {
40936             value += "";
40937             r = new RegExp(",", "g");
40938             value = value.replace(r, "");
40939         }
40940         
40941         var nan = isNaN(value);
40942         
40943         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40944             return nan ? '' : value;
40945         }
40946         return parseFloat(value).toFixed(this.decimalPrecision);
40947     },
40948     
40949     decimalPrecisionFcn : function(v)
40950     {
40951         return Math.floor(v);
40952     },
40953     
40954     validateValue : function(value)
40955     {
40956         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40957             return false;
40958         }
40959         
40960         var num = this.parseValue(value);
40961         
40962         if(isNaN(num)){
40963             this.markInvalid(String.format(this.nanText, value));
40964             return false;
40965         }
40966         
40967         if(num < this.minValue){
40968             this.markInvalid(String.format(this.minText, this.minValue));
40969             return false;
40970         }
40971         
40972         if(num > this.maxValue){
40973             this.markInvalid(String.format(this.maxText, this.maxValue));
40974             return false;
40975         }
40976         
40977         return true;
40978     },
40979     
40980     validate : function()
40981     {
40982         if(this.disabled || this.allowBlank){
40983             this.markValid();
40984             return true;
40985         }
40986         
40987         var currency = this.getCurrency();
40988         
40989         if(this.validateValue(this.getRawValue()) && currency.length){
40990             this.markValid();
40991             return true;
40992         }
40993         
40994         this.markInvalid();
40995         return false;
40996     },
40997     
40998     getName: function()
40999     {
41000         return this.name;
41001     },
41002     
41003     beforeBlur : function()
41004     {
41005         if(!this.castInt){
41006             return;
41007         }
41008         
41009         var v = this.parseValue(this.getRawValue());
41010         
41011         if(v || v == 0){
41012             this.setValue(v);
41013         }
41014     },
41015     
41016     onBlur : function()
41017     {
41018         this.beforeBlur();
41019         
41020         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41021             //this.el.removeClass(this.focusClass);
41022         }
41023         
41024         this.hasFocus = false;
41025         
41026         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41027             this.validate();
41028         }
41029         
41030         var v = this.getValue();
41031         
41032         if(String(v) !== String(this.startValue)){
41033             this.fireEvent('change', this, v, this.startValue);
41034         }
41035         
41036         this.fireEvent("blur", this);
41037     },
41038     
41039     inputEl : function()
41040     {
41041         return this.el.select('.roo-money-amount-input', true).first();
41042     },
41043     
41044     currencyEl : function()
41045     {
41046         return this.el.select('.roo-money-currency-input', true).first();
41047     },
41048     
41049     hiddenEl : function()
41050     {
41051         return this.el.select('input.hidden-number-input',true).first();
41052     }
41053     
41054 });