roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     
921     setWeight : function(str)
922     {
923         this.el.removeClass(this.weightClass);
924         this.el.addClass('btn-' + str);        
925     }
926     
927     
928 });
929
930  /*
931  * - LGPL
932  *
933  * column
934  * 
935  */
936
937 /**
938  * @class Roo.bootstrap.Column
939  * @extends Roo.bootstrap.Component
940  * Bootstrap Column class
941  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
949  *
950  * 
951  * @cfg {Boolean} hidden (true|false) hide the element
952  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953  * @cfg {String} fa (ban|check|...) font awesome icon
954  * @cfg {Number} fasize (1|2|....) font awsome size
955
956  * @cfg {String} icon (info-sign|check|...) glyphicon name
957
958  * @cfg {String} html content of column.
959  * 
960  * @constructor
961  * Create a new Column
962  * @param {Object} config The config object
963  */
964
965 Roo.bootstrap.Column = function(config){
966     Roo.bootstrap.Column.superclass.constructor.call(this, config);
967 };
968
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
970     
971     xs: false,
972     sm: false,
973     md: false,
974     lg: false,
975     xsoff: false,
976     smoff: false,
977     mdoff: false,
978     lgoff: false,
979     html: '',
980     offset: 0,
981     alert: false,
982     fa: false,
983     icon : false,
984     hidden : false,
985     fasize : 1,
986     
987     getAutoCreate : function(){
988         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
989         
990         cfg = {
991             tag: 'div',
992             cls: 'column'
993         };
994         
995         var settings=this;
996         ['xs','sm','md','lg'].map(function(size){
997             //Roo.log( size + ':' + settings[size]);
998             
999             if (settings[size+'off'] !== false) {
1000                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1001             }
1002             
1003             if (settings[size] === false) {
1004                 return;
1005             }
1006             
1007             if (!settings[size]) { // 0 = hidden
1008                 cfg.cls += ' hidden-' + size;
1009                 return;
1010             }
1011             cfg.cls += ' col-' + size + '-' + settings[size];
1012             
1013         });
1014         
1015         if (this.hidden) {
1016             cfg.cls += ' hidden';
1017         }
1018         
1019         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020             cfg.cls +=' alert alert-' + this.alert;
1021         }
1022         
1023         
1024         if (this.html.length) {
1025             cfg.html = this.html;
1026         }
1027         if (this.fa) {
1028             var fasize = '';
1029             if (this.fasize > 1) {
1030                 fasize = ' fa-' + this.fasize + 'x';
1031             }
1032             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1033             
1034             
1035         }
1036         if (this.icon) {
1037             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1038         }
1039         
1040         return cfg;
1041     }
1042    
1043 });
1044
1045  
1046
1047  /*
1048  * - LGPL
1049  *
1050  * page container.
1051  * 
1052  */
1053
1054
1055 /**
1056  * @class Roo.bootstrap.Container
1057  * @extends Roo.bootstrap.Component
1058  * Bootstrap Container class
1059  * @cfg {Boolean} jumbotron is it a jumbotron element
1060  * @cfg {String} html content of element
1061  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1063  * @cfg {String} header content of header (for panel)
1064  * @cfg {String} footer content of footer (for panel)
1065  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066  * @cfg {String} tag (header|aside|section) type of HTML tag.
1067  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068  * @cfg {String} fa font awesome icon
1069  * @cfg {String} icon (info-sign|check|...) glyphicon name
1070  * @cfg {Boolean} hidden (true|false) hide the element
1071  * @cfg {Boolean} expandable (true|false) default false
1072  * @cfg {Boolean} expanded (true|false) default true
1073  * @cfg {String} rheader contet on the right of header
1074  * @cfg {Boolean} clickable (true|false) default false
1075
1076  *     
1077  * @constructor
1078  * Create a new Container
1079  * @param {Object} config The config object
1080  */
1081
1082 Roo.bootstrap.Container = function(config){
1083     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1084     
1085     this.addEvents({
1086         // raw events
1087          /**
1088          * @event expand
1089          * After the panel has been expand
1090          * 
1091          * @param {Roo.bootstrap.Container} this
1092          */
1093         "expand" : true,
1094         /**
1095          * @event collapse
1096          * After the panel has been collapsed
1097          * 
1098          * @param {Roo.bootstrap.Container} this
1099          */
1100         "collapse" : true,
1101         /**
1102          * @event click
1103          * When a element is chick
1104          * @param {Roo.bootstrap.Container} this
1105          * @param {Roo.EventObject} e
1106          */
1107         "click" : true
1108     });
1109 };
1110
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1112     
1113     jumbotron : false,
1114     well: '',
1115     panel : '',
1116     header: '',
1117     footer : '',
1118     sticky: '',
1119     tag : false,
1120     alert : false,
1121     fa: false,
1122     icon : false,
1123     expandable : false,
1124     rheader : '',
1125     expanded : true,
1126     clickable: false,
1127   
1128      
1129     getChildContainer : function() {
1130         
1131         if(!this.el){
1132             return false;
1133         }
1134         
1135         if (this.panel.length) {
1136             return this.el.select('.panel-body',true).first();
1137         }
1138         
1139         return this.el;
1140     },
1141     
1142     
1143     getAutoCreate : function(){
1144         
1145         var cfg = {
1146             tag : this.tag || 'div',
1147             html : '',
1148             cls : ''
1149         };
1150         if (this.jumbotron) {
1151             cfg.cls = 'jumbotron';
1152         }
1153         
1154         
1155         
1156         // - this is applied by the parent..
1157         //if (this.cls) {
1158         //    cfg.cls = this.cls + '';
1159         //}
1160         
1161         if (this.sticky.length) {
1162             
1163             var bd = Roo.get(document.body);
1164             if (!bd.hasClass('bootstrap-sticky')) {
1165                 bd.addClass('bootstrap-sticky');
1166                 Roo.select('html',true).setStyle('height', '100%');
1167             }
1168              
1169             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1170         }
1171         
1172         
1173         if (this.well.length) {
1174             switch (this.well) {
1175                 case 'lg':
1176                 case 'sm':
1177                     cfg.cls +=' well well-' +this.well;
1178                     break;
1179                 default:
1180                     cfg.cls +=' well';
1181                     break;
1182             }
1183         }
1184         
1185         if (this.hidden) {
1186             cfg.cls += ' hidden';
1187         }
1188         
1189         
1190         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191             cfg.cls +=' alert alert-' + this.alert;
1192         }
1193         
1194         var body = cfg;
1195         
1196         if (this.panel.length) {
1197             cfg.cls += ' panel panel-' + this.panel;
1198             cfg.cn = [];
1199             if (this.header.length) {
1200                 
1201                 var h = [];
1202                 
1203                 if(this.expandable){
1204                     
1205                     cfg.cls = cfg.cls + ' expandable';
1206                     
1207                     h.push({
1208                         tag: 'i',
1209                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1210                     });
1211                     
1212                 }
1213                 
1214                 h.push(
1215                     {
1216                         tag: 'span',
1217                         cls : 'panel-title',
1218                         html : (this.expandable ? '&nbsp;' : '') + this.header
1219                     },
1220                     {
1221                         tag: 'span',
1222                         cls: 'panel-header-right',
1223                         html: this.rheader
1224                     }
1225                 );
1226                 
1227                 cfg.cn.push({
1228                     cls : 'panel-heading',
1229                     style : this.expandable ? 'cursor: pointer' : '',
1230                     cn : h
1231                 });
1232                 
1233             }
1234             
1235             body = false;
1236             cfg.cn.push({
1237                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1238                 html : this.html
1239             });
1240             
1241             
1242             if (this.footer.length) {
1243                 cfg.cn.push({
1244                     cls : 'panel-footer',
1245                     html : this.footer
1246                     
1247                 });
1248             }
1249             
1250         }
1251         
1252         if (body) {
1253             body.html = this.html || cfg.html;
1254             // prefix with the icons..
1255             if (this.fa) {
1256                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1257             }
1258             if (this.icon) {
1259                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1260             }
1261             
1262             
1263         }
1264         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265             cfg.cls =  'container';
1266         }
1267         
1268         return cfg;
1269     },
1270     
1271     initEvents: function() 
1272     {
1273         if(this.expandable){
1274             var headerEl = this.headerEl();
1275         
1276             if(headerEl){
1277                 headerEl.on('click', this.onToggleClick, this);
1278             }
1279         }
1280         
1281         if(this.clickable){
1282             this.el.on('click', this.onClick, this);
1283         }
1284         
1285     },
1286     
1287     onToggleClick : function()
1288     {
1289         var headerEl = this.headerEl();
1290         
1291         if(!headerEl){
1292             return;
1293         }
1294         
1295         if(this.expanded){
1296             this.collapse();
1297             return;
1298         }
1299         
1300         this.expand();
1301     },
1302     
1303     expand : function()
1304     {
1305         if(this.fireEvent('expand', this)) {
1306             
1307             this.expanded = true;
1308             
1309             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1310             
1311             this.el.select('.panel-body',true).first().removeClass('hide');
1312             
1313             var toggleEl = this.toggleEl();
1314
1315             if(!toggleEl){
1316                 return;
1317             }
1318
1319             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1320         }
1321         
1322     },
1323     
1324     collapse : function()
1325     {
1326         if(this.fireEvent('collapse', this)) {
1327             
1328             this.expanded = false;
1329             
1330             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331             this.el.select('.panel-body',true).first().addClass('hide');
1332         
1333             var toggleEl = this.toggleEl();
1334
1335             if(!toggleEl){
1336                 return;
1337             }
1338
1339             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1340         }
1341     },
1342     
1343     toggleEl : function()
1344     {
1345         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1346             return;
1347         }
1348         
1349         return this.el.select('.panel-heading .fa',true).first();
1350     },
1351     
1352     headerEl : function()
1353     {
1354         if(!this.el || !this.panel.length || !this.header.length){
1355             return;
1356         }
1357         
1358         return this.el.select('.panel-heading',true).first()
1359     },
1360     
1361     bodyEl : function()
1362     {
1363         if(!this.el || !this.panel.length){
1364             return;
1365         }
1366         
1367         return this.el.select('.panel-body',true).first()
1368     },
1369     
1370     titleEl : function()
1371     {
1372         if(!this.el || !this.panel.length || !this.header.length){
1373             return;
1374         }
1375         
1376         return this.el.select('.panel-title',true).first();
1377     },
1378     
1379     setTitle : function(v)
1380     {
1381         var titleEl = this.titleEl();
1382         
1383         if(!titleEl){
1384             return;
1385         }
1386         
1387         titleEl.dom.innerHTML = v;
1388     },
1389     
1390     getTitle : function()
1391     {
1392         
1393         var titleEl = this.titleEl();
1394         
1395         if(!titleEl){
1396             return '';
1397         }
1398         
1399         return titleEl.dom.innerHTML;
1400     },
1401     
1402     setRightTitle : function(v)
1403     {
1404         var t = this.el.select('.panel-header-right',true).first();
1405         
1406         if(!t){
1407             return;
1408         }
1409         
1410         t.dom.innerHTML = v;
1411     },
1412     
1413     onClick : function(e)
1414     {
1415         e.preventDefault();
1416         
1417         this.fireEvent('click', this, e);
1418     }
1419 });
1420
1421  /*
1422  * - LGPL
1423  *
1424  * image
1425  * 
1426  */
1427
1428
1429 /**
1430  * @class Roo.bootstrap.Img
1431  * @extends Roo.bootstrap.Component
1432  * Bootstrap Img class
1433  * @cfg {Boolean} imgResponsive false | true
1434  * @cfg {String} border rounded | circle | thumbnail
1435  * @cfg {String} src image source
1436  * @cfg {String} alt image alternative text
1437  * @cfg {String} href a tag href
1438  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439  * @cfg {String} xsUrl xs image source
1440  * @cfg {String} smUrl sm image source
1441  * @cfg {String} mdUrl md image source
1442  * @cfg {String} lgUrl lg image source
1443  * 
1444  * @constructor
1445  * Create a new Input
1446  * @param {Object} config The config object
1447  */
1448
1449 Roo.bootstrap.Img = function(config){
1450     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1451     
1452     this.addEvents({
1453         // img events
1454         /**
1455          * @event click
1456          * The img click event for the img.
1457          * @param {Roo.EventObject} e
1458          */
1459         "click" : true
1460     });
1461 };
1462
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1464     
1465     imgResponsive: true,
1466     border: '',
1467     src: 'about:blank',
1468     href: false,
1469     target: false,
1470     xsUrl: '',
1471     smUrl: '',
1472     mdUrl: '',
1473     lgUrl: '',
1474
1475     getAutoCreate : function()
1476     {   
1477         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478             return this.createSingleImg();
1479         }
1480         
1481         var cfg = {
1482             tag: 'div',
1483             cls: 'roo-image-responsive-group',
1484             cn: []
1485         };
1486         var _this = this;
1487         
1488         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1489             
1490             if(!_this[size + 'Url']){
1491                 return;
1492             }
1493             
1494             var img = {
1495                 tag: 'img',
1496                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497                 html: _this.html || cfg.html,
1498                 src: _this[size + 'Url']
1499             };
1500             
1501             img.cls += ' roo-image-responsive-' + size;
1502             
1503             var s = ['xs', 'sm', 'md', 'lg'];
1504             
1505             s.splice(s.indexOf(size), 1);
1506             
1507             Roo.each(s, function(ss){
1508                 img.cls += ' hidden-' + ss;
1509             });
1510             
1511             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512                 cfg.cls += ' img-' + _this.border;
1513             }
1514             
1515             if(_this.alt){
1516                 cfg.alt = _this.alt;
1517             }
1518             
1519             if(_this.href){
1520                 var a = {
1521                     tag: 'a',
1522                     href: _this.href,
1523                     cn: [
1524                         img
1525                     ]
1526                 };
1527
1528                 if(this.target){
1529                     a.target = _this.target;
1530                 }
1531             }
1532             
1533             cfg.cn.push((_this.href) ? a : img);
1534             
1535         });
1536         
1537         return cfg;
1538     },
1539     
1540     createSingleImg : function()
1541     {
1542         var cfg = {
1543             tag: 'img',
1544             cls: (this.imgResponsive) ? 'img-responsive' : '',
1545             html : null,
1546             src : 'about:blank'  // just incase src get's set to undefined?!?
1547         };
1548         
1549         cfg.html = this.html || cfg.html;
1550         
1551         cfg.src = this.src || cfg.src;
1552         
1553         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554             cfg.cls += ' img-' + this.border;
1555         }
1556         
1557         if(this.alt){
1558             cfg.alt = this.alt;
1559         }
1560         
1561         if(this.href){
1562             var a = {
1563                 tag: 'a',
1564                 href: this.href,
1565                 cn: [
1566                     cfg
1567                 ]
1568             };
1569             
1570             if(this.target){
1571                 a.target = this.target;
1572             }
1573             
1574         }
1575         
1576         return (this.href) ? a : cfg;
1577     },
1578     
1579     initEvents: function() 
1580     {
1581         if(!this.href){
1582             this.el.on('click', this.onClick, this);
1583         }
1584         
1585     },
1586     
1587     onClick : function(e)
1588     {
1589         Roo.log('img onclick');
1590         this.fireEvent('click', this, e);
1591     },
1592     /**
1593      * Sets the url of the image - used to update it
1594      * @param {String} url the url of the image
1595      */
1596     
1597     setSrc : function(url)
1598     {
1599         this.src =  url;
1600         
1601         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602             this.el.dom.src =  url;
1603             return;
1604         }
1605         
1606         this.el.select('img', true).first().dom.src =  url;
1607     }
1608     
1609     
1610    
1611 });
1612
1613  /*
1614  * - LGPL
1615  *
1616  * image
1617  * 
1618  */
1619
1620
1621 /**
1622  * @class Roo.bootstrap.Link
1623  * @extends Roo.bootstrap.Component
1624  * Bootstrap Link Class
1625  * @cfg {String} alt image alternative text
1626  * @cfg {String} href a tag href
1627  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628  * @cfg {String} html the content of the link.
1629  * @cfg {String} anchor name for the anchor link
1630  * @cfg {String} fa - favicon
1631
1632  * @cfg {Boolean} preventDefault (true | false) default false
1633
1634  * 
1635  * @constructor
1636  * Create a new Input
1637  * @param {Object} config The config object
1638  */
1639
1640 Roo.bootstrap.Link = function(config){
1641     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1642     
1643     this.addEvents({
1644         // img events
1645         /**
1646          * @event click
1647          * The img click event for the img.
1648          * @param {Roo.EventObject} e
1649          */
1650         "click" : true
1651     });
1652 };
1653
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1655     
1656     href: false,
1657     target: false,
1658     preventDefault: false,
1659     anchor : false,
1660     alt : false,
1661     fa: false,
1662
1663
1664     getAutoCreate : function()
1665     {
1666         var html = this.html || '';
1667         
1668         if (this.fa !== false) {
1669             html = '<i class="fa fa-' + this.fa + '"></i>';
1670         }
1671         var cfg = {
1672             tag: 'a'
1673         };
1674         // anchor's do not require html/href...
1675         if (this.anchor === false) {
1676             cfg.html = html;
1677             cfg.href = this.href || '#';
1678         } else {
1679             cfg.name = this.anchor;
1680             if (this.html !== false || this.fa !== false) {
1681                 cfg.html = html;
1682             }
1683             if (this.href !== false) {
1684                 cfg.href = this.href;
1685             }
1686         }
1687         
1688         if(this.alt !== false){
1689             cfg.alt = this.alt;
1690         }
1691         
1692         
1693         if(this.target !== false) {
1694             cfg.target = this.target;
1695         }
1696         
1697         return cfg;
1698     },
1699     
1700     initEvents: function() {
1701         
1702         if(!this.href || this.preventDefault){
1703             this.el.on('click', this.onClick, this);
1704         }
1705     },
1706     
1707     onClick : function(e)
1708     {
1709         if(this.preventDefault){
1710             e.preventDefault();
1711         }
1712         //Roo.log('img onclick');
1713         this.fireEvent('click', this, e);
1714     }
1715    
1716 });
1717
1718  /*
1719  * - LGPL
1720  *
1721  * header
1722  * 
1723  */
1724
1725 /**
1726  * @class Roo.bootstrap.Header
1727  * @extends Roo.bootstrap.Component
1728  * Bootstrap Header class
1729  * @cfg {String} html content of header
1730  * @cfg {Number} level (1|2|3|4|5|6) default 1
1731  * 
1732  * @constructor
1733  * Create a new Header
1734  * @param {Object} config The config object
1735  */
1736
1737
1738 Roo.bootstrap.Header  = function(config){
1739     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1740 };
1741
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1743     
1744     //href : false,
1745     html : false,
1746     level : 1,
1747     
1748     
1749     
1750     getAutoCreate : function(){
1751         
1752         
1753         
1754         var cfg = {
1755             tag: 'h' + (1 *this.level),
1756             html: this.html || ''
1757         } ;
1758         
1759         return cfg;
1760     }
1761    
1762 });
1763
1764  
1765
1766  /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776  
1777 /**
1778  * @class Roo.bootstrap.MenuMgr
1779  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1780  * @singleton
1781  */
1782 Roo.bootstrap.MenuMgr = function(){
1783    var menus, active, groups = {}, attached = false, lastShow = new Date();
1784
1785    // private - called when first menu is created
1786    function init(){
1787        menus = {};
1788        active = new Roo.util.MixedCollection();
1789        Roo.get(document).addKeyListener(27, function(){
1790            if(active.length > 0){
1791                hideAll();
1792            }
1793        });
1794    }
1795
1796    // private
1797    function hideAll(){
1798        if(active && active.length > 0){
1799            var c = active.clone();
1800            c.each(function(m){
1801                m.hide();
1802            });
1803        }
1804    }
1805
1806    // private
1807    function onHide(m){
1808        active.remove(m);
1809        if(active.length < 1){
1810            Roo.get(document).un("mouseup", onMouseDown);
1811             
1812            attached = false;
1813        }
1814    }
1815
1816    // private
1817    function onShow(m){
1818        var last = active.last();
1819        lastShow = new Date();
1820        active.add(m);
1821        if(!attached){
1822           Roo.get(document).on("mouseup", onMouseDown);
1823            
1824            attached = true;
1825        }
1826        if(m.parentMenu){
1827           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828           m.parentMenu.activeChild = m;
1829        }else if(last && last.isVisible()){
1830           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1831        }
1832    }
1833
1834    // private
1835    function onBeforeHide(m){
1836        if(m.activeChild){
1837            m.activeChild.hide();
1838        }
1839        if(m.autoHideTimer){
1840            clearTimeout(m.autoHideTimer);
1841            delete m.autoHideTimer;
1842        }
1843    }
1844
1845    // private
1846    function onBeforeShow(m){
1847        var pm = m.parentMenu;
1848        if(!pm && !m.allowOtherMenus){
1849            hideAll();
1850        }else if(pm && pm.activeChild && active != m){
1851            pm.activeChild.hide();
1852        }
1853    }
1854
1855    // private this should really trigger on mouseup..
1856    function onMouseDown(e){
1857         Roo.log("on Mouse Up");
1858         
1859         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860             Roo.log("MenuManager hideAll");
1861             hideAll();
1862             e.stopEvent();
1863         }
1864         
1865         
1866    }
1867
1868    // private
1869    function onBeforeCheck(mi, state){
1870        if(state){
1871            var g = groups[mi.group];
1872            for(var i = 0, l = g.length; i < l; i++){
1873                if(g[i] != mi){
1874                    g[i].setChecked(false);
1875                }
1876            }
1877        }
1878    }
1879
1880    return {
1881
1882        /**
1883         * Hides all menus that are currently visible
1884         */
1885        hideAll : function(){
1886             hideAll();  
1887        },
1888
1889        // private
1890        register : function(menu){
1891            if(!menus){
1892                init();
1893            }
1894            menus[menu.id] = menu;
1895            menu.on("beforehide", onBeforeHide);
1896            menu.on("hide", onHide);
1897            menu.on("beforeshow", onBeforeShow);
1898            menu.on("show", onShow);
1899            var g = menu.group;
1900            if(g && menu.events["checkchange"]){
1901                if(!groups[g]){
1902                    groups[g] = [];
1903                }
1904                groups[g].push(menu);
1905                menu.on("checkchange", onCheck);
1906            }
1907        },
1908
1909         /**
1910          * Returns a {@link Roo.menu.Menu} object
1911          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912          * be used to generate and return a new Menu instance.
1913          */
1914        get : function(menu){
1915            if(typeof menu == "string"){ // menu id
1916                return menus[menu];
1917            }else if(menu.events){  // menu instance
1918                return menu;
1919            }
1920            /*else if(typeof menu.length == 'number'){ // array of menu items?
1921                return new Roo.bootstrap.Menu({items:menu});
1922            }else{ // otherwise, must be a config
1923                return new Roo.bootstrap.Menu(menu);
1924            }
1925            */
1926            return false;
1927        },
1928
1929        // private
1930        unregister : function(menu){
1931            delete menus[menu.id];
1932            menu.un("beforehide", onBeforeHide);
1933            menu.un("hide", onHide);
1934            menu.un("beforeshow", onBeforeShow);
1935            menu.un("show", onShow);
1936            var g = menu.group;
1937            if(g && menu.events["checkchange"]){
1938                groups[g].remove(menu);
1939                menu.un("checkchange", onCheck);
1940            }
1941        },
1942
1943        // private
1944        registerCheckable : function(menuItem){
1945            var g = menuItem.group;
1946            if(g){
1947                if(!groups[g]){
1948                    groups[g] = [];
1949                }
1950                groups[g].push(menuItem);
1951                menuItem.on("beforecheckchange", onBeforeCheck);
1952            }
1953        },
1954
1955        // private
1956        unregisterCheckable : function(menuItem){
1957            var g = menuItem.group;
1958            if(g){
1959                groups[g].remove(menuItem);
1960                menuItem.un("beforecheckchange", onBeforeCheck);
1961            }
1962        }
1963    };
1964 }();/*
1965  * - LGPL
1966  *
1967  * menu
1968  * 
1969  */
1970
1971 /**
1972  * @class Roo.bootstrap.Menu
1973  * @extends Roo.bootstrap.Component
1974  * Bootstrap Menu class - container for MenuItems
1975  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1977  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1978  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1979  * 
1980  * @constructor
1981  * Create a new Menu
1982  * @param {Object} config The config object
1983  */
1984
1985
1986 Roo.bootstrap.Menu = function(config){
1987     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988     if (this.registerMenu && this.type != 'treeview')  {
1989         Roo.bootstrap.MenuMgr.register(this);
1990     }
1991     this.addEvents({
1992         /**
1993          * @event beforeshow
1994          * Fires before this menu is displayed
1995          * @param {Roo.menu.Menu} this
1996          */
1997         beforeshow : true,
1998         /**
1999          * @event beforehide
2000          * Fires before this menu is hidden
2001          * @param {Roo.menu.Menu} this
2002          */
2003         beforehide : true,
2004         /**
2005          * @event show
2006          * Fires after this menu is displayed
2007          * @param {Roo.menu.Menu} this
2008          */
2009         show : true,
2010         /**
2011          * @event hide
2012          * Fires after this menu is hidden
2013          * @param {Roo.menu.Menu} this
2014          */
2015         hide : true,
2016         /**
2017          * @event click
2018          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019          * @param {Roo.menu.Menu} this
2020          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021          * @param {Roo.EventObject} e
2022          */
2023         click : true,
2024         /**
2025          * @event mouseover
2026          * Fires when the mouse is hovering over this menu
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.EventObject} e
2029          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2030          */
2031         mouseover : true,
2032         /**
2033          * @event mouseout
2034          * Fires when the mouse exits 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         mouseout : true,
2040         /**
2041          * @event itemclick
2042          * Fires when a menu item contained in this menu is clicked
2043          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044          * @param {Roo.EventObject} e
2045          */
2046         itemclick: true
2047     });
2048     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2049 };
2050
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2052     
2053    /// html : false,
2054     //align : '',
2055     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2056     type: false,
2057     /**
2058      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2059      */
2060     registerMenu : true,
2061     
2062     menuItems :false, // stores the menu items..
2063     
2064     hidden:true,
2065         
2066     parentMenu : false,
2067     
2068     stopEvent : true,
2069     
2070     isLink : false,
2071     
2072     getChildContainer : function() {
2073         return this.el;  
2074     },
2075     
2076     getAutoCreate : function(){
2077          
2078         //if (['right'].indexOf(this.align)!==-1) {
2079         //    cfg.cn[1].cls += ' pull-right'
2080         //}
2081         
2082         
2083         var cfg = {
2084             tag : 'ul',
2085             cls : 'dropdown-menu' ,
2086             style : 'z-index:1000'
2087             
2088         };
2089         
2090         if (this.type === 'submenu') {
2091             cfg.cls = 'submenu active';
2092         }
2093         if (this.type === 'treeview') {
2094             cfg.cls = 'treeview-menu';
2095         }
2096         
2097         return cfg;
2098     },
2099     initEvents : function() {
2100         
2101        // Roo.log("ADD event");
2102        // Roo.log(this.triggerEl.dom);
2103         
2104         this.triggerEl.on('click', this.onTriggerClick, this);
2105         
2106         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2107         
2108         this.triggerEl.addClass('dropdown-toggle');
2109         
2110         if (Roo.isTouch) {
2111             this.el.on('touchstart'  , this.onTouch, this);
2112         }
2113         this.el.on('click' , this.onClick, this);
2114
2115         this.el.on("mouseover", this.onMouseOver, this);
2116         this.el.on("mouseout", this.onMouseOut, this);
2117         
2118     },
2119     
2120     findTargetItem : function(e)
2121     {
2122         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2123         if(!t){
2124             return false;
2125         }
2126         //Roo.log(t);         Roo.log(t.id);
2127         if(t && t.id){
2128             //Roo.log(this.menuitems);
2129             return this.menuitems.get(t.id);
2130             
2131             //return this.items.get(t.menuItemId);
2132         }
2133         
2134         return false;
2135     },
2136     
2137     onTouch : function(e) 
2138     {
2139         Roo.log("menu.onTouch");
2140         //e.stopEvent(); this make the user popdown broken
2141         this.onClick(e);
2142     },
2143     
2144     onClick : function(e)
2145     {
2146         Roo.log("menu.onClick");
2147         
2148         var t = this.findTargetItem(e);
2149         if(!t || t.isContainer){
2150             return;
2151         }
2152         Roo.log(e);
2153         /*
2154         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2155             if(t == this.activeItem && t.shouldDeactivate(e)){
2156                 this.activeItem.deactivate();
2157                 delete this.activeItem;
2158                 return;
2159             }
2160             if(t.canActivate){
2161                 this.setActiveItem(t, true);
2162             }
2163             return;
2164             
2165             
2166         }
2167         */
2168        
2169         Roo.log('pass click event');
2170         
2171         t.onClick(e);
2172         
2173         this.fireEvent("click", this, t, e);
2174         
2175         var _this = this;
2176         
2177         if(!t.href.length || t.href == '#'){
2178             (function() { _this.hide(); }).defer(100);
2179         }
2180         
2181     },
2182     
2183     onMouseOver : function(e){
2184         var t  = this.findTargetItem(e);
2185         //Roo.log(t);
2186         //if(t){
2187         //    if(t.canActivate && !t.disabled){
2188         //        this.setActiveItem(t, true);
2189         //    }
2190         //}
2191         
2192         this.fireEvent("mouseover", this, e, t);
2193     },
2194     isVisible : function(){
2195         return !this.hidden;
2196     },
2197      onMouseOut : function(e){
2198         var t  = this.findTargetItem(e);
2199         
2200         //if(t ){
2201         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2202         //        this.activeItem.deactivate();
2203         //        delete this.activeItem;
2204         //    }
2205         //}
2206         this.fireEvent("mouseout", this, e, t);
2207     },
2208     
2209     
2210     /**
2211      * Displays this menu relative to another element
2212      * @param {String/HTMLElement/Roo.Element} element The element to align to
2213      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214      * the element (defaults to this.defaultAlign)
2215      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2216      */
2217     show : function(el, pos, parentMenu){
2218         this.parentMenu = parentMenu;
2219         if(!this.el){
2220             this.render();
2221         }
2222         this.fireEvent("beforeshow", this);
2223         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2224     },
2225      /**
2226      * Displays this menu at a specific xy position
2227      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2229      */
2230     showAt : function(xy, parentMenu, /* private: */_e){
2231         this.parentMenu = parentMenu;
2232         if(!this.el){
2233             this.render();
2234         }
2235         if(_e !== false){
2236             this.fireEvent("beforeshow", this);
2237             //xy = this.el.adjustForConstraints(xy);
2238         }
2239         
2240         //this.el.show();
2241         this.hideMenuItems();
2242         this.hidden = false;
2243         this.triggerEl.addClass('open');
2244         
2245         // reassign x when hitting right
2246         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2248         }
2249         
2250         // reassign y when hitting bottom
2251         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2253         }
2254         
2255         // but the list may align on trigger left or trigger top... should it be a properity?
2256         
2257         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2258             this.el.setXY(xy);
2259         }
2260         
2261         this.focus();
2262         this.fireEvent("show", this);
2263     },
2264     
2265     focus : function(){
2266         return;
2267         if(!this.hidden){
2268             this.doFocus.defer(50, this);
2269         }
2270     },
2271
2272     doFocus : function(){
2273         if(!this.hidden){
2274             this.focusEl.focus();
2275         }
2276     },
2277
2278     /**
2279      * Hides this menu and optionally all parent menus
2280      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2281      */
2282     hide : function(deep)
2283     {
2284         
2285         this.hideMenuItems();
2286         if(this.el && this.isVisible()){
2287             this.fireEvent("beforehide", this);
2288             if(this.activeItem){
2289                 this.activeItem.deactivate();
2290                 this.activeItem = null;
2291             }
2292             this.triggerEl.removeClass('open');;
2293             this.hidden = true;
2294             this.fireEvent("hide", this);
2295         }
2296         if(deep === true && this.parentMenu){
2297             this.parentMenu.hide(true);
2298         }
2299     },
2300     
2301     onTriggerClick : function(e)
2302     {
2303         Roo.log('trigger click');
2304         
2305         var target = e.getTarget();
2306         
2307         Roo.log(target.nodeName.toLowerCase());
2308         
2309         if(target.nodeName.toLowerCase() === 'i'){
2310             e.preventDefault();
2311         }
2312         
2313     },
2314     
2315     onTriggerPress  : function(e)
2316     {
2317         Roo.log('trigger press');
2318         //Roo.log(e.getTarget());
2319        // Roo.log(this.triggerEl.dom);
2320        
2321         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322         var pel = Roo.get(e.getTarget());
2323         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324             Roo.log('is treeview or dropdown?');
2325             return;
2326         }
2327         
2328         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2329             return;
2330         }
2331         
2332         if (this.isVisible()) {
2333             Roo.log('hide');
2334             this.hide();
2335         } else {
2336             Roo.log('show');
2337             this.show(this.triggerEl, false, false);
2338         }
2339         
2340         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2341             e.stopEvent();
2342         }
2343         
2344     },
2345        
2346     
2347     hideMenuItems : function()
2348     {
2349         Roo.log("hide Menu Items");
2350         if (!this.el) { 
2351             return;
2352         }
2353         //$(backdrop).remove()
2354         this.el.select('.open',true).each(function(aa) {
2355             
2356             aa.removeClass('open');
2357           //var parent = getParent($(this))
2358           //var relatedTarget = { relatedTarget: this }
2359           
2360            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361           //if (e.isDefaultPrevented()) return
2362            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2363         });
2364     },
2365     addxtypeChild : function (tree, cntr) {
2366         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2367           
2368         this.menuitems.add(comp);
2369         return comp;
2370
2371     },
2372     getEl : function()
2373     {
2374         Roo.log(this.el);
2375         return this.el;
2376     },
2377     
2378     clear : function()
2379     {
2380         this.getEl().dom.innerHTML = '';
2381         this.menuitems.clear();
2382     }
2383 });
2384
2385  
2386  /*
2387  * - LGPL
2388  *
2389  * menu item
2390  * 
2391  */
2392
2393
2394 /**
2395  * @class Roo.bootstrap.MenuItem
2396  * @extends Roo.bootstrap.Component
2397  * Bootstrap MenuItem class
2398  * @cfg {String} html the menu label
2399  * @cfg {String} href the link
2400  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2403  * @cfg {String} fa favicon to show on left of menu item.
2404  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2405  * 
2406  * 
2407  * @constructor
2408  * Create a new MenuItem
2409  * @param {Object} config The config object
2410  */
2411
2412
2413 Roo.bootstrap.MenuItem = function(config){
2414     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2415     this.addEvents({
2416         // raw events
2417         /**
2418          * @event click
2419          * The raw click event for the entire grid.
2420          * @param {Roo.bootstrap.MenuItem} this
2421          * @param {Roo.EventObject} e
2422          */
2423         "click" : true
2424     });
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2428     
2429     href : false,
2430     html : false,
2431     preventDefault: false,
2432     isContainer : false,
2433     active : false,
2434     fa: false,
2435     
2436     getAutoCreate : function(){
2437         
2438         if(this.isContainer){
2439             return {
2440                 tag: 'li',
2441                 cls: 'dropdown-menu-item'
2442             };
2443         }
2444         var ctag = {
2445             tag: 'span',
2446             html: 'Link'
2447         };
2448         
2449         var anc = {
2450             tag : 'a',
2451             href : '#',
2452             cn : [  ]
2453         };
2454         
2455         if (this.fa !== false) {
2456             anc.cn.push({
2457                 tag : 'i',
2458                 cls : 'fa fa-' + this.fa
2459             });
2460         }
2461         
2462         anc.cn.push(ctag);
2463         
2464         
2465         var cfg= {
2466             tag: 'li',
2467             cls: 'dropdown-menu-item',
2468             cn: [ anc ]
2469         };
2470         if (this.parent().type == 'treeview') {
2471             cfg.cls = 'treeview-menu';
2472         }
2473         if (this.active) {
2474             cfg.cls += ' active';
2475         }
2476         
2477         
2478         
2479         anc.href = this.href || cfg.cn[0].href ;
2480         ctag.html = this.html || cfg.cn[0].html ;
2481         return cfg;
2482     },
2483     
2484     initEvents: function()
2485     {
2486         if (this.parent().type == 'treeview') {
2487             this.el.select('a').on('click', this.onClick, this);
2488         }
2489         
2490         if (this.menu) {
2491             this.menu.parentType = this.xtype;
2492             this.menu.triggerEl = this.el;
2493             this.menu = this.addxtype(Roo.apply({}, this.menu));
2494         }
2495         
2496     },
2497     onClick : function(e)
2498     {
2499         Roo.log('item on click ');
2500         
2501         if(this.preventDefault){
2502             e.preventDefault();
2503         }
2504         //this.parent().hideMenuItems();
2505         
2506         this.fireEvent('click', this, e);
2507     },
2508     getEl : function()
2509     {
2510         return this.el;
2511     } 
2512 });
2513
2514  
2515
2516  /*
2517  * - LGPL
2518  *
2519  * menu separator
2520  * 
2521  */
2522
2523
2524 /**
2525  * @class Roo.bootstrap.MenuSeparator
2526  * @extends Roo.bootstrap.Component
2527  * Bootstrap MenuSeparator class
2528  * 
2529  * @constructor
2530  * Create a new MenuItem
2531  * @param {Object} config The config object
2532  */
2533
2534
2535 Roo.bootstrap.MenuSeparator = function(config){
2536     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2537 };
2538
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2540     
2541     getAutoCreate : function(){
2542         var cfg = {
2543             cls: 'divider',
2544             tag : 'li'
2545         };
2546         
2547         return cfg;
2548     }
2549    
2550 });
2551
2552  
2553
2554  
2555 /*
2556 * Licence: LGPL
2557 */
2558
2559 /**
2560  * @class Roo.bootstrap.Modal
2561  * @extends Roo.bootstrap.Component
2562  * Bootstrap Modal class
2563  * @cfg {String} title Title of dialog
2564  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2566  * @cfg {Boolean} specificTitle default false
2567  * @cfg {Array} buttons Array of buttons or standard button set..
2568  * @cfg {String} buttonPosition (left|right|center) default right
2569  * @cfg {Boolean} animate default true
2570  * @cfg {Boolean} allow_close default true
2571  * @cfg {Boolean} fitwindow default false
2572  * @cfg {String} size (sm|lg) default empty
2573  * @cfg {Number} max_width set the max width of modal
2574  *
2575  *
2576  * @constructor
2577  * Create a new Modal Dialog
2578  * @param {Object} config The config object
2579  */
2580
2581 Roo.bootstrap.Modal = function(config){
2582     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2583     this.addEvents({
2584         // raw events
2585         /**
2586          * @event btnclick
2587          * The raw btnclick event for the button
2588          * @param {Roo.EventObject} e
2589          */
2590         "btnclick" : true,
2591         /**
2592          * @event resize
2593          * Fire when dialog resize
2594          * @param {Roo.bootstrap.Modal} this
2595          * @param {Roo.EventObject} e
2596          */
2597         "resize" : true
2598     });
2599     this.buttons = this.buttons || [];
2600
2601     if (this.tmpl) {
2602         this.tmpl = Roo.factory(this.tmpl);
2603     }
2604
2605 };
2606
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2608
2609     title : 'test dialog',
2610
2611     buttons : false,
2612
2613     // set on load...
2614
2615     html: false,
2616
2617     tmp: false,
2618
2619     specificTitle: false,
2620
2621     buttonPosition: 'right',
2622
2623     allow_close : true,
2624
2625     animate : true,
2626
2627     fitwindow: false,
2628     
2629      // private
2630     dialogEl: false,
2631     bodyEl:  false,
2632     footerEl:  false,
2633     titleEl:  false,
2634     closeEl:  false,
2635
2636     size: '',
2637     
2638     max_width: 0,
2639     
2640     max_height: 0,
2641     
2642     fit_content: false,
2643
2644     onRender : function(ct, position)
2645     {
2646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2647
2648         if(!this.el){
2649             var cfg = Roo.apply({},  this.getAutoCreate());
2650             cfg.id = Roo.id();
2651             //if(!cfg.name){
2652             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653             //}
2654             //if (!cfg.name.length) {
2655             //    delete cfg.name;
2656            // }
2657             if (this.cls) {
2658                 cfg.cls += ' ' + this.cls;
2659             }
2660             if (this.style) {
2661                 cfg.style = this.style;
2662             }
2663             this.el = Roo.get(document.body).createChild(cfg, position);
2664         }
2665         //var type = this.el.dom.type;
2666
2667
2668         if(this.tabIndex !== undefined){
2669             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2670         }
2671
2672         this.dialogEl = this.el.select('.modal-dialog',true).first();
2673         this.bodyEl = this.el.select('.modal-body',true).first();
2674         this.closeEl = this.el.select('.modal-header .close', true).first();
2675         this.headerEl = this.el.select('.modal-header',true).first();
2676         this.titleEl = this.el.select('.modal-title',true).first();
2677         this.footerEl = this.el.select('.modal-footer',true).first();
2678
2679         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680         
2681         //this.el.addClass("x-dlg-modal");
2682
2683         if (this.buttons.length) {
2684             Roo.each(this.buttons, function(bb) {
2685                 var b = Roo.apply({}, bb);
2686                 b.xns = b.xns || Roo.bootstrap;
2687                 b.xtype = b.xtype || 'Button';
2688                 if (typeof(b.listeners) == 'undefined') {
2689                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2690                 }
2691
2692                 var btn = Roo.factory(b);
2693
2694                 btn.render(this.el.select('.modal-footer div').first());
2695
2696             },this);
2697         }
2698         // render the children.
2699         var nitems = [];
2700
2701         if(typeof(this.items) != 'undefined'){
2702             var items = this.items;
2703             delete this.items;
2704
2705             for(var i =0;i < items.length;i++) {
2706                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2707             }
2708         }
2709
2710         this.items = nitems;
2711
2712         // where are these used - they used to be body/close/footer
2713
2714
2715         this.initEvents();
2716         //this.el.addClass([this.fieldClass, this.cls]);
2717
2718     },
2719
2720     getAutoCreate : function()
2721     {
2722         var bdy = {
2723                 cls : 'modal-body',
2724                 html : this.html || ''
2725         };
2726
2727         var title = {
2728             tag: 'h4',
2729             cls : 'modal-title',
2730             html : this.title
2731         };
2732
2733         if(this.specificTitle){
2734             title = this.title;
2735
2736         };
2737
2738         var header = [];
2739         if (this.allow_close) {
2740             header.push({
2741                 tag: 'button',
2742                 cls : 'close',
2743                 html : '&times'
2744             });
2745         }
2746
2747         header.push(title);
2748
2749         var size = '';
2750
2751         if(this.size.length){
2752             size = 'modal-' + this.size;
2753         }
2754
2755         var modal = {
2756             cls: "modal",
2757              cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(
2819             Roo.lib.Dom.getViewWidth(true),
2820             Roo.lib.Dom.getViewHeight(true)
2821         );
2822         
2823         if (this.fitwindow) {
2824             this.setSize(
2825                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2827             );
2828             return;
2829         }
2830         
2831         if(this.max_width !== 0) {
2832             
2833             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834             
2835             if(this.height) {
2836                 this.setSize(w, this.height);
2837                 return;
2838             }
2839             
2840             if(this.max_height) {
2841                 this.setSize(w,Math.min(
2842                     this.max_height,
2843                     Roo.lib.Dom.getViewportHeight(true) - 60
2844                 ));
2845                 
2846                 return;
2847             }
2848             
2849             if(!this.fit_content) {
2850                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2851                 return;
2852             }
2853             
2854             this.setSize(w, Math.min(
2855                 60 +
2856                 this.headerEl.getHeight() + 
2857                 this.footerEl.getHeight() + 
2858                 this.getChildHeight(this.bodyEl.dom.childNodes),
2859                 Roo.lib.Dom.getViewportHeight(true) - 60)
2860             );
2861         }
2862         
2863     },
2864
2865     setSize : function(w,h)
2866     {
2867         if (!w && !h) {
2868             return;
2869         }
2870         
2871         this.resizeTo(w,h);
2872     },
2873
2874     show : function() {
2875
2876         if (!this.rendered) {
2877             this.render();
2878         }
2879
2880         //this.el.setStyle('display', 'block');
2881         this.el.removeClass('hideing');        
2882         this.el.addClass('show');
2883  
2884         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2885             var _this = this;
2886             (function(){
2887                 this.el.addClass('in');
2888             }).defer(50, this);
2889         }else{
2890             this.el.addClass('in');
2891         }
2892
2893         // not sure how we can show data in here..
2894         //if (this.tmpl) {
2895         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2896         //}
2897
2898         Roo.get(document.body).addClass("x-body-masked");
2899         
2900         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2901         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902         this.maskEl.addClass('show');
2903         
2904         this.resize();
2905         
2906         this.fireEvent('show', this);
2907
2908         // set zindex here - otherwise it appears to be ignored...
2909         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910
2911         (function () {
2912             this.items.forEach( function(e) {
2913                 e.layout ? e.layout() : false;
2914
2915             });
2916         }).defer(100,this);
2917
2918     },
2919     hide : function()
2920     {
2921         if(this.fireEvent("beforehide", this) !== false){
2922             this.maskEl.removeClass('show');
2923             Roo.get(document.body).removeClass("x-body-masked");
2924             this.el.removeClass('in');
2925             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2926
2927             if(this.animate){ // why
2928                 this.el.addClass('hideing');
2929                 (function(){
2930                     if (!this.el.hasClass('hideing')) {
2931                         return; // it's been shown again...
2932                     }
2933                     this.el.removeClass('show');
2934                     this.el.removeClass('hideing');
2935                 }).defer(150,this);
2936                 
2937             }else{
2938                  this.el.removeClass('show');
2939             }
2940             this.fireEvent('hide', this);
2941         }
2942     },
2943     isVisible : function()
2944     {
2945         
2946         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2947         
2948     },
2949
2950     addButton : function(str, cb)
2951     {
2952
2953
2954         var b = Roo.apply({}, { html : str } );
2955         b.xns = b.xns || Roo.bootstrap;
2956         b.xtype = b.xtype || 'Button';
2957         if (typeof(b.listeners) == 'undefined') {
2958             b.listeners = { click : cb.createDelegate(this)  };
2959         }
2960
2961         var btn = Roo.factory(b);
2962
2963         btn.render(this.el.select('.modal-footer div').first());
2964
2965         return btn;
2966
2967     },
2968
2969     setDefaultButton : function(btn)
2970     {
2971         //this.el.select('.modal-footer').()
2972     },
2973     diff : false,
2974
2975     resizeTo: function(w,h)
2976     {
2977         // skip.. ?? why??
2978
2979         this.dialogEl.setWidth(w);
2980         if (this.diff === false) {
2981             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2982         }
2983
2984         this.bodyEl.setHeight(h - this.diff);
2985
2986         this.fireEvent('resize', this);
2987
2988     },
2989     setContentSize  : function(w, h)
2990     {
2991
2992     },
2993     onButtonClick: function(btn,e)
2994     {
2995         //Roo.log([a,b,c]);
2996         this.fireEvent('btnclick', btn.name, e);
2997     },
2998      /**
2999      * Set the title of the Dialog
3000      * @param {String} str new Title
3001      */
3002     setTitle: function(str) {
3003         this.titleEl.dom.innerHTML = str;
3004     },
3005     /**
3006      * Set the body of the Dialog
3007      * @param {String} str new Title
3008      */
3009     setBody: function(str) {
3010         this.bodyEl.dom.innerHTML = str;
3011     },
3012     /**
3013      * Set the body of the Dialog using the template
3014      * @param {Obj} data - apply this data to the template and replace the body contents.
3015      */
3016     applyBody: function(obj)
3017     {
3018         if (!this.tmpl) {
3019             Roo.log("Error - using apply Body without a template");
3020             //code
3021         }
3022         this.tmpl.overwrite(this.bodyEl, obj);
3023     },
3024     
3025     getChildHeight : function(child_nodes)
3026     {
3027         if(
3028             !child_nodes ||
3029             child_nodes.length == 0
3030         ) {
3031             return;
3032         }
3033         
3034         var child_height = 0;
3035         
3036         for(var i = 0; i < child_nodes.length; i++) {
3037             
3038             /*
3039             * for modal with tabs...
3040             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3041                 
3042                 var layout_childs = child_nodes[i].childNodes;
3043                 
3044                 for(var j = 0; j < layout_childs.length; j++) {
3045                     
3046                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3047                         
3048                         var layout_body_childs = layout_childs[j].childNodes;
3049                         
3050                         for(var k = 0; k < layout_body_childs.length; k++) {
3051                             
3052                             if(layout_body_childs[k].classList.contains('navbar')) {
3053                                 child_height += layout_body_childs[k].offsetHeight;
3054                                 continue;
3055                             }
3056                             
3057                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3058                                 
3059                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3060                                 
3061                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3062                                     
3063                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3065                                         continue;
3066                                     }
3067                                     
3068                                 }
3069                                 
3070                             }
3071                             
3072                         }
3073                     }
3074                 }
3075                 continue;
3076             }
3077             */
3078             
3079             child_height += child_nodes[i].offsetHeight;
3080             // Roo.log(child_nodes[i].offsetHeight);
3081         }
3082         
3083         return child_height;
3084     }
3085
3086 });
3087
3088
3089 Roo.apply(Roo.bootstrap.Modal,  {
3090     /**
3091          * Button config that displays a single OK button
3092          * @type Object
3093          */
3094         OK :  [{
3095             name : 'ok',
3096             weight : 'primary',
3097             html : 'OK'
3098         }],
3099         /**
3100          * Button config that displays Yes and No buttons
3101          * @type Object
3102          */
3103         YESNO : [
3104             {
3105                 name  : 'no',
3106                 html : 'No'
3107             },
3108             {
3109                 name  :'yes',
3110                 weight : 'primary',
3111                 html : 'Yes'
3112             }
3113         ],
3114
3115         /**
3116          * Button config that displays OK and Cancel buttons
3117          * @type Object
3118          */
3119         OKCANCEL : [
3120             {
3121                name : 'cancel',
3122                 html : 'Cancel'
3123             },
3124             {
3125                 name : 'ok',
3126                 weight : 'primary',
3127                 html : 'OK'
3128             }
3129         ],
3130         /**
3131          * Button config that displays Yes, No and Cancel buttons
3132          * @type Object
3133          */
3134         YESNOCANCEL : [
3135             {
3136                 name : 'yes',
3137                 weight : 'primary',
3138                 html : 'Yes'
3139             },
3140             {
3141                 name : 'no',
3142                 html : 'No'
3143             },
3144             {
3145                 name : 'cancel',
3146                 html : 'Cancel'
3147             }
3148         ],
3149         
3150         zIndex : 10001
3151 });
3152 /*
3153  * - LGPL
3154  *
3155  * messagebox - can be used as a replace
3156  * 
3157  */
3158 /**
3159  * @class Roo.MessageBox
3160  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3161  * Example usage:
3162  *<pre><code>
3163 // Basic alert:
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3165
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3168     if (btn == 'ok'){
3169         // process text value...
3170     }
3171 });
3172
3173 // Show a dialog using config options:
3174 Roo.Msg.show({
3175    title:'Save Changes?',
3176    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177    buttons: Roo.Msg.YESNOCANCEL,
3178    fn: processResult,
3179    animEl: 'elId'
3180 });
3181 </code></pre>
3182  * @singleton
3183  */
3184 Roo.bootstrap.MessageBox = function(){
3185     var dlg, opt, mask, waitTimer;
3186     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187     var buttons, activeTextEl, bwidth;
3188
3189     
3190     // private
3191     var handleButton = function(button){
3192         dlg.hide();
3193         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3194     };
3195
3196     // private
3197     var handleHide = function(){
3198         if(opt && opt.cls){
3199             dlg.el.removeClass(opt.cls);
3200         }
3201         //if(waitTimer){
3202         //    Roo.TaskMgr.stop(waitTimer);
3203         //    waitTimer = null;
3204         //}
3205     };
3206
3207     // private
3208     var updateButtons = function(b){
3209         var width = 0;
3210         if(!b){
3211             buttons["ok"].hide();
3212             buttons["cancel"].hide();
3213             buttons["yes"].hide();
3214             buttons["no"].hide();
3215             //dlg.footer.dom.style.display = 'none';
3216             return width;
3217         }
3218         dlg.footerEl.dom.style.display = '';
3219         for(var k in buttons){
3220             if(typeof buttons[k] != "function"){
3221                 if(b[k]){
3222                     buttons[k].show();
3223                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224                     width += buttons[k].el.getWidth()+15;
3225                 }else{
3226                     buttons[k].hide();
3227                 }
3228             }
3229         }
3230         return width;
3231     };
3232
3233     // private
3234     var handleEsc = function(d, k, e){
3235         if(opt && opt.closable !== false){
3236             dlg.hide();
3237         }
3238         if(e){
3239             e.stopEvent();
3240         }
3241     };
3242
3243     return {
3244         /**
3245          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246          * @return {Roo.BasicDialog} The BasicDialog element
3247          */
3248         getDialog : function(){
3249            if(!dlg){
3250                 dlg = new Roo.bootstrap.Modal( {
3251                     //draggable: true,
3252                     //resizable:false,
3253                     //constraintoviewport:false,
3254                     //fixedcenter:true,
3255                     //collapsible : false,
3256                     //shim:true,
3257                     //modal: true,
3258                 //    width: 'auto',
3259                   //  height:100,
3260                     //buttonAlign:"center",
3261                     closeClick : function(){
3262                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3263                             handleButton("no");
3264                         }else{
3265                             handleButton("cancel");
3266                         }
3267                     }
3268                 });
3269                 dlg.render();
3270                 dlg.on("hide", handleHide);
3271                 mask = dlg.mask;
3272                 //dlg.addKeyListener(27, handleEsc);
3273                 buttons = {};
3274                 this.buttons = buttons;
3275                 var bt = this.buttonText;
3276                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3280                 //Roo.log(buttons);
3281                 bodyEl = dlg.bodyEl.createChild({
3282
3283                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284                         '<textarea class="roo-mb-textarea"></textarea>' +
3285                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3286                 });
3287                 msgEl = bodyEl.dom.firstChild;
3288                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289                 textboxEl.enableDisplayMode();
3290                 textboxEl.addKeyListener([10,13], function(){
3291                     if(dlg.isVisible() && opt && opt.buttons){
3292                         if(opt.buttons.ok){
3293                             handleButton("ok");
3294                         }else if(opt.buttons.yes){
3295                             handleButton("yes");
3296                         }
3297                     }
3298                 });
3299                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300                 textareaEl.enableDisplayMode();
3301                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302                 progressEl.enableDisplayMode();
3303                 
3304                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305                 var pf = progressEl.dom.firstChild;
3306                 if (pf) {
3307                     pp = Roo.get(pf.firstChild);
3308                     pp.setHeight(pf.offsetHeight);
3309                 }
3310                 
3311             }
3312             return dlg;
3313         },
3314
3315         /**
3316          * Updates the message box body text
3317          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318          * the XHTML-compliant non-breaking space character '&amp;#160;')
3319          * @return {Roo.MessageBox} This message box
3320          */
3321         updateText : function(text)
3322         {
3323             if(!dlg.isVisible() && !opt.width){
3324                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3326             }
3327             msgEl.innerHTML = text || '&#160;';
3328       
3329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3331             var w = Math.max(
3332                     Math.min(opt.width || cw , this.maxWidth), 
3333                     Math.max(opt.minWidth || this.minWidth, bwidth)
3334             );
3335             if(opt.prompt){
3336                 activeTextEl.setWidth(w);
3337             }
3338             if(dlg.isVisible()){
3339                 dlg.fixedcenter = false;
3340             }
3341             // to big, make it scroll. = But as usual stupid IE does not support
3342             // !important..
3343             
3344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3347             } else {
3348                 bodyEl.dom.style.height = '';
3349                 bodyEl.dom.style.overflowY = '';
3350             }
3351             if (cw > w) {
3352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3353             } else {
3354                 bodyEl.dom.style.overflowX = '';
3355             }
3356             
3357             dlg.setContentSize(w, bodyEl.getHeight());
3358             if(dlg.isVisible()){
3359                 dlg.fixedcenter = true;
3360             }
3361             return this;
3362         },
3363
3364         /**
3365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369          * @return {Roo.MessageBox} This message box
3370          */
3371         updateProgress : function(value, text){
3372             if(text){
3373                 this.updateText(text);
3374             }
3375             
3376             if (pp) { // weird bug on my firefox - for some reason this is not defined
3377                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3379             }
3380             return this;
3381         },        
3382
3383         /**
3384          * Returns true if the message box is currently displayed
3385          * @return {Boolean} True if the message box is visible, else false
3386          */
3387         isVisible : function(){
3388             return dlg && dlg.isVisible();  
3389         },
3390
3391         /**
3392          * Hides the message box if it is displayed
3393          */
3394         hide : function(){
3395             if(this.isVisible()){
3396                 dlg.hide();
3397             }  
3398         },
3399
3400         /**
3401          * Displays a new message box, or reinitializes an existing message box, based on the config options
3402          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403          * The following config object properties are supported:
3404          * <pre>
3405 Property    Type             Description
3406 ----------  ---------------  ------------------------------------------------------------------------------------
3407 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3408                                    closes (defaults to undefined)
3409 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3412                                    progress and wait dialogs will ignore this property and always hide the
3413                                    close button as they can only be closed programmatically.
3414 cls               String           A custom CSS class to apply to the message box element
3415 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3416                                    displayed (defaults to 75)
3417 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3418                                    function will be btn (the name of the button that was clicked, if applicable,
3419                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3420                                    Progress and wait dialogs will ignore this option since they do not respond to
3421                                    user actions and can only be closed programmatically, so any required function
3422                                    should be called by the same code after it closes the dialog.
3423 icon              String           A CSS class that provides a background image to be used as an icon for
3424                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3426 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3427 modal             Boolean          False to allow user interaction with the page while the message box is
3428                                    displayed (defaults to true)
3429 msg               String           A string that will replace the existing message box body text (defaults
3430                                    to the XHTML-compliant non-breaking space character '&#160;')
3431 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3432 progress          Boolean          True to display a progress bar (defaults to false)
3433 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3436 title             String           The title text
3437 value             String           The string value to set into the active textbox element if displayed
3438 wait              Boolean          True to display a progress bar (defaults to false)
3439 width             Number           The width of the dialog in pixels
3440 </pre>
3441          *
3442          * Example usage:
3443          * <pre><code>
3444 Roo.Msg.show({
3445    title: 'Address',
3446    msg: 'Please enter your address:',
3447    width: 300,
3448    buttons: Roo.MessageBox.OKCANCEL,
3449    multiline: true,
3450    fn: saveAddress,
3451    animEl: 'addAddressBtn'
3452 });
3453 </code></pre>
3454          * @param {Object} config Configuration options
3455          * @return {Roo.MessageBox} This message box
3456          */
3457         show : function(options)
3458         {
3459             
3460             // this causes nightmares if you show one dialog after another
3461             // especially on callbacks..
3462              
3463             if(this.isVisible()){
3464                 
3465                 this.hide();
3466                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3468                 Roo.log("New Dialog Message:" +  options.msg )
3469                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3471                 
3472             }
3473             var d = this.getDialog();
3474             opt = options;
3475             d.setTitle(opt.title || "&#160;");
3476             d.closeEl.setDisplayed(opt.closable !== false);
3477             activeTextEl = textboxEl;
3478             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3479             if(opt.prompt){
3480                 if(opt.multiline){
3481                     textboxEl.hide();
3482                     textareaEl.show();
3483                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3484                         opt.multiline : this.defaultTextHeight);
3485                     activeTextEl = textareaEl;
3486                 }else{
3487                     textboxEl.show();
3488                     textareaEl.hide();
3489                 }
3490             }else{
3491                 textboxEl.hide();
3492                 textareaEl.hide();
3493             }
3494             progressEl.setDisplayed(opt.progress === true);
3495             this.updateProgress(0);
3496             activeTextEl.dom.value = opt.value || "";
3497             if(opt.prompt){
3498                 dlg.setDefaultButton(activeTextEl);
3499             }else{
3500                 var bs = opt.buttons;
3501                 var db = null;
3502                 if(bs && bs.ok){
3503                     db = buttons["ok"];
3504                 }else if(bs && bs.yes){
3505                     db = buttons["yes"];
3506                 }
3507                 dlg.setDefaultButton(db);
3508             }
3509             bwidth = updateButtons(opt.buttons);
3510             this.updateText(opt.msg);
3511             if(opt.cls){
3512                 d.el.addClass(opt.cls);
3513             }
3514             d.proxyDrag = opt.proxyDrag === true;
3515             d.modal = opt.modal !== false;
3516             d.mask = opt.modal !== false ? mask : false;
3517             if(!d.isVisible()){
3518                 // force it to the end of the z-index stack so it gets a cursor in FF
3519                 document.body.appendChild(dlg.el.dom);
3520                 d.animateTarget = null;
3521                 d.show(options.animEl);
3522             }
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3528          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529          * and closing the message box when the process is complete.
3530          * @param {String} title The title bar text
3531          * @param {String} msg The message box body text
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         progress : function(title, msg){
3535             this.show({
3536                 title : title,
3537                 msg : msg,
3538                 buttons: false,
3539                 progress:true,
3540                 closable:false,
3541                 minWidth: this.minProgressWidth,
3542                 modal : true
3543             });
3544             return this;
3545         },
3546
3547         /**
3548          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549          * If a callback function is passed it will be called after the user clicks the button, and the
3550          * id of the button that was clicked will be passed as the only parameter to the callback
3551          * (could also be the top-right close button).
3552          * @param {String} title The title bar text
3553          * @param {String} msg The message box body text
3554          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555          * @param {Object} scope (optional) The scope of the callback function
3556          * @return {Roo.MessageBox} This message box
3557          */
3558         alert : function(title, msg, fn, scope)
3559         {
3560             this.show({
3561                 title : title,
3562                 msg : msg,
3563                 buttons: this.OK,
3564                 fn: fn,
3565                 closable : false,
3566                 scope : scope,
3567                 modal : true
3568             });
3569             return this;
3570         },
3571
3572         /**
3573          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3574          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575          * You are responsible for closing the message box when the process is complete.
3576          * @param {String} msg The message box body text
3577          * @param {String} title (optional) The title bar text
3578          * @return {Roo.MessageBox} This message box
3579          */
3580         wait : function(msg, title){
3581             this.show({
3582                 title : title,
3583                 msg : msg,
3584                 buttons: false,
3585                 closable:false,
3586                 progress:true,
3587                 modal:true,
3588                 width:300,
3589                 wait:true
3590             });
3591             waitTimer = Roo.TaskMgr.start({
3592                 run: function(i){
3593                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3594                 },
3595                 interval: 1000
3596             });
3597             return this;
3598         },
3599
3600         /**
3601          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604          * @param {String} title The title bar text
3605          * @param {String} msg The message box body text
3606          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607          * @param {Object} scope (optional) The scope of the callback function
3608          * @return {Roo.MessageBox} This message box
3609          */
3610         confirm : function(title, msg, fn, scope){
3611             this.show({
3612                 title : title,
3613                 msg : msg,
3614                 buttons: this.YESNO,
3615                 fn: fn,
3616                 scope : scope,
3617                 modal : true
3618             });
3619             return this;
3620         },
3621
3622         /**
3623          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3625          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626          * (could also be the top-right close button) and the text that was entered will be passed as the two
3627          * parameters to the callback.
3628          * @param {String} title The title bar text
3629          * @param {String} msg The message box body text
3630          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631          * @param {Object} scope (optional) The scope of the callback function
3632          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634          * @return {Roo.MessageBox} This message box
3635          */
3636         prompt : function(title, msg, fn, scope, multiline){
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OKCANCEL,
3641                 fn: fn,
3642                 minWidth:250,
3643                 scope : scope,
3644                 prompt:true,
3645                 multiline: multiline,
3646                 modal : true
3647             });
3648             return this;
3649         },
3650
3651         /**
3652          * Button config that displays a single OK button
3653          * @type Object
3654          */
3655         OK : {ok:true},
3656         /**
3657          * Button config that displays Yes and No buttons
3658          * @type Object
3659          */
3660         YESNO : {yes:true, no:true},
3661         /**
3662          * Button config that displays OK and Cancel buttons
3663          * @type Object
3664          */
3665         OKCANCEL : {ok:true, cancel:true},
3666         /**
3667          * Button config that displays Yes, No and Cancel buttons
3668          * @type Object
3669          */
3670         YESNOCANCEL : {yes:true, no:true, cancel:true},
3671
3672         /**
3673          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3674          * @type Number
3675          */
3676         defaultTextHeight : 75,
3677         /**
3678          * The maximum width in pixels of the message box (defaults to 600)
3679          * @type Number
3680          */
3681         maxWidth : 600,
3682         /**
3683          * The minimum width in pixels of the message box (defaults to 100)
3684          * @type Number
3685          */
3686         minWidth : 100,
3687         /**
3688          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3689          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3690          * @type Number
3691          */
3692         minProgressWidth : 250,
3693         /**
3694          * An object containing the default button text strings that can be overriden for localized language support.
3695          * Supported properties are: ok, cancel, yes and no.
3696          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3697          * @type Object
3698          */
3699         buttonText : {
3700             ok : "OK",
3701             cancel : "Cancel",
3702             yes : "Yes",
3703             no : "No"
3704         }
3705     };
3706 }();
3707
3708 /**
3709  * Shorthand for {@link Roo.MessageBox}
3710  */
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3713 /*
3714  * - LGPL
3715  *
3716  * navbar
3717  * 
3718  */
3719
3720 /**
3721  * @class Roo.bootstrap.Navbar
3722  * @extends Roo.bootstrap.Component
3723  * Bootstrap Navbar class
3724
3725  * @constructor
3726  * Create a new Navbar
3727  * @param {Object} config The config object
3728  */
3729
3730
3731 Roo.bootstrap.Navbar = function(config){
3732     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3733     this.addEvents({
3734         // raw events
3735         /**
3736          * @event beforetoggle
3737          * Fire before toggle the menu
3738          * @param {Roo.EventObject} e
3739          */
3740         "beforetoggle" : true
3741     });
3742 };
3743
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3745     
3746     
3747    
3748     // private
3749     navItems : false,
3750     loadMask : false,
3751     
3752     
3753     getAutoCreate : function(){
3754         
3755         
3756         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3757         
3758     },
3759     
3760     initEvents :function ()
3761     {
3762         //Roo.log(this.el.select('.navbar-toggle',true));
3763         this.el.select('.navbar-toggle',true).on('click', function() {
3764             if(this.fireEvent('beforetoggle', this) !== false){
3765                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3766             }
3767             
3768         }, this);
3769         
3770         var mark = {
3771             tag: "div",
3772             cls:"x-dlg-mask"
3773         };
3774         
3775         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3776         
3777         var size = this.el.getSize();
3778         this.maskEl.setSize(size.width, size.height);
3779         this.maskEl.enableDisplayMode("block");
3780         this.maskEl.hide();
3781         
3782         if(this.loadMask){
3783             this.maskEl.show();
3784         }
3785     },
3786     
3787     
3788     getChildContainer : function()
3789     {
3790         if (this.el.select('.collapse').getCount()) {
3791             return this.el.select('.collapse',true).first();
3792         }
3793         
3794         return this.el;
3795     },
3796     
3797     mask : function()
3798     {
3799         this.maskEl.show();
3800     },
3801     
3802     unmask : function()
3803     {
3804         this.maskEl.hide();
3805     } 
3806     
3807     
3808     
3809     
3810 });
3811
3812
3813
3814  
3815
3816  /*
3817  * - LGPL
3818  *
3819  * navbar
3820  * 
3821  */
3822
3823 /**
3824  * @class Roo.bootstrap.NavSimplebar
3825  * @extends Roo.bootstrap.Navbar
3826  * Bootstrap Sidebar class
3827  *
3828  * @cfg {Boolean} inverse is inverted color
3829  * 
3830  * @cfg {String} type (nav | pills | tabs)
3831  * @cfg {Boolean} arrangement stacked | justified
3832  * @cfg {String} align (left | right) alignment
3833  * 
3834  * @cfg {Boolean} main (true|false) main nav bar? default false
3835  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3836  * 
3837  * @cfg {String} tag (header|footer|nav|div) default is nav 
3838
3839  * 
3840  * 
3841  * 
3842  * @constructor
3843  * Create a new Sidebar
3844  * @param {Object} config The config object
3845  */
3846
3847
3848 Roo.bootstrap.NavSimplebar = function(config){
3849     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3853     
3854     inverse: false,
3855     
3856     type: false,
3857     arrangement: '',
3858     align : false,
3859     
3860     
3861     
3862     main : false,
3863     
3864     
3865     tag : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         var cfg = {
3872             tag : this.tag || 'div',
3873             cls : 'navbar'
3874         };
3875           
3876         
3877         cfg.cn = [
3878             {
3879                 cls: 'nav',
3880                 tag : 'ul'
3881             }
3882         ];
3883         
3884          
3885         this.type = this.type || 'nav';
3886         if (['tabs','pills'].indexOf(this.type)!==-1) {
3887             cfg.cn[0].cls += ' nav-' + this.type
3888         
3889         
3890         } else {
3891             if (this.type!=='nav') {
3892                 Roo.log('nav type must be nav/tabs/pills')
3893             }
3894             cfg.cn[0].cls += ' navbar-nav'
3895         }
3896         
3897         
3898         
3899         
3900         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901             cfg.cn[0].cls += ' nav-' + this.arrangement;
3902         }
3903         
3904         
3905         if (this.align === 'right') {
3906             cfg.cn[0].cls += ' navbar-right';
3907         }
3908         
3909         if (this.inverse) {
3910             cfg.cls += ' navbar-inverse';
3911             
3912         }
3913         
3914         
3915         return cfg;
3916     
3917         
3918     }
3919     
3920     
3921     
3922 });
3923
3924
3925
3926  
3927
3928  
3929        /*
3930  * - LGPL
3931  *
3932  * navbar
3933  * 
3934  */
3935
3936 /**
3937  * @class Roo.bootstrap.NavHeaderbar
3938  * @extends Roo.bootstrap.NavSimplebar
3939  * Bootstrap Sidebar class
3940  *
3941  * @cfg {String} brand what is brand
3942  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943  * @cfg {String} brand_href href of the brand
3944  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3945  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3948  * 
3949  * @constructor
3950  * Create a new Sidebar
3951  * @param {Object} config The config object
3952  */
3953
3954
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3957       
3958 };
3959
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3961     
3962     position: '',
3963     brand: '',
3964     brand_href: false,
3965     srButton : true,
3966     autohide : false,
3967     desktopCenter : false,
3968    
3969     
3970     getAutoCreate : function(){
3971         
3972         var   cfg = {
3973             tag: this.nav || 'nav',
3974             cls: 'navbar',
3975             role: 'navigation',
3976             cn: []
3977         };
3978         
3979         var cn = cfg.cn;
3980         if (this.desktopCenter) {
3981             cn.push({cls : 'container', cn : []});
3982             cn = cn[0].cn;
3983         }
3984         
3985         if(this.srButton){
3986             cn.push({
3987                 tag: 'div',
3988                 cls: 'navbar-header',
3989                 cn: [
3990                     {
3991                         tag: 'button',
3992                         type: 'button',
3993                         cls: 'navbar-toggle',
3994                         'data-toggle': 'collapse',
3995                         cn: [
3996                             {
3997                                 tag: 'span',
3998                                 cls: 'sr-only',
3999                                 html: 'Toggle navigation'
4000                             },
4001                             {
4002                                 tag: 'span',
4003                                 cls: 'icon-bar'
4004                             },
4005                             {
4006                                 tag: 'span',
4007                                 cls: 'icon-bar'
4008                             },
4009                             {
4010                                 tag: 'span',
4011                                 cls: 'icon-bar'
4012                             }
4013                         ]
4014                     }
4015                 ]
4016             });
4017         }
4018         
4019         cn.push({
4020             tag: 'div',
4021             cls: 'collapse navbar-collapse',
4022             cn : []
4023         });
4024         
4025         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4026         
4027         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028             cfg.cls += ' navbar-' + this.position;
4029             
4030             // tag can override this..
4031             
4032             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4033         }
4034         
4035         if (this.brand !== '') {
4036             cn[0].cn.push({
4037                 tag: 'a',
4038                 href: this.brand_href ? this.brand_href : '#',
4039                 cls: 'navbar-brand',
4040                 cn: [
4041                 this.brand
4042                 ]
4043             });
4044         }
4045         
4046         if(this.main){
4047             cfg.cls += ' main-nav';
4048         }
4049         
4050         
4051         return cfg;
4052
4053         
4054     },
4055     getHeaderChildContainer : function()
4056     {
4057         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058             return this.el.select('.navbar-header',true).first();
4059         }
4060         
4061         return this.getChildContainer();
4062     },
4063     
4064     
4065     initEvents : function()
4066     {
4067         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4068         
4069         if (this.autohide) {
4070             
4071             var prevScroll = 0;
4072             var ft = this.el;
4073             
4074             Roo.get(document).on('scroll',function(e) {
4075                 var ns = Roo.get(document).getScroll().top;
4076                 var os = prevScroll;
4077                 prevScroll = ns;
4078                 
4079                 if(ns > os){
4080                     ft.removeClass('slideDown');
4081                     ft.addClass('slideUp');
4082                     return;
4083                 }
4084                 ft.removeClass('slideUp');
4085                 ft.addClass('slideDown');
4086                  
4087               
4088           },this);
4089         }
4090     }    
4091     
4092 });
4093
4094
4095
4096  
4097
4098  /*
4099  * - LGPL
4100  *
4101  * navbar
4102  * 
4103  */
4104
4105 /**
4106  * @class Roo.bootstrap.NavSidebar
4107  * @extends Roo.bootstrap.Navbar
4108  * Bootstrap Sidebar class
4109  * 
4110  * @constructor
4111  * Create a new Sidebar
4112  * @param {Object} config The config object
4113  */
4114
4115
4116 Roo.bootstrap.NavSidebar = function(config){
4117     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4118 };
4119
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4121     
4122     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4123     
4124     getAutoCreate : function(){
4125         
4126         
4127         return  {
4128             tag: 'div',
4129             cls: 'sidebar sidebar-nav'
4130         };
4131     
4132         
4133     }
4134     
4135     
4136     
4137 });
4138
4139
4140
4141  
4142
4143  /*
4144  * - LGPL
4145  *
4146  * nav group
4147  * 
4148  */
4149
4150 /**
4151  * @class Roo.bootstrap.NavGroup
4152  * @extends Roo.bootstrap.Component
4153  * Bootstrap NavGroup class
4154  * @cfg {String} align (left|right)
4155  * @cfg {Boolean} inverse
4156  * @cfg {String} type (nav|pills|tab) default nav
4157  * @cfg {String} navId - reference Id for navbar.
4158
4159  * 
4160  * @constructor
4161  * Create a new nav group
4162  * @param {Object} config The config object
4163  */
4164
4165 Roo.bootstrap.NavGroup = function(config){
4166     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4167     this.navItems = [];
4168    
4169     Roo.bootstrap.NavGroup.register(this);
4170      this.addEvents({
4171         /**
4172              * @event changed
4173              * Fires when the active item changes
4174              * @param {Roo.bootstrap.NavGroup} this
4175              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4177          */
4178         'changed': true
4179      });
4180     
4181 };
4182
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4184     
4185     align: '',
4186     inverse: false,
4187     form: false,
4188     type: 'nav',
4189     navId : '',
4190     // private
4191     
4192     navItems : false, 
4193     
4194     getAutoCreate : function()
4195     {
4196         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4197         
4198         cfg = {
4199             tag : 'ul',
4200             cls: 'nav' 
4201         };
4202         
4203         if (['tabs','pills'].indexOf(this.type)!==-1) {
4204             cfg.cls += ' nav-' + this.type
4205         } else {
4206             if (this.type!=='nav') {
4207                 Roo.log('nav type must be nav/tabs/pills')
4208             }
4209             cfg.cls += ' navbar-nav'
4210         }
4211         
4212         if (this.parent() && this.parent().sidebar) {
4213             cfg = {
4214                 tag: 'ul',
4215                 cls: 'dashboard-menu sidebar-menu'
4216             };
4217             
4218             return cfg;
4219         }
4220         
4221         if (this.form === true) {
4222             cfg = {
4223                 tag: 'form',
4224                 cls: 'navbar-form'
4225             };
4226             
4227             if (this.align === 'right') {
4228                 cfg.cls += ' navbar-right';
4229             } else {
4230                 cfg.cls += ' navbar-left';
4231             }
4232         }
4233         
4234         if (this.align === 'right') {
4235             cfg.cls += ' navbar-right';
4236         }
4237         
4238         if (this.inverse) {
4239             cfg.cls += ' navbar-inverse';
4240             
4241         }
4242         
4243         
4244         return cfg;
4245     },
4246     /**
4247     * sets the active Navigation item
4248     * @param {Roo.bootstrap.NavItem} the new current navitem
4249     */
4250     setActiveItem : function(item)
4251     {
4252         var prev = false;
4253         Roo.each(this.navItems, function(v){
4254             if (v == item) {
4255                 return ;
4256             }
4257             if (v.isActive()) {
4258                 v.setActive(false, true);
4259                 prev = v;
4260                 
4261             }
4262             
4263         });
4264
4265         item.setActive(true, true);
4266         this.fireEvent('changed', this, item, prev);
4267         
4268         
4269     },
4270     /**
4271     * gets the active Navigation item
4272     * @return {Roo.bootstrap.NavItem} the current navitem
4273     */
4274     getActive : function()
4275     {
4276         
4277         var prev = false;
4278         Roo.each(this.navItems, function(v){
4279             
4280             if (v.isActive()) {
4281                 prev = v;
4282                 
4283             }
4284             
4285         });
4286         return prev;
4287     },
4288     
4289     indexOfNav : function()
4290     {
4291         
4292         var prev = false;
4293         Roo.each(this.navItems, function(v,i){
4294             
4295             if (v.isActive()) {
4296                 prev = i;
4297                 
4298             }
4299             
4300         });
4301         return prev;
4302     },
4303     /**
4304     * adds a Navigation item
4305     * @param {Roo.bootstrap.NavItem} the navitem to add
4306     */
4307     addItem : function(cfg)
4308     {
4309         var cn = new Roo.bootstrap.NavItem(cfg);
4310         this.register(cn);
4311         cn.parentId = this.id;
4312         cn.onRender(this.el, null);
4313         return cn;
4314     },
4315     /**
4316     * register a Navigation item
4317     * @param {Roo.bootstrap.NavItem} the navitem to add
4318     */
4319     register : function(item)
4320     {
4321         this.navItems.push( item);
4322         item.navId = this.navId;
4323     
4324     },
4325     
4326     /**
4327     * clear all the Navigation item
4328     */
4329    
4330     clearAll : function()
4331     {
4332         this.navItems = [];
4333         this.el.dom.innerHTML = '';
4334     },
4335     
4336     getNavItem: function(tabId)
4337     {
4338         var ret = false;
4339         Roo.each(this.navItems, function(e) {
4340             if (e.tabId == tabId) {
4341                ret =  e;
4342                return false;
4343             }
4344             return true;
4345             
4346         });
4347         return ret;
4348     },
4349     
4350     setActiveNext : function()
4351     {
4352         var i = this.indexOfNav(this.getActive());
4353         if (i > this.navItems.length) {
4354             return;
4355         }
4356         this.setActiveItem(this.navItems[i+1]);
4357     },
4358     setActivePrev : function()
4359     {
4360         var i = this.indexOfNav(this.getActive());
4361         if (i  < 1) {
4362             return;
4363         }
4364         this.setActiveItem(this.navItems[i-1]);
4365     },
4366     clearWasActive : function(except) {
4367         Roo.each(this.navItems, function(e) {
4368             if (e.tabId != except.tabId && e.was_active) {
4369                e.was_active = false;
4370                return false;
4371             }
4372             return true;
4373             
4374         });
4375     },
4376     getWasActive : function ()
4377     {
4378         var r = false;
4379         Roo.each(this.navItems, function(e) {
4380             if (e.was_active) {
4381                r = e;
4382                return false;
4383             }
4384             return true;
4385             
4386         });
4387         return r;
4388     }
4389     
4390     
4391 });
4392
4393  
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4395     
4396     groups: {},
4397      /**
4398     * register a Navigation Group
4399     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4400     */
4401     register : function(navgrp)
4402     {
4403         this.groups[navgrp.navId] = navgrp;
4404         
4405     },
4406     /**
4407     * fetch a Navigation Group based on the navigation ID
4408     * @param {string} the navgroup to add
4409     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4410     */
4411     get: function(navId) {
4412         if (typeof(this.groups[navId]) == 'undefined') {
4413             return false;
4414             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4415         }
4416         return this.groups[navId] ;
4417     }
4418     
4419     
4420     
4421 });
4422
4423  /*
4424  * - LGPL
4425  *
4426  * row
4427  * 
4428  */
4429
4430 /**
4431  * @class Roo.bootstrap.NavItem
4432  * @extends Roo.bootstrap.Component
4433  * Bootstrap Navbar.NavItem class
4434  * @cfg {String} href  link to
4435  * @cfg {String} html content of button
4436  * @cfg {String} badge text inside badge
4437  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438  * @cfg {String} glyphicon name of glyphicon
4439  * @cfg {String} icon name of font awesome icon
4440  * @cfg {Boolean} active Is item active
4441  * @cfg {Boolean} disabled Is item disabled
4442  
4443  * @cfg {Boolean} preventDefault (true | false) default false
4444  * @cfg {String} tabId the tab that this item activates.
4445  * @cfg {String} tagtype (a|span) render as a href or span?
4446  * @cfg {Boolean} animateRef (true|false) link to element default false  
4447   
4448  * @constructor
4449  * Create a new Navbar Item
4450  * @param {Object} config The config object
4451  */
4452 Roo.bootstrap.NavItem = function(config){
4453     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4454     this.addEvents({
4455         // raw events
4456         /**
4457          * @event click
4458          * The raw click event for the entire grid.
4459          * @param {Roo.EventObject} e
4460          */
4461         "click" : true,
4462          /**
4463             * @event changed
4464             * Fires when the active item active state changes
4465             * @param {Roo.bootstrap.NavItem} this
4466             * @param {boolean} state the new state
4467              
4468          */
4469         'changed': true,
4470         /**
4471             * @event scrollto
4472             * Fires when scroll to element
4473             * @param {Roo.bootstrap.NavItem} this
4474             * @param {Object} options
4475             * @param {Roo.EventObject} e
4476              
4477          */
4478         'scrollto': true
4479     });
4480    
4481 };
4482
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4484     
4485     href: false,
4486     html: '',
4487     badge: '',
4488     icon: false,
4489     glyphicon: false,
4490     active: false,
4491     preventDefault : false,
4492     tabId : false,
4493     tagtype : 'a',
4494     disabled : false,
4495     animateRef : false,
4496     was_active : false,
4497     
4498     getAutoCreate : function(){
4499          
4500         var cfg = {
4501             tag: 'li',
4502             cls: 'nav-item'
4503             
4504         };
4505         
4506         if (this.active) {
4507             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4508         }
4509         if (this.disabled) {
4510             cfg.cls += ' disabled';
4511         }
4512         
4513         if (this.href || this.html || this.glyphicon || this.icon) {
4514             cfg.cn = [
4515                 {
4516                     tag: this.tagtype,
4517                     href : this.href || "#",
4518                     html: this.html || ''
4519                 }
4520             ];
4521             
4522             if (this.icon) {
4523                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4524             }
4525
4526             if(this.glyphicon) {
4527                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4528             }
4529             
4530             if (this.menu) {
4531                 
4532                 cfg.cn[0].html += " <span class='caret'></span>";
4533              
4534             }
4535             
4536             if (this.badge !== '') {
4537                  
4538                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4539             }
4540         }
4541         
4542         
4543         
4544         return cfg;
4545     },
4546     initEvents: function() 
4547     {
4548         if (typeof (this.menu) != 'undefined') {
4549             this.menu.parentType = this.xtype;
4550             this.menu.triggerEl = this.el;
4551             this.menu = this.addxtype(Roo.apply({}, this.menu));
4552         }
4553         
4554         this.el.select('a',true).on('click', this.onClick, this);
4555         
4556         if(this.tagtype == 'span'){
4557             this.el.select('span',true).on('click', this.onClick, this);
4558         }
4559        
4560         // at this point parent should be available..
4561         this.parent().register(this);
4562     },
4563     
4564     onClick : function(e)
4565     {
4566         if (e.getTarget('.dropdown-menu-item')) {
4567             // did you click on a menu itemm.... - then don't trigger onclick..
4568             return;
4569         }
4570         
4571         if(
4572                 this.preventDefault || 
4573                 this.href == '#' 
4574         ){
4575             Roo.log("NavItem - prevent Default?");
4576             e.preventDefault();
4577         }
4578         
4579         if (this.disabled) {
4580             return;
4581         }
4582         
4583         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584         if (tg && tg.transition) {
4585             Roo.log("waiting for the transitionend");
4586             return;
4587         }
4588         
4589         
4590         
4591         //Roo.log("fire event clicked");
4592         if(this.fireEvent('click', this, e) === false){
4593             return;
4594         };
4595         
4596         if(this.tagtype == 'span'){
4597             return;
4598         }
4599         
4600         //Roo.log(this.href);
4601         var ael = this.el.select('a',true).first();
4602         //Roo.log(ael);
4603         
4604         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607                 return; // ignore... - it's a 'hash' to another page.
4608             }
4609             Roo.log("NavItem - prevent Default?");
4610             e.preventDefault();
4611             this.scrollToElement(e);
4612         }
4613         
4614         
4615         var p =  this.parent();
4616    
4617         if (['tabs','pills'].indexOf(p.type)!==-1) {
4618             if (typeof(p.setActiveItem) !== 'undefined') {
4619                 p.setActiveItem(this);
4620             }
4621         }
4622         
4623         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625             // remove the collapsed menu expand...
4626             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4627         }
4628     },
4629     
4630     isActive: function () {
4631         return this.active
4632     },
4633     setActive : function(state, fire, is_was_active)
4634     {
4635         if (this.active && !state && this.navId) {
4636             this.was_active = true;
4637             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4638             if (nv) {
4639                 nv.clearWasActive(this);
4640             }
4641             
4642         }
4643         this.active = state;
4644         
4645         if (!state ) {
4646             this.el.removeClass('active');
4647         } else if (!this.el.hasClass('active')) {
4648             this.el.addClass('active');
4649         }
4650         if (fire) {
4651             this.fireEvent('changed', this, state);
4652         }
4653         
4654         // show a panel if it's registered and related..
4655         
4656         if (!this.navId || !this.tabId || !state || is_was_active) {
4657             return;
4658         }
4659         
4660         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4661         if (!tg) {
4662             return;
4663         }
4664         var pan = tg.getPanelByName(this.tabId);
4665         if (!pan) {
4666             return;
4667         }
4668         // if we can not flip to new panel - go back to old nav highlight..
4669         if (false == tg.showPanel(pan)) {
4670             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4671             if (nv) {
4672                 var onav = nv.getWasActive();
4673                 if (onav) {
4674                     onav.setActive(true, false, true);
4675                 }
4676             }
4677             
4678         }
4679         
4680         
4681         
4682     },
4683      // this should not be here...
4684     setDisabled : function(state)
4685     {
4686         this.disabled = state;
4687         if (!state ) {
4688             this.el.removeClass('disabled');
4689         } else if (!this.el.hasClass('disabled')) {
4690             this.el.addClass('disabled');
4691         }
4692         
4693     },
4694     
4695     /**
4696      * Fetch the element to display the tooltip on.
4697      * @return {Roo.Element} defaults to this.el
4698      */
4699     tooltipEl : function()
4700     {
4701         return this.el.select('' + this.tagtype + '', true).first();
4702     },
4703     
4704     scrollToElement : function(e)
4705     {
4706         var c = document.body;
4707         
4708         /*
4709          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4710          */
4711         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712             c = document.documentElement;
4713         }
4714         
4715         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4716         
4717         if(!target){
4718             return;
4719         }
4720
4721         var o = target.calcOffsetsTo(c);
4722         
4723         var options = {
4724             target : target,
4725             value : o[1]
4726         };
4727         
4728         this.fireEvent('scrollto', this, options, e);
4729         
4730         Roo.get(c).scrollTo('top', options.value, true);
4731         
4732         return;
4733     }
4734 });
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * sidebar item
4741  *
4742  *  li
4743  *    <span> icon </span>
4744  *    <span> text </span>
4745  *    <span>badge </span>
4746  */
4747
4748 /**
4749  * @class Roo.bootstrap.NavSidebarItem
4750  * @extends Roo.bootstrap.NavItem
4751  * Bootstrap Navbar.NavSidebarItem class
4752  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753  * {Boolean} open is the menu open
4754  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756  * {String} buttonSize (sm|md|lg)the extra classes for the button
4757  * {Boolean} showArrow show arrow next to the text (default true)
4758  * @constructor
4759  * Create a new Navbar Button
4760  * @param {Object} config The config object
4761  */
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4764     this.addEvents({
4765         // raw events
4766         /**
4767          * @event click
4768          * The raw click event for the entire grid.
4769          * @param {Roo.EventObject} e
4770          */
4771         "click" : true,
4772          /**
4773             * @event changed
4774             * Fires when the active item active state changes
4775             * @param {Roo.bootstrap.NavSidebarItem} this
4776             * @param {boolean} state the new state
4777              
4778          */
4779         'changed': true
4780     });
4781    
4782 };
4783
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4785     
4786     badgeWeight : 'default',
4787     
4788     open: false,
4789     
4790     buttonView : false,
4791     
4792     buttonWeight : 'default',
4793     
4794     buttonSize : 'md',
4795     
4796     showArrow : true,
4797     
4798     getAutoCreate : function(){
4799         
4800         
4801         var a = {
4802                 tag: 'a',
4803                 href : this.href || '#',
4804                 cls: '',
4805                 html : '',
4806                 cn : []
4807         };
4808         
4809         if(this.buttonView){
4810             a = {
4811                 tag: 'button',
4812                 href : this.href || '#',
4813                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4814                 html : this.html,
4815                 cn : []
4816             };
4817         }
4818         
4819         var cfg = {
4820             tag: 'li',
4821             cls: '',
4822             cn: [ a ]
4823         };
4824         
4825         if (this.active) {
4826             cfg.cls += ' active';
4827         }
4828         
4829         if (this.disabled) {
4830             cfg.cls += ' disabled';
4831         }
4832         if (this.open) {
4833             cfg.cls += ' open x-open';
4834         }
4835         // left icon..
4836         if (this.glyphicon || this.icon) {
4837             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4838             a.cn.push({ tag : 'i', cls : c }) ;
4839         }
4840         
4841         if(!this.buttonView){
4842             var span = {
4843                 tag: 'span',
4844                 html : this.html || ''
4845             };
4846
4847             a.cn.push(span);
4848             
4849         }
4850         
4851         if (this.badge !== '') {
4852             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4853         }
4854         
4855         if (this.menu) {
4856             
4857             if(this.showArrow){
4858                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4859             }
4860             
4861             a.cls += ' dropdown-toggle treeview' ;
4862         }
4863         
4864         return cfg;
4865     },
4866     
4867     initEvents : function()
4868     { 
4869         if (typeof (this.menu) != 'undefined') {
4870             this.menu.parentType = this.xtype;
4871             this.menu.triggerEl = this.el;
4872             this.menu = this.addxtype(Roo.apply({}, this.menu));
4873         }
4874         
4875         this.el.on('click', this.onClick, this);
4876         
4877         if(this.badge !== ''){
4878             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4879         }
4880         
4881     },
4882     
4883     onClick : function(e)
4884     {
4885         if(this.disabled){
4886             e.preventDefault();
4887             return;
4888         }
4889         
4890         if(this.preventDefault){
4891             e.preventDefault();
4892         }
4893         
4894         this.fireEvent('click', this);
4895     },
4896     
4897     disable : function()
4898     {
4899         this.setDisabled(true);
4900     },
4901     
4902     enable : function()
4903     {
4904         this.setDisabled(false);
4905     },
4906     
4907     setDisabled : function(state)
4908     {
4909         if(this.disabled == state){
4910             return;
4911         }
4912         
4913         this.disabled = state;
4914         
4915         if (state) {
4916             this.el.addClass('disabled');
4917             return;
4918         }
4919         
4920         this.el.removeClass('disabled');
4921         
4922         return;
4923     },
4924     
4925     setActive : function(state)
4926     {
4927         if(this.active == state){
4928             return;
4929         }
4930         
4931         this.active = state;
4932         
4933         if (state) {
4934             this.el.addClass('active');
4935             return;
4936         }
4937         
4938         this.el.removeClass('active');
4939         
4940         return;
4941     },
4942     
4943     isActive: function () 
4944     {
4945         return this.active;
4946     },
4947     
4948     setBadge : function(str)
4949     {
4950         if(!this.badgeEl){
4951             return;
4952         }
4953         
4954         this.badgeEl.dom.innerHTML = str;
4955     }
4956     
4957    
4958      
4959  
4960 });
4961  
4962
4963  /*
4964  * - LGPL
4965  *
4966  * row
4967  * 
4968  */
4969
4970 /**
4971  * @class Roo.bootstrap.Row
4972  * @extends Roo.bootstrap.Component
4973  * Bootstrap Row class (contains columns...)
4974  * 
4975  * @constructor
4976  * Create a new Row
4977  * @param {Object} config The config object
4978  */
4979
4980 Roo.bootstrap.Row = function(config){
4981     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4982 };
4983
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4985     
4986     getAutoCreate : function(){
4987        return {
4988             cls: 'row clearfix'
4989        };
4990     }
4991     
4992     
4993 });
4994
4995  
4996
4997  /*
4998  * - LGPL
4999  *
5000  * element
5001  * 
5002  */
5003
5004 /**
5005  * @class Roo.bootstrap.Element
5006  * @extends Roo.bootstrap.Component
5007  * Bootstrap Element class
5008  * @cfg {String} html contents of the element
5009  * @cfg {String} tag tag of the element
5010  * @cfg {String} cls class of the element
5011  * @cfg {Boolean} preventDefault (true|false) default false
5012  * @cfg {Boolean} clickable (true|false) default false
5013  * 
5014  * @constructor
5015  * Create a new Element
5016  * @param {Object} config The config object
5017  */
5018
5019 Roo.bootstrap.Element = function(config){
5020     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5021     
5022     this.addEvents({
5023         // raw events
5024         /**
5025          * @event click
5026          * When a element is chick
5027          * @param {Roo.bootstrap.Element} this
5028          * @param {Roo.EventObject} e
5029          */
5030         "click" : true
5031     });
5032 };
5033
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5035     
5036     tag: 'div',
5037     cls: '',
5038     html: '',
5039     preventDefault: false, 
5040     clickable: false,
5041     
5042     getAutoCreate : function(){
5043         
5044         var cfg = {
5045             tag: this.tag,
5046             // cls: this.cls, double assign in parent class Component.js :: onRender
5047             html: this.html
5048         };
5049         
5050         return cfg;
5051     },
5052     
5053     initEvents: function() 
5054     {
5055         Roo.bootstrap.Element.superclass.initEvents.call(this);
5056         
5057         if(this.clickable){
5058             this.el.on('click', this.onClick, this);
5059         }
5060         
5061     },
5062     
5063     onClick : function(e)
5064     {
5065         if(this.preventDefault){
5066             e.preventDefault();
5067         }
5068         
5069         this.fireEvent('click', this, e);
5070     },
5071     
5072     getValue : function()
5073     {
5074         return this.el.dom.innerHTML;
5075     },
5076     
5077     setValue : function(value)
5078     {
5079         this.el.dom.innerHTML = value;
5080     }
5081    
5082 });
5083
5084  
5085
5086  /*
5087  * - LGPL
5088  *
5089  * pagination
5090  * 
5091  */
5092
5093 /**
5094  * @class Roo.bootstrap.Pagination
5095  * @extends Roo.bootstrap.Component
5096  * Bootstrap Pagination class
5097  * @cfg {String} size xs | sm | md | lg
5098  * @cfg {Boolean} inverse false | true
5099  * 
5100  * @constructor
5101  * Create a new Pagination
5102  * @param {Object} config The config object
5103  */
5104
5105 Roo.bootstrap.Pagination = function(config){
5106     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5107 };
5108
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5110     
5111     cls: false,
5112     size: false,
5113     inverse: false,
5114     
5115     getAutoCreate : function(){
5116         var cfg = {
5117             tag: 'ul',
5118                 cls: 'pagination'
5119         };
5120         if (this.inverse) {
5121             cfg.cls += ' inverse';
5122         }
5123         if (this.html) {
5124             cfg.html=this.html;
5125         }
5126         if (this.cls) {
5127             cfg.cls += " " + this.cls;
5128         }
5129         return cfg;
5130     }
5131    
5132 });
5133
5134  
5135
5136  /*
5137  * - LGPL
5138  *
5139  * Pagination item
5140  * 
5141  */
5142
5143
5144 /**
5145  * @class Roo.bootstrap.PaginationItem
5146  * @extends Roo.bootstrap.Component
5147  * Bootstrap PaginationItem class
5148  * @cfg {String} html text
5149  * @cfg {String} href the link
5150  * @cfg {Boolean} preventDefault (true | false) default true
5151  * @cfg {Boolean} active (true | false) default false
5152  * @cfg {Boolean} disabled default false
5153  * 
5154  * 
5155  * @constructor
5156  * Create a new PaginationItem
5157  * @param {Object} config The config object
5158  */
5159
5160
5161 Roo.bootstrap.PaginationItem = function(config){
5162     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5163     this.addEvents({
5164         // raw events
5165         /**
5166          * @event click
5167          * The raw click event for the entire grid.
5168          * @param {Roo.EventObject} e
5169          */
5170         "click" : true
5171     });
5172 };
5173
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5175     
5176     href : false,
5177     html : false,
5178     preventDefault: true,
5179     active : false,
5180     cls : false,
5181     disabled: false,
5182     
5183     getAutoCreate : function(){
5184         var cfg= {
5185             tag: 'li',
5186             cn: [
5187                 {
5188                     tag : 'a',
5189                     href : this.href ? this.href : '#',
5190                     html : this.html ? this.html : ''
5191                 }
5192             ]
5193         };
5194         
5195         if(this.cls){
5196             cfg.cls = this.cls;
5197         }
5198         
5199         if(this.disabled){
5200             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5201         }
5202         
5203         if(this.active){
5204             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5205         }
5206         
5207         return cfg;
5208     },
5209     
5210     initEvents: function() {
5211         
5212         this.el.on('click', this.onClick, this);
5213         
5214     },
5215     onClick : function(e)
5216     {
5217         Roo.log('PaginationItem on click ');
5218         if(this.preventDefault){
5219             e.preventDefault();
5220         }
5221         
5222         if(this.disabled){
5223             return;
5224         }
5225         
5226         this.fireEvent('click', this, e);
5227     }
5228    
5229 });
5230
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * slider
5237  * 
5238  */
5239
5240
5241 /**
5242  * @class Roo.bootstrap.Slider
5243  * @extends Roo.bootstrap.Component
5244  * Bootstrap Slider class
5245  *    
5246  * @constructor
5247  * Create a new Slider
5248  * @param {Object} config The config object
5249  */
5250
5251 Roo.bootstrap.Slider = function(config){
5252     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5253 };
5254
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5256     
5257     getAutoCreate : function(){
5258         
5259         var cfg = {
5260             tag: 'div',
5261             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5262             cn: [
5263                 {
5264                     tag: 'a',
5265                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5266                 }
5267             ]
5268         };
5269         
5270         return cfg;
5271     }
5272    
5273 });
5274
5275  /*
5276  * Based on:
5277  * Ext JS Library 1.1.1
5278  * Copyright(c) 2006-2007, Ext JS, LLC.
5279  *
5280  * Originally Released Under LGPL - original licence link has changed is not relivant.
5281  *
5282  * Fork - LGPL
5283  * <script type="text/javascript">
5284  */
5285  
5286
5287 /**
5288  * @class Roo.grid.ColumnModel
5289  * @extends Roo.util.Observable
5290  * This is the default implementation of a ColumnModel used by the Grid. It defines
5291  * the columns in the grid.
5292  * <br>Usage:<br>
5293  <pre><code>
5294  var colModel = new Roo.grid.ColumnModel([
5295         {header: "Ticker", width: 60, sortable: true, locked: true},
5296         {header: "Company Name", width: 150, sortable: true},
5297         {header: "Market Cap.", width: 100, sortable: true},
5298         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299         {header: "Employees", width: 100, sortable: true, resizable: false}
5300  ]);
5301  </code></pre>
5302  * <p>
5303  
5304  * The config options listed for this class are options which may appear in each
5305  * individual column definition.
5306  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5307  * @constructor
5308  * @param {Object} config An Array of column config objects. See this class's
5309  * config objects for details.
5310 */
5311 Roo.grid.ColumnModel = function(config){
5312         /**
5313      * The config passed into the constructor
5314      */
5315     this.config = config;
5316     this.lookup = {};
5317
5318     // if no id, create one
5319     // if the column does not have a dataIndex mapping,
5320     // map it to the order it is in the config
5321     for(var i = 0, len = config.length; i < len; i++){
5322         var c = config[i];
5323         if(typeof c.dataIndex == "undefined"){
5324             c.dataIndex = i;
5325         }
5326         if(typeof c.renderer == "string"){
5327             c.renderer = Roo.util.Format[c.renderer];
5328         }
5329         if(typeof c.id == "undefined"){
5330             c.id = Roo.id();
5331         }
5332         if(c.editor && c.editor.xtype){
5333             c.editor  = Roo.factory(c.editor, Roo.grid);
5334         }
5335         if(c.editor && c.editor.isFormField){
5336             c.editor = new Roo.grid.GridEditor(c.editor);
5337         }
5338         this.lookup[c.id] = c;
5339     }
5340
5341     /**
5342      * The width of columns which have no width specified (defaults to 100)
5343      * @type Number
5344      */
5345     this.defaultWidth = 100;
5346
5347     /**
5348      * Default sortable of columns which have no sortable specified (defaults to false)
5349      * @type Boolean
5350      */
5351     this.defaultSortable = false;
5352
5353     this.addEvents({
5354         /**
5355              * @event widthchange
5356              * Fires when the width of a column changes.
5357              * @param {ColumnModel} this
5358              * @param {Number} columnIndex The column index
5359              * @param {Number} newWidth The new width
5360              */
5361             "widthchange": true,
5362         /**
5363              * @event headerchange
5364              * Fires when the text of a header changes.
5365              * @param {ColumnModel} this
5366              * @param {Number} columnIndex The column index
5367              * @param {Number} newText The new header text
5368              */
5369             "headerchange": true,
5370         /**
5371              * @event hiddenchange
5372              * Fires when a column is hidden or "unhidden".
5373              * @param {ColumnModel} this
5374              * @param {Number} columnIndex The column index
5375              * @param {Boolean} hidden true if hidden, false otherwise
5376              */
5377             "hiddenchange": true,
5378             /**
5379          * @event columnmoved
5380          * Fires when a column is moved.
5381          * @param {ColumnModel} this
5382          * @param {Number} oldIndex
5383          * @param {Number} newIndex
5384          */
5385         "columnmoved" : true,
5386         /**
5387          * @event columlockchange
5388          * Fires when a column's locked state is changed
5389          * @param {ColumnModel} this
5390          * @param {Number} colIndex
5391          * @param {Boolean} locked true if locked
5392          */
5393         "columnlockchange" : true
5394     });
5395     Roo.grid.ColumnModel.superclass.constructor.call(this);
5396 };
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5398     /**
5399      * @cfg {String} header The header text to display in the Grid view.
5400      */
5401     /**
5402      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404      * specified, the column's index is used as an index into the Record's data Array.
5405      */
5406     /**
5407      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5409      */
5410     /**
5411      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412      * Defaults to the value of the {@link #defaultSortable} property.
5413      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5414      */
5415     /**
5416      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5417      */
5418     /**
5419      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5420      */
5421     /**
5422      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5423      */
5424     /**
5425      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5426      */
5427     /**
5428      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5432      */
5433        /**
5434      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5435      */
5436     /**
5437      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5438      */
5439     /**
5440      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5441      */
5442     /**
5443      * @cfg {String} cursor (Optional)
5444      */
5445     /**
5446      * @cfg {String} tooltip (Optional)
5447      */
5448     /**
5449      * @cfg {Number} xs (Optional)
5450      */
5451     /**
5452      * @cfg {Number} sm (Optional)
5453      */
5454     /**
5455      * @cfg {Number} md (Optional)
5456      */
5457     /**
5458      * @cfg {Number} lg (Optional)
5459      */
5460     /**
5461      * Returns the id of the column at the specified index.
5462      * @param {Number} index The column index
5463      * @return {String} the id
5464      */
5465     getColumnId : function(index){
5466         return this.config[index].id;
5467     },
5468
5469     /**
5470      * Returns the column for a specified id.
5471      * @param {String} id The column id
5472      * @return {Object} the column
5473      */
5474     getColumnById : function(id){
5475         return this.lookup[id];
5476     },
5477
5478     
5479     /**
5480      * Returns the column for a specified dataIndex.
5481      * @param {String} dataIndex The column dataIndex
5482      * @return {Object|Boolean} the column or false if not found
5483      */
5484     getColumnByDataIndex: function(dataIndex){
5485         var index = this.findColumnIndex(dataIndex);
5486         return index > -1 ? this.config[index] : false;
5487     },
5488     
5489     /**
5490      * Returns the index for a specified column id.
5491      * @param {String} id The column id
5492      * @return {Number} the index, or -1 if not found
5493      */
5494     getIndexById : function(id){
5495         for(var i = 0, len = this.config.length; i < len; i++){
5496             if(this.config[i].id == id){
5497                 return i;
5498             }
5499         }
5500         return -1;
5501     },
5502     
5503     /**
5504      * Returns the index for a specified column dataIndex.
5505      * @param {String} dataIndex The column dataIndex
5506      * @return {Number} the index, or -1 if not found
5507      */
5508     
5509     findColumnIndex : function(dataIndex){
5510         for(var i = 0, len = this.config.length; i < len; i++){
5511             if(this.config[i].dataIndex == dataIndex){
5512                 return i;
5513             }
5514         }
5515         return -1;
5516     },
5517     
5518     
5519     moveColumn : function(oldIndex, newIndex){
5520         var c = this.config[oldIndex];
5521         this.config.splice(oldIndex, 1);
5522         this.config.splice(newIndex, 0, c);
5523         this.dataMap = null;
5524         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5525     },
5526
5527     isLocked : function(colIndex){
5528         return this.config[colIndex].locked === true;
5529     },
5530
5531     setLocked : function(colIndex, value, suppressEvent){
5532         if(this.isLocked(colIndex) == value){
5533             return;
5534         }
5535         this.config[colIndex].locked = value;
5536         if(!suppressEvent){
5537             this.fireEvent("columnlockchange", this, colIndex, value);
5538         }
5539     },
5540
5541     getTotalLockedWidth : function(){
5542         var totalWidth = 0;
5543         for(var i = 0; i < this.config.length; i++){
5544             if(this.isLocked(i) && !this.isHidden(i)){
5545                 this.totalWidth += this.getColumnWidth(i);
5546             }
5547         }
5548         return totalWidth;
5549     },
5550
5551     getLockedCount : function(){
5552         for(var i = 0, len = this.config.length; i < len; i++){
5553             if(!this.isLocked(i)){
5554                 return i;
5555             }
5556         }
5557         
5558         return this.config.length;
5559     },
5560
5561     /**
5562      * Returns the number of columns.
5563      * @return {Number}
5564      */
5565     getColumnCount : function(visibleOnly){
5566         if(visibleOnly === true){
5567             var c = 0;
5568             for(var i = 0, len = this.config.length; i < len; i++){
5569                 if(!this.isHidden(i)){
5570                     c++;
5571                 }
5572             }
5573             return c;
5574         }
5575         return this.config.length;
5576     },
5577
5578     /**
5579      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580      * @param {Function} fn
5581      * @param {Object} scope (optional)
5582      * @return {Array} result
5583      */
5584     getColumnsBy : function(fn, scope){
5585         var r = [];
5586         for(var i = 0, len = this.config.length; i < len; i++){
5587             var c = this.config[i];
5588             if(fn.call(scope||this, c, i) === true){
5589                 r[r.length] = c;
5590             }
5591         }
5592         return r;
5593     },
5594
5595     /**
5596      * Returns true if the specified column is sortable.
5597      * @param {Number} col The column index
5598      * @return {Boolean}
5599      */
5600     isSortable : function(col){
5601         if(typeof this.config[col].sortable == "undefined"){
5602             return this.defaultSortable;
5603         }
5604         return this.config[col].sortable;
5605     },
5606
5607     /**
5608      * Returns the rendering (formatting) function defined for the column.
5609      * @param {Number} col The column index.
5610      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5611      */
5612     getRenderer : function(col){
5613         if(!this.config[col].renderer){
5614             return Roo.grid.ColumnModel.defaultRenderer;
5615         }
5616         return this.config[col].renderer;
5617     },
5618
5619     /**
5620      * Sets the rendering (formatting) function for a column.
5621      * @param {Number} col The column index
5622      * @param {Function} fn The function to use to process the cell's raw data
5623      * to return HTML markup for the grid view. The render function is called with
5624      * the following parameters:<ul>
5625      * <li>Data value.</li>
5626      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627      * <li>css A CSS style string to apply to the table cell.</li>
5628      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630      * <li>Row index</li>
5631      * <li>Column index</li>
5632      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5633      */
5634     setRenderer : function(col, fn){
5635         this.config[col].renderer = fn;
5636     },
5637
5638     /**
5639      * Returns the width for the specified column.
5640      * @param {Number} col The column index
5641      * @return {Number}
5642      */
5643     getColumnWidth : function(col){
5644         return this.config[col].width * 1 || this.defaultWidth;
5645     },
5646
5647     /**
5648      * Sets the width for a column.
5649      * @param {Number} col The column index
5650      * @param {Number} width The new width
5651      */
5652     setColumnWidth : function(col, width, suppressEvent){
5653         this.config[col].width = width;
5654         this.totalWidth = null;
5655         if(!suppressEvent){
5656              this.fireEvent("widthchange", this, col, width);
5657         }
5658     },
5659
5660     /**
5661      * Returns the total width of all columns.
5662      * @param {Boolean} includeHidden True to include hidden column widths
5663      * @return {Number}
5664      */
5665     getTotalWidth : function(includeHidden){
5666         if(!this.totalWidth){
5667             this.totalWidth = 0;
5668             for(var i = 0, len = this.config.length; i < len; i++){
5669                 if(includeHidden || !this.isHidden(i)){
5670                     this.totalWidth += this.getColumnWidth(i);
5671                 }
5672             }
5673         }
5674         return this.totalWidth;
5675     },
5676
5677     /**
5678      * Returns the header for the specified column.
5679      * @param {Number} col The column index
5680      * @return {String}
5681      */
5682     getColumnHeader : function(col){
5683         return this.config[col].header;
5684     },
5685
5686     /**
5687      * Sets the header for a column.
5688      * @param {Number} col The column index
5689      * @param {String} header The new header
5690      */
5691     setColumnHeader : function(col, header){
5692         this.config[col].header = header;
5693         this.fireEvent("headerchange", this, col, header);
5694     },
5695
5696     /**
5697      * Returns the tooltip for the specified column.
5698      * @param {Number} col The column index
5699      * @return {String}
5700      */
5701     getColumnTooltip : function(col){
5702             return this.config[col].tooltip;
5703     },
5704     /**
5705      * Sets the tooltip for a column.
5706      * @param {Number} col The column index
5707      * @param {String} tooltip The new tooltip
5708      */
5709     setColumnTooltip : function(col, tooltip){
5710             this.config[col].tooltip = tooltip;
5711     },
5712
5713     /**
5714      * Returns the dataIndex for the specified column.
5715      * @param {Number} col The column index
5716      * @return {Number}
5717      */
5718     getDataIndex : function(col){
5719         return this.config[col].dataIndex;
5720     },
5721
5722     /**
5723      * Sets the dataIndex for a column.
5724      * @param {Number} col The column index
5725      * @param {Number} dataIndex The new dataIndex
5726      */
5727     setDataIndex : function(col, dataIndex){
5728         this.config[col].dataIndex = dataIndex;
5729     },
5730
5731     
5732     
5733     /**
5734      * Returns true if the cell is editable.
5735      * @param {Number} colIndex The column index
5736      * @param {Number} rowIndex The row index - this is nto actually used..?
5737      * @return {Boolean}
5738      */
5739     isCellEditable : function(colIndex, rowIndex){
5740         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5741     },
5742
5743     /**
5744      * Returns the editor defined for the cell/column.
5745      * return false or null to disable editing.
5746      * @param {Number} colIndex The column index
5747      * @param {Number} rowIndex The row index
5748      * @return {Object}
5749      */
5750     getCellEditor : function(colIndex, rowIndex){
5751         return this.config[colIndex].editor;
5752     },
5753
5754     /**
5755      * Sets if a column is editable.
5756      * @param {Number} col The column index
5757      * @param {Boolean} editable True if the column is editable
5758      */
5759     setEditable : function(col, editable){
5760         this.config[col].editable = editable;
5761     },
5762
5763
5764     /**
5765      * Returns true if the column is hidden.
5766      * @param {Number} colIndex The column index
5767      * @return {Boolean}
5768      */
5769     isHidden : function(colIndex){
5770         return this.config[colIndex].hidden;
5771     },
5772
5773
5774     /**
5775      * Returns true if the column width cannot be changed
5776      */
5777     isFixed : function(colIndex){
5778         return this.config[colIndex].fixed;
5779     },
5780
5781     /**
5782      * Returns true if the column can be resized
5783      * @return {Boolean}
5784      */
5785     isResizable : function(colIndex){
5786         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5787     },
5788     /**
5789      * Sets if a column is hidden.
5790      * @param {Number} colIndex The column index
5791      * @param {Boolean} hidden True if the column is hidden
5792      */
5793     setHidden : function(colIndex, hidden){
5794         this.config[colIndex].hidden = hidden;
5795         this.totalWidth = null;
5796         this.fireEvent("hiddenchange", this, colIndex, hidden);
5797     },
5798
5799     /**
5800      * Sets the editor for a column.
5801      * @param {Number} col The column index
5802      * @param {Object} editor The editor object
5803      */
5804     setEditor : function(col, editor){
5805         this.config[col].editor = editor;
5806     }
5807 });
5808
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5810 {
5811     if(typeof value == "object") {
5812         return value;
5813     }
5814         if(typeof value == "string" && value.length < 1){
5815             return "&#160;";
5816         }
5817     
5818         return String.format("{0}", value);
5819 };
5820
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5823 /*
5824  * Based on:
5825  * Ext JS Library 1.1.1
5826  * Copyright(c) 2006-2007, Ext JS, LLC.
5827  *
5828  * Originally Released Under LGPL - original licence link has changed is not relivant.
5829  *
5830  * Fork - LGPL
5831  * <script type="text/javascript">
5832  */
5833  
5834 /**
5835  * @class Roo.LoadMask
5836  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5837  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5839  * element's UpdateManager load indicator and will be destroyed after the initial load.
5840  * @constructor
5841  * Create a new LoadMask
5842  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843  * @param {Object} config The config object
5844  */
5845 Roo.LoadMask = function(el, config){
5846     this.el = Roo.get(el);
5847     Roo.apply(this, config);
5848     if(this.store){
5849         this.store.on('beforeload', this.onBeforeLoad, this);
5850         this.store.on('load', this.onLoad, this);
5851         this.store.on('loadexception', this.onLoadException, this);
5852         this.removeMask = false;
5853     }else{
5854         var um = this.el.getUpdateManager();
5855         um.showLoadIndicator = false; // disable the default indicator
5856         um.on('beforeupdate', this.onBeforeLoad, this);
5857         um.on('update', this.onLoad, this);
5858         um.on('failure', this.onLoad, this);
5859         this.removeMask = true;
5860     }
5861 };
5862
5863 Roo.LoadMask.prototype = {
5864     /**
5865      * @cfg {Boolean} removeMask
5866      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5868      */
5869     /**
5870      * @cfg {String} msg
5871      * The text to display in a centered loading message box (defaults to 'Loading...')
5872      */
5873     msg : 'Loading...',
5874     /**
5875      * @cfg {String} msgCls
5876      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5877      */
5878     msgCls : 'x-mask-loading',
5879
5880     /**
5881      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5882      * @type Boolean
5883      */
5884     disabled: false,
5885
5886     /**
5887      * Disables the mask to prevent it from being displayed
5888      */
5889     disable : function(){
5890        this.disabled = true;
5891     },
5892
5893     /**
5894      * Enables the mask so that it can be displayed
5895      */
5896     enable : function(){
5897         this.disabled = false;
5898     },
5899     
5900     onLoadException : function()
5901     {
5902         Roo.log(arguments);
5903         
5904         if (typeof(arguments[3]) != 'undefined') {
5905             Roo.MessageBox.alert("Error loading",arguments[3]);
5906         } 
5907         /*
5908         try {
5909             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5911             }   
5912         } catch(e) {
5913             
5914         }
5915         */
5916     
5917         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5918     },
5919     // private
5920     onLoad : function()
5921     {
5922         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5923     },
5924
5925     // private
5926     onBeforeLoad : function(){
5927         if(!this.disabled){
5928             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5929         }
5930     },
5931
5932     // private
5933     destroy : function(){
5934         if(this.store){
5935             this.store.un('beforeload', this.onBeforeLoad, this);
5936             this.store.un('load', this.onLoad, this);
5937             this.store.un('loadexception', this.onLoadException, this);
5938         }else{
5939             var um = this.el.getUpdateManager();
5940             um.un('beforeupdate', this.onBeforeLoad, this);
5941             um.un('update', this.onLoad, this);
5942             um.un('failure', this.onLoad, this);
5943         }
5944     }
5945 };/*
5946  * - LGPL
5947  *
5948  * table
5949  * 
5950  */
5951
5952 /**
5953  * @class Roo.bootstrap.Table
5954  * @extends Roo.bootstrap.Component
5955  * Bootstrap Table class
5956  * @cfg {String} cls table class
5957  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958  * @cfg {String} bgcolor Specifies the background color for a table
5959  * @cfg {Number} border Specifies whether the table cells should have borders or not
5960  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961  * @cfg {Number} cellspacing Specifies the space between cells
5962  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964  * @cfg {String} sortable Specifies that the table should be sortable
5965  * @cfg {String} summary Specifies a summary of the content of a table
5966  * @cfg {Number} width Specifies the width of a table
5967  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5968  * 
5969  * @cfg {boolean} striped Should the rows be alternative striped
5970  * @cfg {boolean} bordered Add borders to the table
5971  * @cfg {boolean} hover Add hover highlighting
5972  * @cfg {boolean} condensed Format condensed
5973  * @cfg {boolean} responsive Format condensed
5974  * @cfg {Boolean} loadMask (true|false) default false
5975  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977  * @cfg {Boolean} rowSelection (true|false) default false
5978  * @cfg {Boolean} cellSelection (true|false) default false
5979  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5981  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5982  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5983  
5984  * 
5985  * @constructor
5986  * Create a new Table
5987  * @param {Object} config The config object
5988  */
5989
5990 Roo.bootstrap.Table = function(config){
5991     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5992     
5993   
5994     
5995     // BC...
5996     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6000     
6001     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6002     if (this.sm) {
6003         this.sm.grid = this;
6004         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005         this.sm = this.selModel;
6006         this.sm.xmodule = this.xmodule || false;
6007     }
6008     
6009     if (this.cm && typeof(this.cm.config) == 'undefined') {
6010         this.colModel = new Roo.grid.ColumnModel(this.cm);
6011         this.cm = this.colModel;
6012         this.cm.xmodule = this.xmodule || false;
6013     }
6014     if (this.store) {
6015         this.store= Roo.factory(this.store, Roo.data);
6016         this.ds = this.store;
6017         this.ds.xmodule = this.xmodule || false;
6018          
6019     }
6020     if (this.footer && this.store) {
6021         this.footer.dataSource = this.ds;
6022         this.footer = Roo.factory(this.footer);
6023     }
6024     
6025     /** @private */
6026     this.addEvents({
6027         /**
6028          * @event cellclick
6029          * Fires when a cell is clicked
6030          * @param {Roo.bootstrap.Table} this
6031          * @param {Roo.Element} el
6032          * @param {Number} rowIndex
6033          * @param {Number} columnIndex
6034          * @param {Roo.EventObject} e
6035          */
6036         "cellclick" : true,
6037         /**
6038          * @event celldblclick
6039          * Fires when a cell is double clicked
6040          * @param {Roo.bootstrap.Table} this
6041          * @param {Roo.Element} el
6042          * @param {Number} rowIndex
6043          * @param {Number} columnIndex
6044          * @param {Roo.EventObject} e
6045          */
6046         "celldblclick" : true,
6047         /**
6048          * @event rowclick
6049          * Fires when a row is clicked
6050          * @param {Roo.bootstrap.Table} this
6051          * @param {Roo.Element} el
6052          * @param {Number} rowIndex
6053          * @param {Roo.EventObject} e
6054          */
6055         "rowclick" : true,
6056         /**
6057          * @event rowdblclick
6058          * Fires when a row is double clicked
6059          * @param {Roo.bootstrap.Table} this
6060          * @param {Roo.Element} el
6061          * @param {Number} rowIndex
6062          * @param {Roo.EventObject} e
6063          */
6064         "rowdblclick" : true,
6065         /**
6066          * @event mouseover
6067          * Fires when a mouseover occur
6068          * @param {Roo.bootstrap.Table} this
6069          * @param {Roo.Element} el
6070          * @param {Number} rowIndex
6071          * @param {Number} columnIndex
6072          * @param {Roo.EventObject} e
6073          */
6074         "mouseover" : true,
6075         /**
6076          * @event mouseout
6077          * Fires when a mouseout occur
6078          * @param {Roo.bootstrap.Table} this
6079          * @param {Roo.Element} el
6080          * @param {Number} rowIndex
6081          * @param {Number} columnIndex
6082          * @param {Roo.EventObject} e
6083          */
6084         "mouseout" : true,
6085         /**
6086          * @event rowclass
6087          * Fires when a row is rendered, so you can change add a style to it.
6088          * @param {Roo.bootstrap.Table} this
6089          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6090          */
6091         'rowclass' : true,
6092           /**
6093          * @event rowsrendered
6094          * Fires when all the  rows have been rendered
6095          * @param {Roo.bootstrap.Table} this
6096          */
6097         'rowsrendered' : true,
6098         /**
6099          * @event contextmenu
6100          * The raw contextmenu event for the entire grid.
6101          * @param {Roo.EventObject} e
6102          */
6103         "contextmenu" : true,
6104         /**
6105          * @event rowcontextmenu
6106          * Fires when a row is right clicked
6107          * @param {Roo.bootstrap.Table} this
6108          * @param {Number} rowIndex
6109          * @param {Roo.EventObject} e
6110          */
6111         "rowcontextmenu" : true,
6112         /**
6113          * @event cellcontextmenu
6114          * Fires when a cell is right clicked
6115          * @param {Roo.bootstrap.Table} this
6116          * @param {Number} rowIndex
6117          * @param {Number} cellIndex
6118          * @param {Roo.EventObject} e
6119          */
6120          "cellcontextmenu" : true,
6121          /**
6122          * @event headercontextmenu
6123          * Fires when a header is right clicked
6124          * @param {Roo.bootstrap.Table} this
6125          * @param {Number} columnIndex
6126          * @param {Roo.EventObject} e
6127          */
6128         "headercontextmenu" : true
6129     });
6130 };
6131
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6133     
6134     cls: false,
6135     align: false,
6136     bgcolor: false,
6137     border: false,
6138     cellpadding: false,
6139     cellspacing: false,
6140     frame: false,
6141     rules: false,
6142     sortable: false,
6143     summary: false,
6144     width: false,
6145     striped : false,
6146     scrollBody : false,
6147     bordered: false,
6148     hover:  false,
6149     condensed : false,
6150     responsive : false,
6151     sm : false,
6152     cm : false,
6153     store : false,
6154     loadMask : false,
6155     footerShow : true,
6156     headerShow : true,
6157   
6158     rowSelection : false,
6159     cellSelection : false,
6160     layout : false,
6161     
6162     // Roo.Element - the tbody
6163     mainBody: false,
6164     // Roo.Element - thead element
6165     mainHead: false,
6166     
6167     container: false, // used by gridpanel...
6168     
6169     lazyLoad : false,
6170     
6171     CSS : Roo.util.CSS,
6172     
6173     auto_hide_footer : false,
6174     
6175     getAutoCreate : function()
6176     {
6177         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'table',
6181             cls : 'table',
6182             cn : []
6183         };
6184         if (this.scrollBody) {
6185             cfg.cls += ' table-body-fixed';
6186         }    
6187         if (this.striped) {
6188             cfg.cls += ' table-striped';
6189         }
6190         
6191         if (this.hover) {
6192             cfg.cls += ' table-hover';
6193         }
6194         if (this.bordered) {
6195             cfg.cls += ' table-bordered';
6196         }
6197         if (this.condensed) {
6198             cfg.cls += ' table-condensed';
6199         }
6200         if (this.responsive) {
6201             cfg.cls += ' table-responsive';
6202         }
6203         
6204         if (this.cls) {
6205             cfg.cls+=  ' ' +this.cls;
6206         }
6207         
6208         // this lot should be simplifed...
6209         var _t = this;
6210         var cp = [
6211             'align',
6212             'bgcolor',
6213             'border',
6214             'cellpadding',
6215             'cellspacing',
6216             'frame',
6217             'rules',
6218             'sortable',
6219             'summary',
6220             'width'
6221         ].forEach(function(k) {
6222             if (_t[k]) {
6223                 cfg[k] = _t[k];
6224             }
6225         });
6226         
6227         
6228         if (this.layout) {
6229             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6230         }
6231         
6232         if(this.store || this.cm){
6233             if(this.headerShow){
6234                 cfg.cn.push(this.renderHeader());
6235             }
6236             
6237             cfg.cn.push(this.renderBody());
6238             
6239             if(this.footerShow){
6240                 cfg.cn.push(this.renderFooter());
6241             }
6242             // where does this come from?
6243             //cfg.cls+=  ' TableGrid';
6244         }
6245         
6246         return { cn : [ cfg ] };
6247     },
6248     
6249     initEvents : function()
6250     {   
6251         if(!this.store || !this.cm){
6252             return;
6253         }
6254         if (this.selModel) {
6255             this.selModel.initEvents();
6256         }
6257         
6258         
6259         //Roo.log('initEvents with ds!!!!');
6260         
6261         this.mainBody = this.el.select('tbody', true).first();
6262         this.mainHead = this.el.select('thead', true).first();
6263         this.mainFoot = this.el.select('tfoot', true).first();
6264         
6265         
6266         
6267         var _this = this;
6268         
6269         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270             e.on('click', _this.sort, _this);
6271         });
6272         
6273         this.mainBody.on("click", this.onClick, this);
6274         this.mainBody.on("dblclick", this.onDblClick, this);
6275         
6276         // why is this done????? = it breaks dialogs??
6277         //this.parent().el.setStyle('position', 'relative');
6278         
6279         
6280         if (this.footer) {
6281             this.footer.parentId = this.id;
6282             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6283             
6284             if(this.lazyLoad){
6285                 this.el.select('tfoot tr td').first().addClass('hide');
6286             }
6287         } 
6288         
6289         if(this.loadMask) {
6290             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6291         }
6292         
6293         this.store.on('load', this.onLoad, this);
6294         this.store.on('beforeload', this.onBeforeLoad, this);
6295         this.store.on('update', this.onUpdate, this);
6296         this.store.on('add', this.onAdd, this);
6297         this.store.on("clear", this.clear, this);
6298         
6299         this.el.on("contextmenu", this.onContextMenu, this);
6300         
6301         this.mainBody.on('scroll', this.onBodyScroll, this);
6302         
6303         this.cm.on("headerchange", this.onHeaderChange, this);
6304         
6305         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6306         
6307     },
6308     
6309     onContextMenu : function(e, t)
6310     {
6311         this.processEvent("contextmenu", e);
6312     },
6313     
6314     processEvent : function(name, e)
6315     {
6316         if (name != 'touchstart' ) {
6317             this.fireEvent(name, e);    
6318         }
6319         
6320         var t = e.getTarget();
6321         
6322         var cell = Roo.get(t);
6323         
6324         if(!cell){
6325             return;
6326         }
6327         
6328         if(cell.findParent('tfoot', false, true)){
6329             return;
6330         }
6331         
6332         if(cell.findParent('thead', false, true)){
6333             
6334             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335                 cell = Roo.get(t).findParent('th', false, true);
6336                 if (!cell) {
6337                     Roo.log("failed to find th in thead?");
6338                     Roo.log(e.getTarget());
6339                     return;
6340                 }
6341             }
6342             
6343             var cellIndex = cell.dom.cellIndex;
6344             
6345             var ename = name == 'touchstart' ? 'click' : name;
6346             this.fireEvent("header" + ename, this, cellIndex, e);
6347             
6348             return;
6349         }
6350         
6351         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352             cell = Roo.get(t).findParent('td', false, true);
6353             if (!cell) {
6354                 Roo.log("failed to find th in tbody?");
6355                 Roo.log(e.getTarget());
6356                 return;
6357             }
6358         }
6359         
6360         var row = cell.findParent('tr', false, true);
6361         var cellIndex = cell.dom.cellIndex;
6362         var rowIndex = row.dom.rowIndex - 1;
6363         
6364         if(row !== false){
6365             
6366             this.fireEvent("row" + name, this, rowIndex, e);
6367             
6368             if(cell !== false){
6369             
6370                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6371             }
6372         }
6373         
6374     },
6375     
6376     onMouseover : function(e, el)
6377     {
6378         var cell = Roo.get(el);
6379         
6380         if(!cell){
6381             return;
6382         }
6383         
6384         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385             cell = cell.findParent('td', false, true);
6386         }
6387         
6388         var row = cell.findParent('tr', false, true);
6389         var cellIndex = cell.dom.cellIndex;
6390         var rowIndex = row.dom.rowIndex - 1; // start from 0
6391         
6392         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6393         
6394     },
6395     
6396     onMouseout : function(e, el)
6397     {
6398         var cell = Roo.get(el);
6399         
6400         if(!cell){
6401             return;
6402         }
6403         
6404         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405             cell = cell.findParent('td', false, true);
6406         }
6407         
6408         var row = cell.findParent('tr', false, true);
6409         var cellIndex = cell.dom.cellIndex;
6410         var rowIndex = row.dom.rowIndex - 1; // start from 0
6411         
6412         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6413         
6414     },
6415     
6416     onClick : function(e, el)
6417     {
6418         var cell = Roo.get(el);
6419         
6420         if(!cell || (!this.cellSelection && !this.rowSelection)){
6421             return;
6422         }
6423         
6424         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425             cell = cell.findParent('td', false, true);
6426         }
6427         
6428         if(!cell || typeof(cell) == 'undefined'){
6429             return;
6430         }
6431         
6432         var row = cell.findParent('tr', false, true);
6433         
6434         if(!row || typeof(row) == 'undefined'){
6435             return;
6436         }
6437         
6438         var cellIndex = cell.dom.cellIndex;
6439         var rowIndex = this.getRowIndex(row);
6440         
6441         // why??? - should these not be based on SelectionModel?
6442         if(this.cellSelection){
6443             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6444         }
6445         
6446         if(this.rowSelection){
6447             this.fireEvent('rowclick', this, row, rowIndex, e);
6448         }
6449         
6450         
6451     },
6452         
6453     onDblClick : function(e,el)
6454     {
6455         var cell = Roo.get(el);
6456         
6457         if(!cell || (!this.cellSelection && !this.rowSelection)){
6458             return;
6459         }
6460         
6461         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462             cell = cell.findParent('td', false, true);
6463         }
6464         
6465         if(!cell || typeof(cell) == 'undefined'){
6466             return;
6467         }
6468         
6469         var row = cell.findParent('tr', false, true);
6470         
6471         if(!row || typeof(row) == 'undefined'){
6472             return;
6473         }
6474         
6475         var cellIndex = cell.dom.cellIndex;
6476         var rowIndex = this.getRowIndex(row);
6477         
6478         if(this.cellSelection){
6479             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6480         }
6481         
6482         if(this.rowSelection){
6483             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6484         }
6485     },
6486     
6487     sort : function(e,el)
6488     {
6489         var col = Roo.get(el);
6490         
6491         if(!col.hasClass('sortable')){
6492             return;
6493         }
6494         
6495         var sort = col.attr('sort');
6496         var dir = 'ASC';
6497         
6498         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6499             dir = 'DESC';
6500         }
6501         
6502         this.store.sortInfo = {field : sort, direction : dir};
6503         
6504         if (this.footer) {
6505             Roo.log("calling footer first");
6506             this.footer.onClick('first');
6507         } else {
6508         
6509             this.store.load({ params : { start : 0 } });
6510         }
6511     },
6512     
6513     renderHeader : function()
6514     {
6515         var header = {
6516             tag: 'thead',
6517             cn : []
6518         };
6519         
6520         var cm = this.cm;
6521         this.totalWidth = 0;
6522         
6523         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6524             
6525             var config = cm.config[i];
6526             
6527             var c = {
6528                 tag: 'th',
6529                 cls : 'x-hcol-' + i,
6530                 style : '',
6531                 html: cm.getColumnHeader(i)
6532             };
6533             
6534             var hh = '';
6535             
6536             if(typeof(config.sortable) != 'undefined' && config.sortable){
6537                 c.cls = 'sortable';
6538                 c.html = '<i class="glyphicon"></i>' + c.html;
6539             }
6540             
6541             if(typeof(config.lgHeader) != 'undefined'){
6542                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6543             }
6544             
6545             if(typeof(config.mdHeader) != 'undefined'){
6546                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6547             }
6548             
6549             if(typeof(config.smHeader) != 'undefined'){
6550                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6551             }
6552             
6553             if(typeof(config.xsHeader) != 'undefined'){
6554                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6555             }
6556             
6557             if(hh.length){
6558                 c.html = hh;
6559             }
6560             
6561             if(typeof(config.tooltip) != 'undefined'){
6562                 c.tooltip = config.tooltip;
6563             }
6564             
6565             if(typeof(config.colspan) != 'undefined'){
6566                 c.colspan = config.colspan;
6567             }
6568             
6569             if(typeof(config.hidden) != 'undefined' && config.hidden){
6570                 c.style += ' display:none;';
6571             }
6572             
6573             if(typeof(config.dataIndex) != 'undefined'){
6574                 c.sort = config.dataIndex;
6575             }
6576             
6577            
6578             
6579             if(typeof(config.align) != 'undefined' && config.align.length){
6580                 c.style += ' text-align:' + config.align + ';';
6581             }
6582             
6583             if(typeof(config.width) != 'undefined'){
6584                 c.style += ' width:' + config.width + 'px;';
6585                 this.totalWidth += config.width;
6586             } else {
6587                 this.totalWidth += 100; // assume minimum of 100 per column?
6588             }
6589             
6590             if(typeof(config.cls) != 'undefined'){
6591                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6592             }
6593             
6594             ['xs','sm','md','lg'].map(function(size){
6595                 
6596                 if(typeof(config[size]) == 'undefined'){
6597                     return;
6598                 }
6599                 
6600                 if (!config[size]) { // 0 = hidden
6601                     c.cls += ' hidden-' + size;
6602                     return;
6603                 }
6604                 
6605                 c.cls += ' col-' + size + '-' + config[size];
6606
6607             });
6608             
6609             header.cn.push(c)
6610         }
6611         
6612         return header;
6613     },
6614     
6615     renderBody : function()
6616     {
6617         var body = {
6618             tag: 'tbody',
6619             cn : [
6620                 {
6621                     tag: 'tr',
6622                     cn : [
6623                         {
6624                             tag : 'td',
6625                             colspan :  this.cm.getColumnCount()
6626                         }
6627                     ]
6628                 }
6629             ]
6630         };
6631         
6632         return body;
6633     },
6634     
6635     renderFooter : function()
6636     {
6637         var footer = {
6638             tag: 'tfoot',
6639             cn : [
6640                 {
6641                     tag: 'tr',
6642                     cn : [
6643                         {
6644                             tag : 'td',
6645                             colspan :  this.cm.getColumnCount()
6646                         }
6647                     ]
6648                 }
6649             ]
6650         };
6651         
6652         return footer;
6653     },
6654     
6655     
6656     
6657     onLoad : function()
6658     {
6659 //        Roo.log('ds onload');
6660         this.clear();
6661         
6662         var _this = this;
6663         var cm = this.cm;
6664         var ds = this.store;
6665         
6666         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668             if (_this.store.sortInfo) {
6669                     
6670                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6672                 }
6673                 
6674                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6676                 }
6677             }
6678         });
6679         
6680         var tbody =  this.mainBody;
6681               
6682         if(ds.getCount() > 0){
6683             ds.data.each(function(d,rowIndex){
6684                 var row =  this.renderRow(cm, ds, rowIndex);
6685                 
6686                 tbody.createChild(row);
6687                 
6688                 var _this = this;
6689                 
6690                 if(row.cellObjects.length){
6691                     Roo.each(row.cellObjects, function(r){
6692                         _this.renderCellObject(r);
6693                     })
6694                 }
6695                 
6696             }, this);
6697         }
6698         
6699         var tfoot = this.el.select('tfoot', true).first();
6700         
6701         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6702             
6703             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6704             
6705             var total = this.ds.getTotalCount();
6706             
6707             if(this.footer.pageSize < total){
6708                 this.mainFoot.show();
6709             }
6710         }
6711         
6712         Roo.each(this.el.select('tbody td', true).elements, function(e){
6713             e.on('mouseover', _this.onMouseover, _this);
6714         });
6715         
6716         Roo.each(this.el.select('tbody td', true).elements, function(e){
6717             e.on('mouseout', _this.onMouseout, _this);
6718         });
6719         this.fireEvent('rowsrendered', this);
6720         
6721         this.autoSize();
6722     },
6723     
6724     
6725     onUpdate : function(ds,record)
6726     {
6727         this.refreshRow(record);
6728         this.autoSize();
6729     },
6730     
6731     onRemove : function(ds, record, index, isUpdate){
6732         if(isUpdate !== true){
6733             this.fireEvent("beforerowremoved", this, index, record);
6734         }
6735         var bt = this.mainBody.dom;
6736         
6737         var rows = this.el.select('tbody > tr', true).elements;
6738         
6739         if(typeof(rows[index]) != 'undefined'){
6740             bt.removeChild(rows[index].dom);
6741         }
6742         
6743 //        if(bt.rows[index]){
6744 //            bt.removeChild(bt.rows[index]);
6745 //        }
6746         
6747         if(isUpdate !== true){
6748             //this.stripeRows(index);
6749             //this.syncRowHeights(index, index);
6750             //this.layout();
6751             this.fireEvent("rowremoved", this, index, record);
6752         }
6753     },
6754     
6755     onAdd : function(ds, records, rowIndex)
6756     {
6757         //Roo.log('on Add called');
6758         // - note this does not handle multiple adding very well..
6759         var bt = this.mainBody.dom;
6760         for (var i =0 ; i < records.length;i++) {
6761             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762             //Roo.log(records[i]);
6763             //Roo.log(this.store.getAt(rowIndex+i));
6764             this.insertRow(this.store, rowIndex + i, false);
6765             return;
6766         }
6767         
6768     },
6769     
6770     
6771     refreshRow : function(record){
6772         var ds = this.store, index;
6773         if(typeof record == 'number'){
6774             index = record;
6775             record = ds.getAt(index);
6776         }else{
6777             index = ds.indexOf(record);
6778         }
6779         this.insertRow(ds, index, true);
6780         this.autoSize();
6781         this.onRemove(ds, record, index+1, true);
6782         this.autoSize();
6783         //this.syncRowHeights(index, index);
6784         //this.layout();
6785         this.fireEvent("rowupdated", this, index, record);
6786     },
6787     
6788     insertRow : function(dm, rowIndex, isUpdate){
6789         
6790         if(!isUpdate){
6791             this.fireEvent("beforerowsinserted", this, rowIndex);
6792         }
6793             //var s = this.getScrollState();
6794         var row = this.renderRow(this.cm, this.store, rowIndex);
6795         // insert before rowIndex..
6796         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6797         
6798         var _this = this;
6799                 
6800         if(row.cellObjects.length){
6801             Roo.each(row.cellObjects, function(r){
6802                 _this.renderCellObject(r);
6803             })
6804         }
6805             
6806         if(!isUpdate){
6807             this.fireEvent("rowsinserted", this, rowIndex);
6808             //this.syncRowHeights(firstRow, lastRow);
6809             //this.stripeRows(firstRow);
6810             //this.layout();
6811         }
6812         
6813     },
6814     
6815     
6816     getRowDom : function(rowIndex)
6817     {
6818         var rows = this.el.select('tbody > tr', true).elements;
6819         
6820         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6821         
6822     },
6823     // returns the object tree for a tr..
6824   
6825     
6826     renderRow : function(cm, ds, rowIndex) 
6827     {
6828         var d = ds.getAt(rowIndex);
6829         
6830         var row = {
6831             tag : 'tr',
6832             cls : 'x-row-' + rowIndex,
6833             cn : []
6834         };
6835             
6836         var cellObjects = [];
6837         
6838         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839             var config = cm.config[i];
6840             
6841             var renderer = cm.getRenderer(i);
6842             var value = '';
6843             var id = false;
6844             
6845             if(typeof(renderer) !== 'undefined'){
6846                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6847             }
6848             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849             // and are rendered into the cells after the row is rendered - using the id for the element.
6850             
6851             if(typeof(value) === 'object'){
6852                 id = Roo.id();
6853                 cellObjects.push({
6854                     container : id,
6855                     cfg : value 
6856                 })
6857             }
6858             
6859             var rowcfg = {
6860                 record: d,
6861                 rowIndex : rowIndex,
6862                 colIndex : i,
6863                 rowClass : ''
6864             };
6865
6866             this.fireEvent('rowclass', this, rowcfg);
6867             
6868             var td = {
6869                 tag: 'td',
6870                 cls : rowcfg.rowClass + ' x-col-' + i,
6871                 style: '',
6872                 html: (typeof(value) === 'object') ? '' : value
6873             };
6874             
6875             if (id) {
6876                 td.id = id;
6877             }
6878             
6879             if(typeof(config.colspan) != 'undefined'){
6880                 td.colspan = config.colspan;
6881             }
6882             
6883             if(typeof(config.hidden) != 'undefined' && config.hidden){
6884                 td.style += ' display:none;';
6885             }
6886             
6887             if(typeof(config.align) != 'undefined' && config.align.length){
6888                 td.style += ' text-align:' + config.align + ';';
6889             }
6890             if(typeof(config.valign) != 'undefined' && config.valign.length){
6891                 td.style += ' vertical-align:' + config.valign + ';';
6892             }
6893             
6894             if(typeof(config.width) != 'undefined'){
6895                 td.style += ' width:' +  config.width + 'px;';
6896             }
6897             
6898             if(typeof(config.cursor) != 'undefined'){
6899                 td.style += ' cursor:' +  config.cursor + ';';
6900             }
6901             
6902             if(typeof(config.cls) != 'undefined'){
6903                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6904             }
6905             
6906             ['xs','sm','md','lg'].map(function(size){
6907                 
6908                 if(typeof(config[size]) == 'undefined'){
6909                     return;
6910                 }
6911                 
6912                 if (!config[size]) { // 0 = hidden
6913                     td.cls += ' hidden-' + size;
6914                     return;
6915                 }
6916                 
6917                 td.cls += ' col-' + size + '-' + config[size];
6918
6919             });
6920             
6921             row.cn.push(td);
6922            
6923         }
6924         
6925         row.cellObjects = cellObjects;
6926         
6927         return row;
6928           
6929     },
6930     
6931     
6932     
6933     onBeforeLoad : function()
6934     {
6935         
6936     },
6937      /**
6938      * Remove all rows
6939      */
6940     clear : function()
6941     {
6942         this.el.select('tbody', true).first().dom.innerHTML = '';
6943     },
6944     /**
6945      * Show or hide a row.
6946      * @param {Number} rowIndex to show or hide
6947      * @param {Boolean} state hide
6948      */
6949     setRowVisibility : function(rowIndex, state)
6950     {
6951         var bt = this.mainBody.dom;
6952         
6953         var rows = this.el.select('tbody > tr', true).elements;
6954         
6955         if(typeof(rows[rowIndex]) == 'undefined'){
6956             return;
6957         }
6958         rows[rowIndex].dom.style.display = state ? '' : 'none';
6959     },
6960     
6961     
6962     getSelectionModel : function(){
6963         if(!this.selModel){
6964             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6965         }
6966         return this.selModel;
6967     },
6968     /*
6969      * Render the Roo.bootstrap object from renderder
6970      */
6971     renderCellObject : function(r)
6972     {
6973         var _this = this;
6974         
6975         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6976         
6977         var t = r.cfg.render(r.container);
6978         
6979         if(r.cfg.cn){
6980             Roo.each(r.cfg.cn, function(c){
6981                 var child = {
6982                     container: t.getChildContainer(),
6983                     cfg: c
6984                 };
6985                 _this.renderCellObject(child);
6986             })
6987         }
6988     },
6989     
6990     getRowIndex : function(row)
6991     {
6992         var rowIndex = -1;
6993         
6994         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6995             if(el != row){
6996                 return;
6997             }
6998             
6999             rowIndex = index;
7000         });
7001         
7002         return rowIndex;
7003     },
7004      /**
7005      * Returns the grid's underlying element = used by panel.Grid
7006      * @return {Element} The element
7007      */
7008     getGridEl : function(){
7009         return this.el;
7010     },
7011      /**
7012      * Forces a resize - used by panel.Grid
7013      * @return {Element} The element
7014      */
7015     autoSize : function()
7016     {
7017         //var ctr = Roo.get(this.container.dom.parentElement);
7018         var ctr = Roo.get(this.el.dom);
7019         
7020         var thd = this.getGridEl().select('thead',true).first();
7021         var tbd = this.getGridEl().select('tbody', true).first();
7022         var tfd = this.getGridEl().select('tfoot', true).first();
7023         
7024         var cw = ctr.getWidth();
7025         
7026         if (tbd) {
7027             
7028             tbd.setSize(ctr.getWidth(),
7029                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7030             );
7031             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7032             cw -= barsize;
7033         }
7034         cw = Math.max(cw, this.totalWidth);
7035         this.getGridEl().select('tr',true).setWidth(cw);
7036         // resize 'expandable coloumn?
7037         
7038         return; // we doe not have a view in this design..
7039         
7040     },
7041     onBodyScroll: function()
7042     {
7043         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7044         if(this.mainHead){
7045             this.mainHead.setStyle({
7046                 'position' : 'relative',
7047                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7048             });
7049         }
7050         
7051         if(this.lazyLoad){
7052             
7053             var scrollHeight = this.mainBody.dom.scrollHeight;
7054             
7055             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7056             
7057             var height = this.mainBody.getHeight();
7058             
7059             if(scrollHeight - height == scrollTop) {
7060                 
7061                 var total = this.ds.getTotalCount();
7062                 
7063                 if(this.footer.cursor + this.footer.pageSize < total){
7064                     
7065                     this.footer.ds.load({
7066                         params : {
7067                             start : this.footer.cursor + this.footer.pageSize,
7068                             limit : this.footer.pageSize
7069                         },
7070                         add : true
7071                     });
7072                 }
7073             }
7074             
7075         }
7076     },
7077     
7078     onHeaderChange : function()
7079     {
7080         var header = this.renderHeader();
7081         var table = this.el.select('table', true).first();
7082         
7083         this.mainHead.remove();
7084         this.mainHead = table.createChild(header, this.mainBody, false);
7085     },
7086     
7087     onHiddenChange : function(colModel, colIndex, hidden)
7088     {
7089         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7091         
7092         this.CSS.updateRule(thSelector, "display", "");
7093         this.CSS.updateRule(tdSelector, "display", "");
7094         
7095         if(hidden){
7096             this.CSS.updateRule(thSelector, "display", "none");
7097             this.CSS.updateRule(tdSelector, "display", "none");
7098         }
7099         
7100         this.onHeaderChange();
7101         this.onLoad();
7102     },
7103     
7104     setColumnWidth: function(col_index, width)
7105     {
7106         // width = "md-2 xs-2..."
7107         if(!this.colModel.config[col_index]) {
7108             return;
7109         }
7110         
7111         var w = width.split(" ");
7112         
7113         Roo.log(w);
7114     }
7115 });
7116
7117  
7118
7119  /*
7120  * - LGPL
7121  *
7122  * table cell
7123  * 
7124  */
7125
7126 /**
7127  * @class Roo.bootstrap.TableCell
7128  * @extends Roo.bootstrap.Component
7129  * Bootstrap TableCell class
7130  * @cfg {String} html cell contain text
7131  * @cfg {String} cls cell class
7132  * @cfg {String} tag cell tag (td|th) default td
7133  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7134  * @cfg {String} align Aligns the content in a cell
7135  * @cfg {String} axis Categorizes cells
7136  * @cfg {String} bgcolor Specifies the background color of a cell
7137  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7138  * @cfg {Number} colspan Specifies the number of columns a cell should span
7139  * @cfg {String} headers Specifies one or more header cells a cell is related to
7140  * @cfg {Number} height Sets the height of a cell
7141  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7142  * @cfg {Number} rowspan Sets the number of rows a cell should span
7143  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7144  * @cfg {String} valign Vertical aligns the content in a cell
7145  * @cfg {Number} width Specifies the width of a cell
7146  * 
7147  * @constructor
7148  * Create a new TableCell
7149  * @param {Object} config The config object
7150  */
7151
7152 Roo.bootstrap.TableCell = function(config){
7153     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7154 };
7155
7156 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7157     
7158     html: false,
7159     cls: false,
7160     tag: false,
7161     abbr: false,
7162     align: false,
7163     axis: false,
7164     bgcolor: false,
7165     charoff: false,
7166     colspan: false,
7167     headers: false,
7168     height: false,
7169     nowrap: false,
7170     rowspan: false,
7171     scope: false,
7172     valign: false,
7173     width: false,
7174     
7175     
7176     getAutoCreate : function(){
7177         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7178         
7179         cfg = {
7180             tag: 'td'
7181         };
7182         
7183         if(this.tag){
7184             cfg.tag = this.tag;
7185         }
7186         
7187         if (this.html) {
7188             cfg.html=this.html
7189         }
7190         if (this.cls) {
7191             cfg.cls=this.cls
7192         }
7193         if (this.abbr) {
7194             cfg.abbr=this.abbr
7195         }
7196         if (this.align) {
7197             cfg.align=this.align
7198         }
7199         if (this.axis) {
7200             cfg.axis=this.axis
7201         }
7202         if (this.bgcolor) {
7203             cfg.bgcolor=this.bgcolor
7204         }
7205         if (this.charoff) {
7206             cfg.charoff=this.charoff
7207         }
7208         if (this.colspan) {
7209             cfg.colspan=this.colspan
7210         }
7211         if (this.headers) {
7212             cfg.headers=this.headers
7213         }
7214         if (this.height) {
7215             cfg.height=this.height
7216         }
7217         if (this.nowrap) {
7218             cfg.nowrap=this.nowrap
7219         }
7220         if (this.rowspan) {
7221             cfg.rowspan=this.rowspan
7222         }
7223         if (this.scope) {
7224             cfg.scope=this.scope
7225         }
7226         if (this.valign) {
7227             cfg.valign=this.valign
7228         }
7229         if (this.width) {
7230             cfg.width=this.width
7231         }
7232         
7233         
7234         return cfg;
7235     }
7236    
7237 });
7238
7239  
7240
7241  /*
7242  * - LGPL
7243  *
7244  * table row
7245  * 
7246  */
7247
7248 /**
7249  * @class Roo.bootstrap.TableRow
7250  * @extends Roo.bootstrap.Component
7251  * Bootstrap TableRow class
7252  * @cfg {String} cls row class
7253  * @cfg {String} align Aligns the content in a table row
7254  * @cfg {String} bgcolor Specifies a background color for a table row
7255  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7256  * @cfg {String} valign Vertical aligns the content in a table row
7257  * 
7258  * @constructor
7259  * Create a new TableRow
7260  * @param {Object} config The config object
7261  */
7262
7263 Roo.bootstrap.TableRow = function(config){
7264     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7265 };
7266
7267 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7268     
7269     cls: false,
7270     align: false,
7271     bgcolor: false,
7272     charoff: false,
7273     valign: false,
7274     
7275     getAutoCreate : function(){
7276         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7277         
7278         cfg = {
7279             tag: 'tr'
7280         };
7281             
7282         if(this.cls){
7283             cfg.cls = this.cls;
7284         }
7285         if(this.align){
7286             cfg.align = this.align;
7287         }
7288         if(this.bgcolor){
7289             cfg.bgcolor = this.bgcolor;
7290         }
7291         if(this.charoff){
7292             cfg.charoff = this.charoff;
7293         }
7294         if(this.valign){
7295             cfg.valign = this.valign;
7296         }
7297         
7298         return cfg;
7299     }
7300    
7301 });
7302
7303  
7304
7305  /*
7306  * - LGPL
7307  *
7308  * table body
7309  * 
7310  */
7311
7312 /**
7313  * @class Roo.bootstrap.TableBody
7314  * @extends Roo.bootstrap.Component
7315  * Bootstrap TableBody class
7316  * @cfg {String} cls element class
7317  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7318  * @cfg {String} align Aligns the content inside the element
7319  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7320  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7321  * 
7322  * @constructor
7323  * Create a new TableBody
7324  * @param {Object} config The config object
7325  */
7326
7327 Roo.bootstrap.TableBody = function(config){
7328     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7329 };
7330
7331 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7332     
7333     cls: false,
7334     tag: false,
7335     align: false,
7336     charoff: false,
7337     valign: false,
7338     
7339     getAutoCreate : function(){
7340         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7341         
7342         cfg = {
7343             tag: 'tbody'
7344         };
7345             
7346         if (this.cls) {
7347             cfg.cls=this.cls
7348         }
7349         if(this.tag){
7350             cfg.tag = this.tag;
7351         }
7352         
7353         if(this.align){
7354             cfg.align = this.align;
7355         }
7356         if(this.charoff){
7357             cfg.charoff = this.charoff;
7358         }
7359         if(this.valign){
7360             cfg.valign = this.valign;
7361         }
7362         
7363         return cfg;
7364     }
7365     
7366     
7367 //    initEvents : function()
7368 //    {
7369 //        
7370 //        if(!this.store){
7371 //            return;
7372 //        }
7373 //        
7374 //        this.store = Roo.factory(this.store, Roo.data);
7375 //        this.store.on('load', this.onLoad, this);
7376 //        
7377 //        this.store.load();
7378 //        
7379 //    },
7380 //    
7381 //    onLoad: function () 
7382 //    {   
7383 //        this.fireEvent('load', this);
7384 //    }
7385 //    
7386 //   
7387 });
7388
7389  
7390
7391  /*
7392  * Based on:
7393  * Ext JS Library 1.1.1
7394  * Copyright(c) 2006-2007, Ext JS, LLC.
7395  *
7396  * Originally Released Under LGPL - original licence link has changed is not relivant.
7397  *
7398  * Fork - LGPL
7399  * <script type="text/javascript">
7400  */
7401
7402 // as we use this in bootstrap.
7403 Roo.namespace('Roo.form');
7404  /**
7405  * @class Roo.form.Action
7406  * Internal Class used to handle form actions
7407  * @constructor
7408  * @param {Roo.form.BasicForm} el The form element or its id
7409  * @param {Object} config Configuration options
7410  */
7411
7412  
7413  
7414 // define the action interface
7415 Roo.form.Action = function(form, options){
7416     this.form = form;
7417     this.options = options || {};
7418 };
7419 /**
7420  * Client Validation Failed
7421  * @const 
7422  */
7423 Roo.form.Action.CLIENT_INVALID = 'client';
7424 /**
7425  * Server Validation Failed
7426  * @const 
7427  */
7428 Roo.form.Action.SERVER_INVALID = 'server';
7429  /**
7430  * Connect to Server Failed
7431  * @const 
7432  */
7433 Roo.form.Action.CONNECT_FAILURE = 'connect';
7434 /**
7435  * Reading Data from Server Failed
7436  * @const 
7437  */
7438 Roo.form.Action.LOAD_FAILURE = 'load';
7439
7440 Roo.form.Action.prototype = {
7441     type : 'default',
7442     failureType : undefined,
7443     response : undefined,
7444     result : undefined,
7445
7446     // interface method
7447     run : function(options){
7448
7449     },
7450
7451     // interface method
7452     success : function(response){
7453
7454     },
7455
7456     // interface method
7457     handleResponse : function(response){
7458
7459     },
7460
7461     // default connection failure
7462     failure : function(response){
7463         
7464         this.response = response;
7465         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7466         this.form.afterAction(this, false);
7467     },
7468
7469     processResponse : function(response){
7470         this.response = response;
7471         if(!response.responseText){
7472             return true;
7473         }
7474         this.result = this.handleResponse(response);
7475         return this.result;
7476     },
7477
7478     // utility functions used internally
7479     getUrl : function(appendParams){
7480         var url = this.options.url || this.form.url || this.form.el.dom.action;
7481         if(appendParams){
7482             var p = this.getParams();
7483             if(p){
7484                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7485             }
7486         }
7487         return url;
7488     },
7489
7490     getMethod : function(){
7491         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7492     },
7493
7494     getParams : function(){
7495         var bp = this.form.baseParams;
7496         var p = this.options.params;
7497         if(p){
7498             if(typeof p == "object"){
7499                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7500             }else if(typeof p == 'string' && bp){
7501                 p += '&' + Roo.urlEncode(bp);
7502             }
7503         }else if(bp){
7504             p = Roo.urlEncode(bp);
7505         }
7506         return p;
7507     },
7508
7509     createCallback : function(){
7510         return {
7511             success: this.success,
7512             failure: this.failure,
7513             scope: this,
7514             timeout: (this.form.timeout*1000),
7515             upload: this.form.fileUpload ? this.success : undefined
7516         };
7517     }
7518 };
7519
7520 Roo.form.Action.Submit = function(form, options){
7521     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7522 };
7523
7524 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7525     type : 'submit',
7526
7527     haveProgress : false,
7528     uploadComplete : false,
7529     
7530     // uploadProgress indicator.
7531     uploadProgress : function()
7532     {
7533         if (!this.form.progressUrl) {
7534             return;
7535         }
7536         
7537         if (!this.haveProgress) {
7538             Roo.MessageBox.progress("Uploading", "Uploading");
7539         }
7540         if (this.uploadComplete) {
7541            Roo.MessageBox.hide();
7542            return;
7543         }
7544         
7545         this.haveProgress = true;
7546    
7547         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7548         
7549         var c = new Roo.data.Connection();
7550         c.request({
7551             url : this.form.progressUrl,
7552             params: {
7553                 id : uid
7554             },
7555             method: 'GET',
7556             success : function(req){
7557                //console.log(data);
7558                 var rdata = false;
7559                 var edata;
7560                 try  {
7561                    rdata = Roo.decode(req.responseText)
7562                 } catch (e) {
7563                     Roo.log("Invalid data from server..");
7564                     Roo.log(edata);
7565                     return;
7566                 }
7567                 if (!rdata || !rdata.success) {
7568                     Roo.log(rdata);
7569                     Roo.MessageBox.alert(Roo.encode(rdata));
7570                     return;
7571                 }
7572                 var data = rdata.data;
7573                 
7574                 if (this.uploadComplete) {
7575                    Roo.MessageBox.hide();
7576                    return;
7577                 }
7578                    
7579                 if (data){
7580                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7581                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7582                     );
7583                 }
7584                 this.uploadProgress.defer(2000,this);
7585             },
7586        
7587             failure: function(data) {
7588                 Roo.log('progress url failed ');
7589                 Roo.log(data);
7590             },
7591             scope : this
7592         });
7593            
7594     },
7595     
7596     
7597     run : function()
7598     {
7599         // run get Values on the form, so it syncs any secondary forms.
7600         this.form.getValues();
7601         
7602         var o = this.options;
7603         var method = this.getMethod();
7604         var isPost = method == 'POST';
7605         if(o.clientValidation === false || this.form.isValid()){
7606             
7607             if (this.form.progressUrl) {
7608                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7609                     (new Date() * 1) + '' + Math.random());
7610                     
7611             } 
7612             
7613             
7614             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7615                 form:this.form.el.dom,
7616                 url:this.getUrl(!isPost),
7617                 method: method,
7618                 params:isPost ? this.getParams() : null,
7619                 isUpload: this.form.fileUpload
7620             }));
7621             
7622             this.uploadProgress();
7623
7624         }else if (o.clientValidation !== false){ // client validation failed
7625             this.failureType = Roo.form.Action.CLIENT_INVALID;
7626             this.form.afterAction(this, false);
7627         }
7628     },
7629
7630     success : function(response)
7631     {
7632         this.uploadComplete= true;
7633         if (this.haveProgress) {
7634             Roo.MessageBox.hide();
7635         }
7636         
7637         
7638         var result = this.processResponse(response);
7639         if(result === true || result.success){
7640             this.form.afterAction(this, true);
7641             return;
7642         }
7643         if(result.errors){
7644             this.form.markInvalid(result.errors);
7645             this.failureType = Roo.form.Action.SERVER_INVALID;
7646         }
7647         this.form.afterAction(this, false);
7648     },
7649     failure : function(response)
7650     {
7651         this.uploadComplete= true;
7652         if (this.haveProgress) {
7653             Roo.MessageBox.hide();
7654         }
7655         
7656         this.response = response;
7657         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7658         this.form.afterAction(this, false);
7659     },
7660     
7661     handleResponse : function(response){
7662         if(this.form.errorReader){
7663             var rs = this.form.errorReader.read(response);
7664             var errors = [];
7665             if(rs.records){
7666                 for(var i = 0, len = rs.records.length; i < len; i++) {
7667                     var r = rs.records[i];
7668                     errors[i] = r.data;
7669                 }
7670             }
7671             if(errors.length < 1){
7672                 errors = null;
7673             }
7674             return {
7675                 success : rs.success,
7676                 errors : errors
7677             };
7678         }
7679         var ret = false;
7680         try {
7681             ret = Roo.decode(response.responseText);
7682         } catch (e) {
7683             ret = {
7684                 success: false,
7685                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7686                 errors : []
7687             };
7688         }
7689         return ret;
7690         
7691     }
7692 });
7693
7694
7695 Roo.form.Action.Load = function(form, options){
7696     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7697     this.reader = this.form.reader;
7698 };
7699
7700 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7701     type : 'load',
7702
7703     run : function(){
7704         
7705         Roo.Ajax.request(Roo.apply(
7706                 this.createCallback(), {
7707                     method:this.getMethod(),
7708                     url:this.getUrl(false),
7709                     params:this.getParams()
7710         }));
7711     },
7712
7713     success : function(response){
7714         
7715         var result = this.processResponse(response);
7716         if(result === true || !result.success || !result.data){
7717             this.failureType = Roo.form.Action.LOAD_FAILURE;
7718             this.form.afterAction(this, false);
7719             return;
7720         }
7721         this.form.clearInvalid();
7722         this.form.setValues(result.data);
7723         this.form.afterAction(this, true);
7724     },
7725
7726     handleResponse : function(response){
7727         if(this.form.reader){
7728             var rs = this.form.reader.read(response);
7729             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7730             return {
7731                 success : rs.success,
7732                 data : data
7733             };
7734         }
7735         return Roo.decode(response.responseText);
7736     }
7737 });
7738
7739 Roo.form.Action.ACTION_TYPES = {
7740     'load' : Roo.form.Action.Load,
7741     'submit' : Roo.form.Action.Submit
7742 };/*
7743  * - LGPL
7744  *
7745  * form
7746  *
7747  */
7748
7749 /**
7750  * @class Roo.bootstrap.Form
7751  * @extends Roo.bootstrap.Component
7752  * Bootstrap Form class
7753  * @cfg {String} method  GET | POST (default POST)
7754  * @cfg {String} labelAlign top | left (default top)
7755  * @cfg {String} align left  | right - for navbars
7756  * @cfg {Boolean} loadMask load mask when submit (default true)
7757
7758  *
7759  * @constructor
7760  * Create a new Form
7761  * @param {Object} config The config object
7762  */
7763
7764
7765 Roo.bootstrap.Form = function(config){
7766     
7767     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7768     
7769     Roo.bootstrap.Form.popover.apply();
7770     
7771     this.addEvents({
7772         /**
7773          * @event clientvalidation
7774          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7775          * @param {Form} this
7776          * @param {Boolean} valid true if the form has passed client-side validation
7777          */
7778         clientvalidation: true,
7779         /**
7780          * @event beforeaction
7781          * Fires before any action is performed. Return false to cancel the action.
7782          * @param {Form} this
7783          * @param {Action} action The action to be performed
7784          */
7785         beforeaction: true,
7786         /**
7787          * @event actionfailed
7788          * Fires when an action fails.
7789          * @param {Form} this
7790          * @param {Action} action The action that failed
7791          */
7792         actionfailed : true,
7793         /**
7794          * @event actioncomplete
7795          * Fires when an action is completed.
7796          * @param {Form} this
7797          * @param {Action} action The action that completed
7798          */
7799         actioncomplete : true
7800     });
7801 };
7802
7803 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7804
7805      /**
7806      * @cfg {String} method
7807      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7808      */
7809     method : 'POST',
7810     /**
7811      * @cfg {String} url
7812      * The URL to use for form actions if one isn't supplied in the action options.
7813      */
7814     /**
7815      * @cfg {Boolean} fileUpload
7816      * Set to true if this form is a file upload.
7817      */
7818
7819     /**
7820      * @cfg {Object} baseParams
7821      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7822      */
7823
7824     /**
7825      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7826      */
7827     timeout: 30,
7828     /**
7829      * @cfg {Sting} align (left|right) for navbar forms
7830      */
7831     align : 'left',
7832
7833     // private
7834     activeAction : null,
7835
7836     /**
7837      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7838      * element by passing it or its id or mask the form itself by passing in true.
7839      * @type Mixed
7840      */
7841     waitMsgTarget : false,
7842
7843     loadMask : true,
7844     
7845     /**
7846      * @cfg {Boolean} errorMask (true|false) default false
7847      */
7848     errorMask : false,
7849     
7850     /**
7851      * @cfg {Number} maskOffset Default 100
7852      */
7853     maskOffset : 100,
7854     
7855     /**
7856      * @cfg {Boolean} maskBody
7857      */
7858     maskBody : false,
7859
7860     getAutoCreate : function(){
7861
7862         var cfg = {
7863             tag: 'form',
7864             method : this.method || 'POST',
7865             id : this.id || Roo.id(),
7866             cls : ''
7867         };
7868         if (this.parent().xtype.match(/^Nav/)) {
7869             cfg.cls = 'navbar-form navbar-' + this.align;
7870
7871         }
7872
7873         if (this.labelAlign == 'left' ) {
7874             cfg.cls += ' form-horizontal';
7875         }
7876
7877
7878         return cfg;
7879     },
7880     initEvents : function()
7881     {
7882         this.el.on('submit', this.onSubmit, this);
7883         // this was added as random key presses on the form where triggering form submit.
7884         this.el.on('keypress', function(e) {
7885             if (e.getCharCode() != 13) {
7886                 return true;
7887             }
7888             // we might need to allow it for textareas.. and some other items.
7889             // check e.getTarget().
7890
7891             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7892                 return true;
7893             }
7894
7895             Roo.log("keypress blocked");
7896
7897             e.preventDefault();
7898             return false;
7899         });
7900         
7901     },
7902     // private
7903     onSubmit : function(e){
7904         e.stopEvent();
7905     },
7906
7907      /**
7908      * Returns true if client-side validation on the form is successful.
7909      * @return Boolean
7910      */
7911     isValid : function(){
7912         var items = this.getItems();
7913         var valid = true;
7914         var target = false;
7915         
7916         items.each(function(f){
7917             
7918             if(f.validate()){
7919                 return;
7920             }
7921             
7922             Roo.log('invalid field: ' + f.name);
7923             
7924             valid = false;
7925
7926             if(!target && f.el.isVisible(true)){
7927                 target = f;
7928             }
7929            
7930         });
7931         
7932         if(this.errorMask && !valid){
7933             Roo.bootstrap.Form.popover.mask(this, target);
7934         }
7935         
7936         return valid;
7937     },
7938     
7939     /**
7940      * Returns true if any fields in this form have changed since their original load.
7941      * @return Boolean
7942      */
7943     isDirty : function(){
7944         var dirty = false;
7945         var items = this.getItems();
7946         items.each(function(f){
7947            if(f.isDirty()){
7948                dirty = true;
7949                return false;
7950            }
7951            return true;
7952         });
7953         return dirty;
7954     },
7955      /**
7956      * Performs a predefined action (submit or load) or custom actions you define on this form.
7957      * @param {String} actionName The name of the action type
7958      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7959      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7960      * accept other config options):
7961      * <pre>
7962 Property          Type             Description
7963 ----------------  ---------------  ----------------------------------------------------------------------------------
7964 url               String           The url for the action (defaults to the form's url)
7965 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7966 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7967 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7968                                    validate the form on the client (defaults to false)
7969      * </pre>
7970      * @return {BasicForm} this
7971      */
7972     doAction : function(action, options){
7973         if(typeof action == 'string'){
7974             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7975         }
7976         if(this.fireEvent('beforeaction', this, action) !== false){
7977             this.beforeAction(action);
7978             action.run.defer(100, action);
7979         }
7980         return this;
7981     },
7982
7983     // private
7984     beforeAction : function(action){
7985         var o = action.options;
7986         
7987         if(this.loadMask){
7988             
7989             if(this.maskBody){
7990                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7991             } else {
7992                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7993             }
7994         }
7995         // not really supported yet.. ??
7996
7997         //if(this.waitMsgTarget === true){
7998         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7999         //}else if(this.waitMsgTarget){
8000         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8001         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8002         //}else {
8003         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8004        // }
8005
8006     },
8007
8008     // private
8009     afterAction : function(action, success){
8010         this.activeAction = null;
8011         var o = action.options;
8012
8013         if(this.loadMask){
8014             
8015             if(this.maskBody){
8016                 Roo.get(document.body).unmask();
8017             } else {
8018                 this.el.unmask();
8019             }
8020         }
8021         
8022         //if(this.waitMsgTarget === true){
8023 //            this.el.unmask();
8024         //}else if(this.waitMsgTarget){
8025         //    this.waitMsgTarget.unmask();
8026         //}else{
8027         //    Roo.MessageBox.updateProgress(1);
8028         //    Roo.MessageBox.hide();
8029        // }
8030         //
8031         if(success){
8032             if(o.reset){
8033                 this.reset();
8034             }
8035             Roo.callback(o.success, o.scope, [this, action]);
8036             this.fireEvent('actioncomplete', this, action);
8037
8038         }else{
8039
8040             // failure condition..
8041             // we have a scenario where updates need confirming.
8042             // eg. if a locking scenario exists..
8043             // we look for { errors : { needs_confirm : true }} in the response.
8044             if (
8045                 (typeof(action.result) != 'undefined')  &&
8046                 (typeof(action.result.errors) != 'undefined')  &&
8047                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8048            ){
8049                 var _t = this;
8050                 Roo.log("not supported yet");
8051                  /*
8052
8053                 Roo.MessageBox.confirm(
8054                     "Change requires confirmation",
8055                     action.result.errorMsg,
8056                     function(r) {
8057                         if (r != 'yes') {
8058                             return;
8059                         }
8060                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8061                     }
8062
8063                 );
8064                 */
8065
8066
8067                 return;
8068             }
8069
8070             Roo.callback(o.failure, o.scope, [this, action]);
8071             // show an error message if no failed handler is set..
8072             if (!this.hasListener('actionfailed')) {
8073                 Roo.log("need to add dialog support");
8074                 /*
8075                 Roo.MessageBox.alert("Error",
8076                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8077                         action.result.errorMsg :
8078                         "Saving Failed, please check your entries or try again"
8079                 );
8080                 */
8081             }
8082
8083             this.fireEvent('actionfailed', this, action);
8084         }
8085
8086     },
8087     /**
8088      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8089      * @param {String} id The value to search for
8090      * @return Field
8091      */
8092     findField : function(id){
8093         var items = this.getItems();
8094         var field = items.get(id);
8095         if(!field){
8096              items.each(function(f){
8097                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8098                     field = f;
8099                     return false;
8100                 }
8101                 return true;
8102             });
8103         }
8104         return field || null;
8105     },
8106      /**
8107      * Mark fields in this form invalid in bulk.
8108      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8109      * @return {BasicForm} this
8110      */
8111     markInvalid : function(errors){
8112         if(errors instanceof Array){
8113             for(var i = 0, len = errors.length; i < len; i++){
8114                 var fieldError = errors[i];
8115                 var f = this.findField(fieldError.id);
8116                 if(f){
8117                     f.markInvalid(fieldError.msg);
8118                 }
8119             }
8120         }else{
8121             var field, id;
8122             for(id in errors){
8123                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8124                     field.markInvalid(errors[id]);
8125                 }
8126             }
8127         }
8128         //Roo.each(this.childForms || [], function (f) {
8129         //    f.markInvalid(errors);
8130         //});
8131
8132         return this;
8133     },
8134
8135     /**
8136      * Set values for fields in this form in bulk.
8137      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8138      * @return {BasicForm} this
8139      */
8140     setValues : function(values){
8141         if(values instanceof Array){ // array of objects
8142             for(var i = 0, len = values.length; i < len; i++){
8143                 var v = values[i];
8144                 var f = this.findField(v.id);
8145                 if(f){
8146                     f.setValue(v.value);
8147                     if(this.trackResetOnLoad){
8148                         f.originalValue = f.getValue();
8149                     }
8150                 }
8151             }
8152         }else{ // object hash
8153             var field, id;
8154             for(id in values){
8155                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8156
8157                     if (field.setFromData &&
8158                         field.valueField &&
8159                         field.displayField &&
8160                         // combos' with local stores can
8161                         // be queried via setValue()
8162                         // to set their value..
8163                         (field.store && !field.store.isLocal)
8164                         ) {
8165                         // it's a combo
8166                         var sd = { };
8167                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8168                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8169                         field.setFromData(sd);
8170
8171                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8172                         
8173                         field.setFromData(values);
8174                         
8175                     } else {
8176                         field.setValue(values[id]);
8177                     }
8178
8179
8180                     if(this.trackResetOnLoad){
8181                         field.originalValue = field.getValue();
8182                     }
8183                 }
8184             }
8185         }
8186
8187         //Roo.each(this.childForms || [], function (f) {
8188         //    f.setValues(values);
8189         //});
8190
8191         return this;
8192     },
8193
8194     /**
8195      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8196      * they are returned as an array.
8197      * @param {Boolean} asString
8198      * @return {Object}
8199      */
8200     getValues : function(asString){
8201         //if (this.childForms) {
8202             // copy values from the child forms
8203         //    Roo.each(this.childForms, function (f) {
8204         //        this.setValues(f.getValues());
8205         //    }, this);
8206         //}
8207
8208
8209
8210         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8211         if(asString === true){
8212             return fs;
8213         }
8214         return Roo.urlDecode(fs);
8215     },
8216
8217     /**
8218      * Returns the fields in this form as an object with key/value pairs.
8219      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8220      * @return {Object}
8221      */
8222     getFieldValues : function(with_hidden)
8223     {
8224         var items = this.getItems();
8225         var ret = {};
8226         items.each(function(f){
8227             
8228             if (!f.getName()) {
8229                 return;
8230             }
8231             
8232             var v = f.getValue();
8233             
8234             if (f.inputType =='radio') {
8235                 if (typeof(ret[f.getName()]) == 'undefined') {
8236                     ret[f.getName()] = ''; // empty..
8237                 }
8238
8239                 if (!f.el.dom.checked) {
8240                     return;
8241
8242                 }
8243                 v = f.el.dom.value;
8244
8245             }
8246             
8247             if(f.xtype == 'MoneyField'){
8248                 ret[f.currencyName] = f.getCurrency();
8249             }
8250
8251             // not sure if this supported any more..
8252             if ((typeof(v) == 'object') && f.getRawValue) {
8253                 v = f.getRawValue() ; // dates..
8254             }
8255             // combo boxes where name != hiddenName...
8256             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8257                 ret[f.name] = f.getRawValue();
8258             }
8259             ret[f.getName()] = v;
8260         });
8261
8262         return ret;
8263     },
8264
8265     /**
8266      * Clears all invalid messages in this form.
8267      * @return {BasicForm} this
8268      */
8269     clearInvalid : function(){
8270         var items = this.getItems();
8271
8272         items.each(function(f){
8273            f.clearInvalid();
8274         });
8275
8276         return this;
8277     },
8278
8279     /**
8280      * Resets this form.
8281      * @return {BasicForm} this
8282      */
8283     reset : function(){
8284         var items = this.getItems();
8285         items.each(function(f){
8286             f.reset();
8287         });
8288
8289         Roo.each(this.childForms || [], function (f) {
8290             f.reset();
8291         });
8292
8293
8294         return this;
8295     },
8296     
8297     getItems : function()
8298     {
8299         var r=new Roo.util.MixedCollection(false, function(o){
8300             return o.id || (o.id = Roo.id());
8301         });
8302         var iter = function(el) {
8303             if (el.inputEl) {
8304                 r.add(el);
8305             }
8306             if (!el.items) {
8307                 return;
8308             }
8309             Roo.each(el.items,function(e) {
8310                 iter(e);
8311             });
8312         };
8313
8314         iter(this);
8315         return r;
8316     },
8317     
8318     hideFields : function(items)
8319     {
8320         Roo.each(items, function(i){
8321             
8322             var f = this.findField(i);
8323             
8324             if(!f){
8325                 return;
8326             }
8327             
8328             f.hide();
8329             
8330         }, this);
8331     },
8332     
8333     showFields : function(items)
8334     {
8335         Roo.each(items, function(i){
8336             
8337             var f = this.findField(i);
8338             
8339             if(!f){
8340                 return;
8341             }
8342             
8343             f.show();
8344             
8345         }, this);
8346     }
8347
8348 });
8349
8350 Roo.apply(Roo.bootstrap.Form, {
8351     
8352     popover : {
8353         
8354         padding : 5,
8355         
8356         isApplied : false,
8357         
8358         isMasked : false,
8359         
8360         form : false,
8361         
8362         target : false,
8363         
8364         toolTip : false,
8365         
8366         intervalID : false,
8367         
8368         maskEl : false,
8369         
8370         apply : function()
8371         {
8372             if(this.isApplied){
8373                 return;
8374             }
8375             
8376             this.maskEl = {
8377                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8378                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8379                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8380                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8381             };
8382             
8383             this.maskEl.top.enableDisplayMode("block");
8384             this.maskEl.left.enableDisplayMode("block");
8385             this.maskEl.bottom.enableDisplayMode("block");
8386             this.maskEl.right.enableDisplayMode("block");
8387             
8388             this.toolTip = new Roo.bootstrap.Tooltip({
8389                 cls : 'roo-form-error-popover',
8390                 alignment : {
8391                     'left' : ['r-l', [-2,0], 'right'],
8392                     'right' : ['l-r', [2,0], 'left'],
8393                     'bottom' : ['tl-bl', [0,2], 'top'],
8394                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8395                 }
8396             });
8397             
8398             this.toolTip.render(Roo.get(document.body));
8399
8400             this.toolTip.el.enableDisplayMode("block");
8401             
8402             Roo.get(document.body).on('click', function(){
8403                 this.unmask();
8404             }, this);
8405             
8406             Roo.get(document.body).on('touchstart', function(){
8407                 this.unmask();
8408             }, this);
8409             
8410             this.isApplied = true
8411         },
8412         
8413         mask : function(form, target)
8414         {
8415             this.form = form;
8416             
8417             this.target = target;
8418             
8419             if(!this.form.errorMask || !target.el){
8420                 return;
8421             }
8422             
8423             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8424             
8425             Roo.log(scrollable);
8426             
8427             var ot = this.target.el.calcOffsetsTo(scrollable);
8428             
8429             var scrollTo = ot[1] - this.form.maskOffset;
8430             
8431             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8432             
8433             scrollable.scrollTo('top', scrollTo);
8434             
8435             var box = this.target.el.getBox();
8436             Roo.log(box);
8437             var zIndex = Roo.bootstrap.Modal.zIndex++;
8438
8439             
8440             this.maskEl.top.setStyle('position', 'absolute');
8441             this.maskEl.top.setStyle('z-index', zIndex);
8442             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8443             this.maskEl.top.setLeft(0);
8444             this.maskEl.top.setTop(0);
8445             this.maskEl.top.show();
8446             
8447             this.maskEl.left.setStyle('position', 'absolute');
8448             this.maskEl.left.setStyle('z-index', zIndex);
8449             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8450             this.maskEl.left.setLeft(0);
8451             this.maskEl.left.setTop(box.y - this.padding);
8452             this.maskEl.left.show();
8453
8454             this.maskEl.bottom.setStyle('position', 'absolute');
8455             this.maskEl.bottom.setStyle('z-index', zIndex);
8456             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8457             this.maskEl.bottom.setLeft(0);
8458             this.maskEl.bottom.setTop(box.bottom + this.padding);
8459             this.maskEl.bottom.show();
8460
8461             this.maskEl.right.setStyle('position', 'absolute');
8462             this.maskEl.right.setStyle('z-index', zIndex);
8463             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8464             this.maskEl.right.setLeft(box.right + this.padding);
8465             this.maskEl.right.setTop(box.y - this.padding);
8466             this.maskEl.right.show();
8467
8468             this.toolTip.bindEl = this.target.el;
8469
8470             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8471
8472             var tip = this.target.blankText;
8473
8474             if(this.target.getValue() !== '' ) {
8475                 
8476                 if (this.target.invalidText.length) {
8477                     tip = this.target.invalidText;
8478                 } else if (this.target.regexText.length){
8479                     tip = this.target.regexText;
8480                 }
8481             }
8482
8483             this.toolTip.show(tip);
8484
8485             this.intervalID = window.setInterval(function() {
8486                 Roo.bootstrap.Form.popover.unmask();
8487             }, 10000);
8488
8489             window.onwheel = function(){ return false;};
8490             
8491             (function(){ this.isMasked = true; }).defer(500, this);
8492             
8493         },
8494         
8495         unmask : function()
8496         {
8497             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8498                 return;
8499             }
8500             
8501             this.maskEl.top.setStyle('position', 'absolute');
8502             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8503             this.maskEl.top.hide();
8504
8505             this.maskEl.left.setStyle('position', 'absolute');
8506             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8507             this.maskEl.left.hide();
8508
8509             this.maskEl.bottom.setStyle('position', 'absolute');
8510             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8511             this.maskEl.bottom.hide();
8512
8513             this.maskEl.right.setStyle('position', 'absolute');
8514             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8515             this.maskEl.right.hide();
8516             
8517             this.toolTip.hide();
8518             
8519             this.toolTip.el.hide();
8520             
8521             window.onwheel = function(){ return true;};
8522             
8523             if(this.intervalID){
8524                 window.clearInterval(this.intervalID);
8525                 this.intervalID = false;
8526             }
8527             
8528             this.isMasked = false;
8529             
8530         }
8531         
8532     }
8533     
8534 });
8535
8536 /*
8537  * Based on:
8538  * Ext JS Library 1.1.1
8539  * Copyright(c) 2006-2007, Ext JS, LLC.
8540  *
8541  * Originally Released Under LGPL - original licence link has changed is not relivant.
8542  *
8543  * Fork - LGPL
8544  * <script type="text/javascript">
8545  */
8546 /**
8547  * @class Roo.form.VTypes
8548  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8549  * @singleton
8550  */
8551 Roo.form.VTypes = function(){
8552     // closure these in so they are only created once.
8553     var alpha = /^[a-zA-Z_]+$/;
8554     var alphanum = /^[a-zA-Z0-9_]+$/;
8555     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8556     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8557
8558     // All these messages and functions are configurable
8559     return {
8560         /**
8561          * The function used to validate email addresses
8562          * @param {String} value The email address
8563          */
8564         'email' : function(v){
8565             return email.test(v);
8566         },
8567         /**
8568          * The error text to display when the email validation function returns false
8569          * @type String
8570          */
8571         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8572         /**
8573          * The keystroke filter mask to be applied on email input
8574          * @type RegExp
8575          */
8576         'emailMask' : /[a-z0-9_\.\-@]/i,
8577
8578         /**
8579          * The function used to validate URLs
8580          * @param {String} value The URL
8581          */
8582         'url' : function(v){
8583             return url.test(v);
8584         },
8585         /**
8586          * The error text to display when the url validation function returns false
8587          * @type String
8588          */
8589         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8590         
8591         /**
8592          * The function used to validate alpha values
8593          * @param {String} value The value
8594          */
8595         'alpha' : function(v){
8596             return alpha.test(v);
8597         },
8598         /**
8599          * The error text to display when the alpha validation function returns false
8600          * @type String
8601          */
8602         'alphaText' : 'This field should only contain letters and _',
8603         /**
8604          * The keystroke filter mask to be applied on alpha input
8605          * @type RegExp
8606          */
8607         'alphaMask' : /[a-z_]/i,
8608
8609         /**
8610          * The function used to validate alphanumeric values
8611          * @param {String} value The value
8612          */
8613         'alphanum' : function(v){
8614             return alphanum.test(v);
8615         },
8616         /**
8617          * The error text to display when the alphanumeric validation function returns false
8618          * @type String
8619          */
8620         'alphanumText' : 'This field should only contain letters, numbers and _',
8621         /**
8622          * The keystroke filter mask to be applied on alphanumeric input
8623          * @type RegExp
8624          */
8625         'alphanumMask' : /[a-z0-9_]/i
8626     };
8627 }();/*
8628  * - LGPL
8629  *
8630  * Input
8631  * 
8632  */
8633
8634 /**
8635  * @class Roo.bootstrap.Input
8636  * @extends Roo.bootstrap.Component
8637  * Bootstrap Input class
8638  * @cfg {Boolean} disabled is it disabled
8639  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8640  * @cfg {String} name name of the input
8641  * @cfg {string} fieldLabel - the label associated
8642  * @cfg {string} placeholder - placeholder to put in text.
8643  * @cfg {string}  before - input group add on before
8644  * @cfg {string} after - input group add on after
8645  * @cfg {string} size - (lg|sm) or leave empty..
8646  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8647  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8648  * @cfg {Number} md colspan out of 12 for computer-sized screens
8649  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8650  * @cfg {string} value default value of the input
8651  * @cfg {Number} labelWidth set the width of label 
8652  * @cfg {Number} labellg set the width of label (1-12)
8653  * @cfg {Number} labelmd set the width of label (1-12)
8654  * @cfg {Number} labelsm set the width of label (1-12)
8655  * @cfg {Number} labelxs set the width of label (1-12)
8656  * @cfg {String} labelAlign (top|left)
8657  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8658  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8659  * @cfg {String} indicatorpos (left|right) default left
8660  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8661  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8662
8663  * @cfg {String} align (left|center|right) Default left
8664  * @cfg {Boolean} forceFeedback (true|false) Default false
8665  * 
8666  * @constructor
8667  * Create a new Input
8668  * @param {Object} config The config object
8669  */
8670
8671 Roo.bootstrap.Input = function(config){
8672     
8673     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8674     
8675     this.addEvents({
8676         /**
8677          * @event focus
8678          * Fires when this field receives input focus.
8679          * @param {Roo.form.Field} this
8680          */
8681         focus : true,
8682         /**
8683          * @event blur
8684          * Fires when this field loses input focus.
8685          * @param {Roo.form.Field} this
8686          */
8687         blur : true,
8688         /**
8689          * @event specialkey
8690          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8691          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8692          * @param {Roo.form.Field} this
8693          * @param {Roo.EventObject} e The event object
8694          */
8695         specialkey : true,
8696         /**
8697          * @event change
8698          * Fires just before the field blurs if the field value has changed.
8699          * @param {Roo.form.Field} this
8700          * @param {Mixed} newValue The new value
8701          * @param {Mixed} oldValue The original value
8702          */
8703         change : true,
8704         /**
8705          * @event invalid
8706          * Fires after the field has been marked as invalid.
8707          * @param {Roo.form.Field} this
8708          * @param {String} msg The validation message
8709          */
8710         invalid : true,
8711         /**
8712          * @event valid
8713          * Fires after the field has been validated with no errors.
8714          * @param {Roo.form.Field} this
8715          */
8716         valid : true,
8717          /**
8718          * @event keyup
8719          * Fires after the key up
8720          * @param {Roo.form.Field} this
8721          * @param {Roo.EventObject}  e The event Object
8722          */
8723         keyup : true
8724     });
8725 };
8726
8727 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8728      /**
8729      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8730       automatic validation (defaults to "keyup").
8731      */
8732     validationEvent : "keyup",
8733      /**
8734      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8735      */
8736     validateOnBlur : true,
8737     /**
8738      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8739      */
8740     validationDelay : 250,
8741      /**
8742      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8743      */
8744     focusClass : "x-form-focus",  // not needed???
8745     
8746        
8747     /**
8748      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8749      */
8750     invalidClass : "has-warning",
8751     
8752     /**
8753      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8754      */
8755     validClass : "has-success",
8756     
8757     /**
8758      * @cfg {Boolean} hasFeedback (true|false) default true
8759      */
8760     hasFeedback : true,
8761     
8762     /**
8763      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8764      */
8765     invalidFeedbackClass : "glyphicon-warning-sign",
8766     
8767     /**
8768      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8769      */
8770     validFeedbackClass : "glyphicon-ok",
8771     
8772     /**
8773      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8774      */
8775     selectOnFocus : false,
8776     
8777      /**
8778      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8779      */
8780     maskRe : null,
8781        /**
8782      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8783      */
8784     vtype : null,
8785     
8786       /**
8787      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8788      */
8789     disableKeyFilter : false,
8790     
8791        /**
8792      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8793      */
8794     disabled : false,
8795      /**
8796      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8797      */
8798     allowBlank : true,
8799     /**
8800      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8801      */
8802     blankText : "Please complete this mandatory field",
8803     
8804      /**
8805      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8806      */
8807     minLength : 0,
8808     /**
8809      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8810      */
8811     maxLength : Number.MAX_VALUE,
8812     /**
8813      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8814      */
8815     minLengthText : "The minimum length for this field is {0}",
8816     /**
8817      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8818      */
8819     maxLengthText : "The maximum length for this field is {0}",
8820   
8821     
8822     /**
8823      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8824      * If available, this function will be called only after the basic validators all return true, and will be passed the
8825      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8826      */
8827     validator : null,
8828     /**
8829      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8830      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8831      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8832      */
8833     regex : null,
8834     /**
8835      * @cfg {String} regexText -- Depricated - use Invalid Text
8836      */
8837     regexText : "",
8838     
8839     /**
8840      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8841      */
8842     invalidText : "",
8843     
8844     
8845     
8846     autocomplete: false,
8847     
8848     
8849     fieldLabel : '',
8850     inputType : 'text',
8851     
8852     name : false,
8853     placeholder: false,
8854     before : false,
8855     after : false,
8856     size : false,
8857     hasFocus : false,
8858     preventMark: false,
8859     isFormField : true,
8860     value : '',
8861     labelWidth : 2,
8862     labelAlign : false,
8863     readOnly : false,
8864     align : false,
8865     formatedValue : false,
8866     forceFeedback : false,
8867     
8868     indicatorpos : 'left',
8869     
8870     labellg : 0,
8871     labelmd : 0,
8872     labelsm : 0,
8873     labelxs : 0,
8874     
8875     capture : '',
8876     accept : '',
8877     
8878     parentLabelAlign : function()
8879     {
8880         var parent = this;
8881         while (parent.parent()) {
8882             parent = parent.parent();
8883             if (typeof(parent.labelAlign) !='undefined') {
8884                 return parent.labelAlign;
8885             }
8886         }
8887         return 'left';
8888         
8889     },
8890     
8891     getAutoCreate : function()
8892     {
8893         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8894         
8895         var id = Roo.id();
8896         
8897         var cfg = {};
8898         
8899         if(this.inputType != 'hidden'){
8900             cfg.cls = 'form-group' //input-group
8901         }
8902         
8903         var input =  {
8904             tag: 'input',
8905             id : id,
8906             type : this.inputType,
8907             value : this.value,
8908             cls : 'form-control',
8909             placeholder : this.placeholder || '',
8910             autocomplete : this.autocomplete || 'new-password'
8911         };
8912         
8913         if(this.capture.length){
8914             input.capture = this.capture;
8915         }
8916         
8917         if(this.accept.length){
8918             input.accept = this.accept + "/*";
8919         }
8920         
8921         if(this.align){
8922             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8923         }
8924         
8925         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8926             input.maxLength = this.maxLength;
8927         }
8928         
8929         if (this.disabled) {
8930             input.disabled=true;
8931         }
8932         
8933         if (this.readOnly) {
8934             input.readonly=true;
8935         }
8936         
8937         if (this.name) {
8938             input.name = this.name;
8939         }
8940         
8941         if (this.size) {
8942             input.cls += ' input-' + this.size;
8943         }
8944         
8945         var settings=this;
8946         ['xs','sm','md','lg'].map(function(size){
8947             if (settings[size]) {
8948                 cfg.cls += ' col-' + size + '-' + settings[size];
8949             }
8950         });
8951         
8952         var inputblock = input;
8953         
8954         var feedback = {
8955             tag: 'span',
8956             cls: 'glyphicon form-control-feedback'
8957         };
8958             
8959         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8960             
8961             inputblock = {
8962                 cls : 'has-feedback',
8963                 cn :  [
8964                     input,
8965                     feedback
8966                 ] 
8967             };  
8968         }
8969         
8970         if (this.before || this.after) {
8971             
8972             inputblock = {
8973                 cls : 'input-group',
8974                 cn :  [] 
8975             };
8976             
8977             if (this.before && typeof(this.before) == 'string') {
8978                 
8979                 inputblock.cn.push({
8980                     tag :'span',
8981                     cls : 'roo-input-before input-group-addon',
8982                     html : this.before
8983                 });
8984             }
8985             if (this.before && typeof(this.before) == 'object') {
8986                 this.before = Roo.factory(this.before);
8987                 
8988                 inputblock.cn.push({
8989                     tag :'span',
8990                     cls : 'roo-input-before input-group-' +
8991                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8992                 });
8993             }
8994             
8995             inputblock.cn.push(input);
8996             
8997             if (this.after && typeof(this.after) == 'string') {
8998                 inputblock.cn.push({
8999                     tag :'span',
9000                     cls : 'roo-input-after input-group-addon',
9001                     html : this.after
9002                 });
9003             }
9004             if (this.after && typeof(this.after) == 'object') {
9005                 this.after = Roo.factory(this.after);
9006                 
9007                 inputblock.cn.push({
9008                     tag :'span',
9009                     cls : 'roo-input-after input-group-' +
9010                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9011                 });
9012             }
9013             
9014             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9015                 inputblock.cls += ' has-feedback';
9016                 inputblock.cn.push(feedback);
9017             }
9018         };
9019         
9020         if (align ==='left' && this.fieldLabel.length) {
9021             
9022             cfg.cls += ' roo-form-group-label-left';
9023             
9024             cfg.cn = [
9025                 {
9026                     tag : 'i',
9027                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9028                     tooltip : 'This field is required'
9029                 },
9030                 {
9031                     tag: 'label',
9032                     'for' :  id,
9033                     cls : 'control-label',
9034                     html : this.fieldLabel
9035
9036                 },
9037                 {
9038                     cls : "", 
9039                     cn: [
9040                         inputblock
9041                     ]
9042                 }
9043             ];
9044             
9045             var labelCfg = cfg.cn[1];
9046             var contentCfg = cfg.cn[2];
9047             
9048             if(this.indicatorpos == 'right'){
9049                 cfg.cn = [
9050                     {
9051                         tag: 'label',
9052                         'for' :  id,
9053                         cls : 'control-label',
9054                         cn : [
9055                             {
9056                                 tag : 'span',
9057                                 html : this.fieldLabel
9058                             },
9059                             {
9060                                 tag : 'i',
9061                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9062                                 tooltip : 'This field is required'
9063                             }
9064                         ]
9065                     },
9066                     {
9067                         cls : "",
9068                         cn: [
9069                             inputblock
9070                         ]
9071                     }
9072
9073                 ];
9074                 
9075                 labelCfg = cfg.cn[0];
9076                 contentCfg = cfg.cn[1];
9077             
9078             }
9079             
9080             if(this.labelWidth > 12){
9081                 labelCfg.style = "width: " + this.labelWidth + 'px';
9082             }
9083             
9084             if(this.labelWidth < 13 && this.labelmd == 0){
9085                 this.labelmd = this.labelWidth;
9086             }
9087             
9088             if(this.labellg > 0){
9089                 labelCfg.cls += ' col-lg-' + this.labellg;
9090                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9091             }
9092             
9093             if(this.labelmd > 0){
9094                 labelCfg.cls += ' col-md-' + this.labelmd;
9095                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9096             }
9097             
9098             if(this.labelsm > 0){
9099                 labelCfg.cls += ' col-sm-' + this.labelsm;
9100                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9101             }
9102             
9103             if(this.labelxs > 0){
9104                 labelCfg.cls += ' col-xs-' + this.labelxs;
9105                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9106             }
9107             
9108             
9109         } else if ( this.fieldLabel.length) {
9110                 
9111             cfg.cn = [
9112                 {
9113                     tag : 'i',
9114                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9115                     tooltip : 'This field is required'
9116                 },
9117                 {
9118                     tag: 'label',
9119                    //cls : 'input-group-addon',
9120                     html : this.fieldLabel
9121
9122                 },
9123
9124                inputblock
9125
9126            ];
9127            
9128            if(this.indicatorpos == 'right'){
9129                 
9130                 cfg.cn = [
9131                     {
9132                         tag: 'label',
9133                        //cls : 'input-group-addon',
9134                         html : this.fieldLabel
9135
9136                     },
9137                     {
9138                         tag : 'i',
9139                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9140                         tooltip : 'This field is required'
9141                     },
9142
9143                    inputblock
9144
9145                ];
9146
9147             }
9148
9149         } else {
9150             
9151             cfg.cn = [
9152
9153                     inputblock
9154
9155             ];
9156                 
9157                 
9158         };
9159         
9160         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9161            cfg.cls += ' navbar-form';
9162         }
9163         
9164         if (this.parentType === 'NavGroup') {
9165            cfg.cls += ' navbar-form';
9166            cfg.tag = 'li';
9167         }
9168         
9169         return cfg;
9170         
9171     },
9172     /**
9173      * return the real input element.
9174      */
9175     inputEl: function ()
9176     {
9177         return this.el.select('input.form-control',true).first();
9178     },
9179     
9180     tooltipEl : function()
9181     {
9182         return this.inputEl();
9183     },
9184     
9185     indicatorEl : function()
9186     {
9187         var indicator = this.el.select('i.roo-required-indicator',true).first();
9188         
9189         if(!indicator){
9190             return false;
9191         }
9192         
9193         return indicator;
9194         
9195     },
9196     
9197     setDisabled : function(v)
9198     {
9199         var i  = this.inputEl().dom;
9200         if (!v) {
9201             i.removeAttribute('disabled');
9202             return;
9203             
9204         }
9205         i.setAttribute('disabled','true');
9206     },
9207     initEvents : function()
9208     {
9209           
9210         this.inputEl().on("keydown" , this.fireKey,  this);
9211         this.inputEl().on("focus", this.onFocus,  this);
9212         this.inputEl().on("blur", this.onBlur,  this);
9213         
9214         this.inputEl().relayEvent('keyup', this);
9215         
9216         this.indicator = this.indicatorEl();
9217         
9218         if(this.indicator){
9219             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9220         }
9221  
9222         // reference to original value for reset
9223         this.originalValue = this.getValue();
9224         //Roo.form.TextField.superclass.initEvents.call(this);
9225         if(this.validationEvent == 'keyup'){
9226             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9227             this.inputEl().on('keyup', this.filterValidation, this);
9228         }
9229         else if(this.validationEvent !== false){
9230             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9231         }
9232         
9233         if(this.selectOnFocus){
9234             this.on("focus", this.preFocus, this);
9235             
9236         }
9237         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9238             this.inputEl().on("keypress", this.filterKeys, this);
9239         } else {
9240             this.inputEl().relayEvent('keypress', this);
9241         }
9242        /* if(this.grow){
9243             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9244             this.el.on("click", this.autoSize,  this);
9245         }
9246         */
9247         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9248             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9249         }
9250         
9251         if (typeof(this.before) == 'object') {
9252             this.before.render(this.el.select('.roo-input-before',true).first());
9253         }
9254         if (typeof(this.after) == 'object') {
9255             this.after.render(this.el.select('.roo-input-after',true).first());
9256         }
9257         
9258         this.inputEl().on('change', this.onChange, this);
9259         
9260     },
9261     filterValidation : function(e){
9262         if(!e.isNavKeyPress()){
9263             this.validationTask.delay(this.validationDelay);
9264         }
9265     },
9266      /**
9267      * Validates the field value
9268      * @return {Boolean} True if the value is valid, else false
9269      */
9270     validate : function(){
9271         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9272         if(this.disabled || this.validateValue(this.getRawValue())){
9273             this.markValid();
9274             return true;
9275         }
9276         
9277         this.markInvalid();
9278         return false;
9279     },
9280     
9281     
9282     /**
9283      * Validates a value according to the field's validation rules and marks the field as invalid
9284      * if the validation fails
9285      * @param {Mixed} value The value to validate
9286      * @return {Boolean} True if the value is valid, else false
9287      */
9288     validateValue : function(value)
9289     {
9290         if(this.getVisibilityEl().hasClass('hidden')){
9291             return true;
9292         }
9293         
9294         if(value.length < 1)  { // if it's blank
9295             if(this.allowBlank){
9296                 return true;
9297             }
9298             return false;
9299         }
9300         
9301         if(value.length < this.minLength){
9302             return false;
9303         }
9304         if(value.length > this.maxLength){
9305             return false;
9306         }
9307         if(this.vtype){
9308             var vt = Roo.form.VTypes;
9309             if(!vt[this.vtype](value, this)){
9310                 return false;
9311             }
9312         }
9313         if(typeof this.validator == "function"){
9314             var msg = this.validator(value);
9315             if(msg !== true){
9316                 return false;
9317             }
9318             if (typeof(msg) == 'string') {
9319                 this.invalidText = msg;
9320             }
9321         }
9322         
9323         if(this.regex && !this.regex.test(value)){
9324             return false;
9325         }
9326         
9327         return true;
9328     },
9329     
9330      // private
9331     fireKey : function(e){
9332         //Roo.log('field ' + e.getKey());
9333         if(e.isNavKeyPress()){
9334             this.fireEvent("specialkey", this, e);
9335         }
9336     },
9337     focus : function (selectText){
9338         if(this.rendered){
9339             this.inputEl().focus();
9340             if(selectText === true){
9341                 this.inputEl().dom.select();
9342             }
9343         }
9344         return this;
9345     } ,
9346     
9347     onFocus : function(){
9348         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9349            // this.el.addClass(this.focusClass);
9350         }
9351         if(!this.hasFocus){
9352             this.hasFocus = true;
9353             this.startValue = this.getValue();
9354             this.fireEvent("focus", this);
9355         }
9356     },
9357     
9358     beforeBlur : Roo.emptyFn,
9359
9360     
9361     // private
9362     onBlur : function(){
9363         this.beforeBlur();
9364         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9365             //this.el.removeClass(this.focusClass);
9366         }
9367         this.hasFocus = false;
9368         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9369             this.validate();
9370         }
9371         var v = this.getValue();
9372         if(String(v) !== String(this.startValue)){
9373             this.fireEvent('change', this, v, this.startValue);
9374         }
9375         this.fireEvent("blur", this);
9376     },
9377     
9378     onChange : function(e)
9379     {
9380         var v = this.getValue();
9381         if(String(v) !== String(this.startValue)){
9382             this.fireEvent('change', this, v, this.startValue);
9383         }
9384         
9385     },
9386     
9387     /**
9388      * Resets the current field value to the originally loaded value and clears any validation messages
9389      */
9390     reset : function(){
9391         this.setValue(this.originalValue);
9392         this.validate();
9393     },
9394      /**
9395      * Returns the name of the field
9396      * @return {Mixed} name The name field
9397      */
9398     getName: function(){
9399         return this.name;
9400     },
9401      /**
9402      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9403      * @return {Mixed} value The field value
9404      */
9405     getValue : function(){
9406         
9407         var v = this.inputEl().getValue();
9408         
9409         return v;
9410     },
9411     /**
9412      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9413      * @return {Mixed} value The field value
9414      */
9415     getRawValue : function(){
9416         var v = this.inputEl().getValue();
9417         
9418         return v;
9419     },
9420     
9421     /**
9422      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9423      * @param {Mixed} value The value to set
9424      */
9425     setRawValue : function(v){
9426         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9427     },
9428     
9429     selectText : function(start, end){
9430         var v = this.getRawValue();
9431         if(v.length > 0){
9432             start = start === undefined ? 0 : start;
9433             end = end === undefined ? v.length : end;
9434             var d = this.inputEl().dom;
9435             if(d.setSelectionRange){
9436                 d.setSelectionRange(start, end);
9437             }else if(d.createTextRange){
9438                 var range = d.createTextRange();
9439                 range.moveStart("character", start);
9440                 range.moveEnd("character", v.length-end);
9441                 range.select();
9442             }
9443         }
9444     },
9445     
9446     /**
9447      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9448      * @param {Mixed} value The value to set
9449      */
9450     setValue : function(v){
9451         this.value = v;
9452         if(this.rendered){
9453             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9454             this.validate();
9455         }
9456     },
9457     
9458     /*
9459     processValue : function(value){
9460         if(this.stripCharsRe){
9461             var newValue = value.replace(this.stripCharsRe, '');
9462             if(newValue !== value){
9463                 this.setRawValue(newValue);
9464                 return newValue;
9465             }
9466         }
9467         return value;
9468     },
9469   */
9470     preFocus : function(){
9471         
9472         if(this.selectOnFocus){
9473             this.inputEl().dom.select();
9474         }
9475     },
9476     filterKeys : function(e){
9477         var k = e.getKey();
9478         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9479             return;
9480         }
9481         var c = e.getCharCode(), cc = String.fromCharCode(c);
9482         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9483             return;
9484         }
9485         if(!this.maskRe.test(cc)){
9486             e.stopEvent();
9487         }
9488     },
9489      /**
9490      * Clear any invalid styles/messages for this field
9491      */
9492     clearInvalid : function(){
9493         
9494         if(!this.el || this.preventMark){ // not rendered
9495             return;
9496         }
9497         
9498      
9499         this.el.removeClass(this.invalidClass);
9500         
9501         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9502             
9503             var feedback = this.el.select('.form-control-feedback', true).first();
9504             
9505             if(feedback){
9506                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9507             }
9508             
9509         }
9510         
9511         if(this.indicator){
9512             this.indicator.removeClass('visible');
9513             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9514         }
9515         
9516         this.fireEvent('valid', this);
9517     },
9518     
9519      /**
9520      * Mark this field as valid
9521      */
9522     markValid : function()
9523     {
9524         if(!this.el  || this.preventMark){ // not rendered...
9525             return;
9526         }
9527         
9528         this.el.removeClass([this.invalidClass, this.validClass]);
9529         
9530         var feedback = this.el.select('.form-control-feedback', true).first();
9531             
9532         if(feedback){
9533             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9534         }
9535         
9536         if(this.indicator){
9537             this.indicator.removeClass('visible');
9538             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9539         }
9540         
9541         if(this.disabled){
9542             return;
9543         }
9544         
9545         if(this.allowBlank && !this.getRawValue().length){
9546             return;
9547         }
9548         
9549         this.el.addClass(this.validClass);
9550         
9551         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9552             
9553             var feedback = this.el.select('.form-control-feedback', true).first();
9554             
9555             if(feedback){
9556                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9557                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9558             }
9559             
9560         }
9561         
9562         this.fireEvent('valid', this);
9563     },
9564     
9565      /**
9566      * Mark this field as invalid
9567      * @param {String} msg The validation message
9568      */
9569     markInvalid : function(msg)
9570     {
9571         if(!this.el  || this.preventMark){ // not rendered
9572             return;
9573         }
9574         
9575         this.el.removeClass([this.invalidClass, this.validClass]);
9576         
9577         var feedback = this.el.select('.form-control-feedback', true).first();
9578             
9579         if(feedback){
9580             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9581         }
9582
9583         if(this.disabled){
9584             return;
9585         }
9586         
9587         if(this.allowBlank && !this.getRawValue().length){
9588             return;
9589         }
9590         
9591         if(this.indicator){
9592             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9593             this.indicator.addClass('visible');
9594         }
9595         
9596         this.el.addClass(this.invalidClass);
9597         
9598         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9599             
9600             var feedback = this.el.select('.form-control-feedback', true).first();
9601             
9602             if(feedback){
9603                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9604                 
9605                 if(this.getValue().length || this.forceFeedback){
9606                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9607                 }
9608                 
9609             }
9610             
9611         }
9612         
9613         this.fireEvent('invalid', this, msg);
9614     },
9615     // private
9616     SafariOnKeyDown : function(event)
9617     {
9618         // this is a workaround for a password hang bug on chrome/ webkit.
9619         if (this.inputEl().dom.type != 'password') {
9620             return;
9621         }
9622         
9623         var isSelectAll = false;
9624         
9625         if(this.inputEl().dom.selectionEnd > 0){
9626             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9627         }
9628         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9629             event.preventDefault();
9630             this.setValue('');
9631             return;
9632         }
9633         
9634         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9635             
9636             event.preventDefault();
9637             // this is very hacky as keydown always get's upper case.
9638             //
9639             var cc = String.fromCharCode(event.getCharCode());
9640             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9641             
9642         }
9643     },
9644     adjustWidth : function(tag, w){
9645         tag = tag.toLowerCase();
9646         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9647             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9648                 if(tag == 'input'){
9649                     return w + 2;
9650                 }
9651                 if(tag == 'textarea'){
9652                     return w-2;
9653                 }
9654             }else if(Roo.isOpera){
9655                 if(tag == 'input'){
9656                     return w + 2;
9657                 }
9658                 if(tag == 'textarea'){
9659                     return w-2;
9660                 }
9661             }
9662         }
9663         return w;
9664     },
9665     
9666     setFieldLabel : function(v)
9667     {
9668         if(!this.rendered){
9669             return;
9670         }
9671         
9672         if(this.indicator){
9673             var ar = this.el.select('label > span',true);
9674             
9675             if (ar.elements.length) {
9676                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9677                 this.fieldLabel = v;
9678                 return;
9679             }
9680             
9681             var br = this.el.select('label',true);
9682             
9683             if(br.elements.length) {
9684                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9685                 this.fieldLabel = v;
9686                 return;
9687             }
9688             
9689             Roo.log('Cannot Found any of label > span || label in input');
9690             return;
9691         }
9692         
9693         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9694         this.fieldLabel = v;
9695         
9696         
9697     }
9698 });
9699
9700  
9701 /*
9702  * - LGPL
9703  *
9704  * Input
9705  * 
9706  */
9707
9708 /**
9709  * @class Roo.bootstrap.TextArea
9710  * @extends Roo.bootstrap.Input
9711  * Bootstrap TextArea class
9712  * @cfg {Number} cols Specifies the visible width of a text area
9713  * @cfg {Number} rows Specifies the visible number of lines in a text area
9714  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9715  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9716  * @cfg {string} html text
9717  * 
9718  * @constructor
9719  * Create a new TextArea
9720  * @param {Object} config The config object
9721  */
9722
9723 Roo.bootstrap.TextArea = function(config){
9724     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9725    
9726 };
9727
9728 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9729      
9730     cols : false,
9731     rows : 5,
9732     readOnly : false,
9733     warp : 'soft',
9734     resize : false,
9735     value: false,
9736     html: false,
9737     
9738     getAutoCreate : function(){
9739         
9740         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9741         
9742         var id = Roo.id();
9743         
9744         var cfg = {};
9745         
9746         if(this.inputType != 'hidden'){
9747             cfg.cls = 'form-group' //input-group
9748         }
9749         
9750         var input =  {
9751             tag: 'textarea',
9752             id : id,
9753             warp : this.warp,
9754             rows : this.rows,
9755             value : this.value || '',
9756             html: this.html || '',
9757             cls : 'form-control',
9758             placeholder : this.placeholder || '' 
9759             
9760         };
9761         
9762         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9763             input.maxLength = this.maxLength;
9764         }
9765         
9766         if(this.resize){
9767             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9768         }
9769         
9770         if(this.cols){
9771             input.cols = this.cols;
9772         }
9773         
9774         if (this.readOnly) {
9775             input.readonly = true;
9776         }
9777         
9778         if (this.name) {
9779             input.name = this.name;
9780         }
9781         
9782         if (this.size) {
9783             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9784         }
9785         
9786         var settings=this;
9787         ['xs','sm','md','lg'].map(function(size){
9788             if (settings[size]) {
9789                 cfg.cls += ' col-' + size + '-' + settings[size];
9790             }
9791         });
9792         
9793         var inputblock = input;
9794         
9795         if(this.hasFeedback && !this.allowBlank){
9796             
9797             var feedback = {
9798                 tag: 'span',
9799                 cls: 'glyphicon form-control-feedback'
9800             };
9801
9802             inputblock = {
9803                 cls : 'has-feedback',
9804                 cn :  [
9805                     input,
9806                     feedback
9807                 ] 
9808             };  
9809         }
9810         
9811         
9812         if (this.before || this.after) {
9813             
9814             inputblock = {
9815                 cls : 'input-group',
9816                 cn :  [] 
9817             };
9818             if (this.before) {
9819                 inputblock.cn.push({
9820                     tag :'span',
9821                     cls : 'input-group-addon',
9822                     html : this.before
9823                 });
9824             }
9825             
9826             inputblock.cn.push(input);
9827             
9828             if(this.hasFeedback && !this.allowBlank){
9829                 inputblock.cls += ' has-feedback';
9830                 inputblock.cn.push(feedback);
9831             }
9832             
9833             if (this.after) {
9834                 inputblock.cn.push({
9835                     tag :'span',
9836                     cls : 'input-group-addon',
9837                     html : this.after
9838                 });
9839             }
9840             
9841         }
9842         
9843         if (align ==='left' && this.fieldLabel.length) {
9844             cfg.cn = [
9845                 {
9846                     tag: 'label',
9847                     'for' :  id,
9848                     cls : 'control-label',
9849                     html : this.fieldLabel
9850                 },
9851                 {
9852                     cls : "",
9853                     cn: [
9854                         inputblock
9855                     ]
9856                 }
9857
9858             ];
9859             
9860             if(this.labelWidth > 12){
9861                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9862             }
9863
9864             if(this.labelWidth < 13 && this.labelmd == 0){
9865                 this.labelmd = this.labelWidth;
9866             }
9867
9868             if(this.labellg > 0){
9869                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9870                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9871             }
9872
9873             if(this.labelmd > 0){
9874                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9875                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9876             }
9877
9878             if(this.labelsm > 0){
9879                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9880                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9881             }
9882
9883             if(this.labelxs > 0){
9884                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9885                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9886             }
9887             
9888         } else if ( this.fieldLabel.length) {
9889             cfg.cn = [
9890
9891                {
9892                    tag: 'label',
9893                    //cls : 'input-group-addon',
9894                    html : this.fieldLabel
9895
9896                },
9897
9898                inputblock
9899
9900            ];
9901
9902         } else {
9903
9904             cfg.cn = [
9905
9906                 inputblock
9907
9908             ];
9909                 
9910         }
9911         
9912         if (this.disabled) {
9913             input.disabled=true;
9914         }
9915         
9916         return cfg;
9917         
9918     },
9919     /**
9920      * return the real textarea element.
9921      */
9922     inputEl: function ()
9923     {
9924         return this.el.select('textarea.form-control',true).first();
9925     },
9926     
9927     /**
9928      * Clear any invalid styles/messages for this field
9929      */
9930     clearInvalid : function()
9931     {
9932         
9933         if(!this.el || this.preventMark){ // not rendered
9934             return;
9935         }
9936         
9937         var label = this.el.select('label', true).first();
9938         var icon = this.el.select('i.fa-star', true).first();
9939         
9940         if(label && icon){
9941             icon.remove();
9942         }
9943         
9944         this.el.removeClass(this.invalidClass);
9945         
9946         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9947             
9948             var feedback = this.el.select('.form-control-feedback', true).first();
9949             
9950             if(feedback){
9951                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9952             }
9953             
9954         }
9955         
9956         this.fireEvent('valid', this);
9957     },
9958     
9959      /**
9960      * Mark this field as valid
9961      */
9962     markValid : function()
9963     {
9964         if(!this.el  || this.preventMark){ // not rendered
9965             return;
9966         }
9967         
9968         this.el.removeClass([this.invalidClass, this.validClass]);
9969         
9970         var feedback = this.el.select('.form-control-feedback', true).first();
9971             
9972         if(feedback){
9973             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9974         }
9975
9976         if(this.disabled || this.allowBlank){
9977             return;
9978         }
9979         
9980         var label = this.el.select('label', true).first();
9981         var icon = this.el.select('i.fa-star', true).first();
9982         
9983         if(label && icon){
9984             icon.remove();
9985         }
9986         
9987         this.el.addClass(this.validClass);
9988         
9989         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9990             
9991             var feedback = this.el.select('.form-control-feedback', true).first();
9992             
9993             if(feedback){
9994                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9995                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9996             }
9997             
9998         }
9999         
10000         this.fireEvent('valid', this);
10001     },
10002     
10003      /**
10004      * Mark this field as invalid
10005      * @param {String} msg The validation message
10006      */
10007     markInvalid : function(msg)
10008     {
10009         if(!this.el  || this.preventMark){ // not rendered
10010             return;
10011         }
10012         
10013         this.el.removeClass([this.invalidClass, this.validClass]);
10014         
10015         var feedback = this.el.select('.form-control-feedback', true).first();
10016             
10017         if(feedback){
10018             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10019         }
10020
10021         if(this.disabled || this.allowBlank){
10022             return;
10023         }
10024         
10025         var label = this.el.select('label', true).first();
10026         var icon = this.el.select('i.fa-star', true).first();
10027         
10028         if(!this.getValue().length && label && !icon){
10029             this.el.createChild({
10030                 tag : 'i',
10031                 cls : 'text-danger fa fa-lg fa-star',
10032                 tooltip : 'This field is required',
10033                 style : 'margin-right:5px;'
10034             }, label, true);
10035         }
10036
10037         this.el.addClass(this.invalidClass);
10038         
10039         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10040             
10041             var feedback = this.el.select('.form-control-feedback', true).first();
10042             
10043             if(feedback){
10044                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10045                 
10046                 if(this.getValue().length || this.forceFeedback){
10047                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10048                 }
10049                 
10050             }
10051             
10052         }
10053         
10054         this.fireEvent('invalid', this, msg);
10055     }
10056 });
10057
10058  
10059 /*
10060  * - LGPL
10061  *
10062  * trigger field - base class for combo..
10063  * 
10064  */
10065  
10066 /**
10067  * @class Roo.bootstrap.TriggerField
10068  * @extends Roo.bootstrap.Input
10069  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10070  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10071  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10072  * for which you can provide a custom implementation.  For example:
10073  * <pre><code>
10074 var trigger = new Roo.bootstrap.TriggerField();
10075 trigger.onTriggerClick = myTriggerFn;
10076 trigger.applyTo('my-field');
10077 </code></pre>
10078  *
10079  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10080  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10081  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10082  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10083  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10084
10085  * @constructor
10086  * Create a new TriggerField.
10087  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10088  * to the base TextField)
10089  */
10090 Roo.bootstrap.TriggerField = function(config){
10091     this.mimicing = false;
10092     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10093 };
10094
10095 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10096     /**
10097      * @cfg {String} triggerClass A CSS class to apply to the trigger
10098      */
10099      /**
10100      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10101      */
10102     hideTrigger:false,
10103
10104     /**
10105      * @cfg {Boolean} removable (true|false) special filter default false
10106      */
10107     removable : false,
10108     
10109     /** @cfg {Boolean} grow @hide */
10110     /** @cfg {Number} growMin @hide */
10111     /** @cfg {Number} growMax @hide */
10112
10113     /**
10114      * @hide 
10115      * @method
10116      */
10117     autoSize: Roo.emptyFn,
10118     // private
10119     monitorTab : true,
10120     // private
10121     deferHeight : true,
10122
10123     
10124     actionMode : 'wrap',
10125     
10126     caret : false,
10127     
10128     
10129     getAutoCreate : function(){
10130        
10131         var align = this.labelAlign || this.parentLabelAlign();
10132         
10133         var id = Roo.id();
10134         
10135         var cfg = {
10136             cls: 'form-group' //input-group
10137         };
10138         
10139         
10140         var input =  {
10141             tag: 'input',
10142             id : id,
10143             type : this.inputType,
10144             cls : 'form-control',
10145             autocomplete: 'new-password',
10146             placeholder : this.placeholder || '' 
10147             
10148         };
10149         if (this.name) {
10150             input.name = this.name;
10151         }
10152         if (this.size) {
10153             input.cls += ' input-' + this.size;
10154         }
10155         
10156         if (this.disabled) {
10157             input.disabled=true;
10158         }
10159         
10160         var inputblock = input;
10161         
10162         if(this.hasFeedback && !this.allowBlank){
10163             
10164             var feedback = {
10165                 tag: 'span',
10166                 cls: 'glyphicon form-control-feedback'
10167             };
10168             
10169             if(this.removable && !this.editable && !this.tickable){
10170                 inputblock = {
10171                     cls : 'has-feedback',
10172                     cn :  [
10173                         inputblock,
10174                         {
10175                             tag: 'button',
10176                             html : 'x',
10177                             cls : 'roo-combo-removable-btn close'
10178                         },
10179                         feedback
10180                     ] 
10181                 };
10182             } else {
10183                 inputblock = {
10184                     cls : 'has-feedback',
10185                     cn :  [
10186                         inputblock,
10187                         feedback
10188                     ] 
10189                 };
10190             }
10191
10192         } else {
10193             if(this.removable && !this.editable && !this.tickable){
10194                 inputblock = {
10195                     cls : 'roo-removable',
10196                     cn :  [
10197                         inputblock,
10198                         {
10199                             tag: 'button',
10200                             html : 'x',
10201                             cls : 'roo-combo-removable-btn close'
10202                         }
10203                     ] 
10204                 };
10205             }
10206         }
10207         
10208         if (this.before || this.after) {
10209             
10210             inputblock = {
10211                 cls : 'input-group',
10212                 cn :  [] 
10213             };
10214             if (this.before) {
10215                 inputblock.cn.push({
10216                     tag :'span',
10217                     cls : 'input-group-addon',
10218                     html : this.before
10219                 });
10220             }
10221             
10222             inputblock.cn.push(input);
10223             
10224             if(this.hasFeedback && !this.allowBlank){
10225                 inputblock.cls += ' has-feedback';
10226                 inputblock.cn.push(feedback);
10227             }
10228             
10229             if (this.after) {
10230                 inputblock.cn.push({
10231                     tag :'span',
10232                     cls : 'input-group-addon',
10233                     html : this.after
10234                 });
10235             }
10236             
10237         };
10238         
10239         var box = {
10240             tag: 'div',
10241             cn: [
10242                 {
10243                     tag: 'input',
10244                     type : 'hidden',
10245                     cls: 'form-hidden-field'
10246                 },
10247                 inputblock
10248             ]
10249             
10250         };
10251         
10252         if(this.multiple){
10253             box = {
10254                 tag: 'div',
10255                 cn: [
10256                     {
10257                         tag: 'input',
10258                         type : 'hidden',
10259                         cls: 'form-hidden-field'
10260                     },
10261                     {
10262                         tag: 'ul',
10263                         cls: 'roo-select2-choices',
10264                         cn:[
10265                             {
10266                                 tag: 'li',
10267                                 cls: 'roo-select2-search-field',
10268                                 cn: [
10269
10270                                     inputblock
10271                                 ]
10272                             }
10273                         ]
10274                     }
10275                 ]
10276             }
10277         };
10278         
10279         var combobox = {
10280             cls: 'roo-select2-container input-group',
10281             cn: [
10282                 box
10283 //                {
10284 //                    tag: 'ul',
10285 //                    cls: 'typeahead typeahead-long dropdown-menu',
10286 //                    style: 'display:none'
10287 //                }
10288             ]
10289         };
10290         
10291         if(!this.multiple && this.showToggleBtn){
10292             
10293             var caret = {
10294                         tag: 'span',
10295                         cls: 'caret'
10296              };
10297             if (this.caret != false) {
10298                 caret = {
10299                      tag: 'i',
10300                      cls: 'fa fa-' + this.caret
10301                 };
10302                 
10303             }
10304             
10305             combobox.cn.push({
10306                 tag :'span',
10307                 cls : 'input-group-addon btn dropdown-toggle',
10308                 cn : [
10309                     caret,
10310                     {
10311                         tag: 'span',
10312                         cls: 'combobox-clear',
10313                         cn  : [
10314                             {
10315                                 tag : 'i',
10316                                 cls: 'icon-remove'
10317                             }
10318                         ]
10319                     }
10320                 ]
10321
10322             })
10323         }
10324         
10325         if(this.multiple){
10326             combobox.cls += ' roo-select2-container-multi';
10327         }
10328         
10329         if (align ==='left' && this.fieldLabel.length) {
10330             
10331             cfg.cls += ' roo-form-group-label-left';
10332
10333             cfg.cn = [
10334                 {
10335                     tag : 'i',
10336                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10337                     tooltip : 'This field is required'
10338                 },
10339                 {
10340                     tag: 'label',
10341                     'for' :  id,
10342                     cls : 'control-label',
10343                     html : this.fieldLabel
10344
10345                 },
10346                 {
10347                     cls : "", 
10348                     cn: [
10349                         combobox
10350                     ]
10351                 }
10352
10353             ];
10354             
10355             var labelCfg = cfg.cn[1];
10356             var contentCfg = cfg.cn[2];
10357             
10358             if(this.indicatorpos == 'right'){
10359                 cfg.cn = [
10360                     {
10361                         tag: 'label',
10362                         'for' :  id,
10363                         cls : 'control-label',
10364                         cn : [
10365                             {
10366                                 tag : 'span',
10367                                 html : this.fieldLabel
10368                             },
10369                             {
10370                                 tag : 'i',
10371                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10372                                 tooltip : 'This field is required'
10373                             }
10374                         ]
10375                     },
10376                     {
10377                         cls : "", 
10378                         cn: [
10379                             combobox
10380                         ]
10381                     }
10382
10383                 ];
10384                 
10385                 labelCfg = cfg.cn[0];
10386                 contentCfg = cfg.cn[1];
10387             }
10388             
10389             if(this.labelWidth > 12){
10390                 labelCfg.style = "width: " + this.labelWidth + 'px';
10391             }
10392             
10393             if(this.labelWidth < 13 && this.labelmd == 0){
10394                 this.labelmd = this.labelWidth;
10395             }
10396             
10397             if(this.labellg > 0){
10398                 labelCfg.cls += ' col-lg-' + this.labellg;
10399                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10400             }
10401             
10402             if(this.labelmd > 0){
10403                 labelCfg.cls += ' col-md-' + this.labelmd;
10404                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10405             }
10406             
10407             if(this.labelsm > 0){
10408                 labelCfg.cls += ' col-sm-' + this.labelsm;
10409                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10410             }
10411             
10412             if(this.labelxs > 0){
10413                 labelCfg.cls += ' col-xs-' + this.labelxs;
10414                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10415             }
10416             
10417         } else if ( this.fieldLabel.length) {
10418 //                Roo.log(" label");
10419             cfg.cn = [
10420                 {
10421                    tag : 'i',
10422                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10423                    tooltip : 'This field is required'
10424                },
10425                {
10426                    tag: 'label',
10427                    //cls : 'input-group-addon',
10428                    html : this.fieldLabel
10429
10430                },
10431
10432                combobox
10433
10434             ];
10435             
10436             if(this.indicatorpos == 'right'){
10437                 
10438                 cfg.cn = [
10439                     {
10440                        tag: 'label',
10441                        cn : [
10442                            {
10443                                tag : 'span',
10444                                html : this.fieldLabel
10445                            },
10446                            {
10447                               tag : 'i',
10448                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10449                               tooltip : 'This field is required'
10450                            }
10451                        ]
10452
10453                     },
10454                     combobox
10455
10456                 ];
10457
10458             }
10459
10460         } else {
10461             
10462 //                Roo.log(" no label && no align");
10463                 cfg = combobox
10464                      
10465                 
10466         }
10467         
10468         var settings=this;
10469         ['xs','sm','md','lg'].map(function(size){
10470             if (settings[size]) {
10471                 cfg.cls += ' col-' + size + '-' + settings[size];
10472             }
10473         });
10474         
10475         return cfg;
10476         
10477     },
10478     
10479     
10480     
10481     // private
10482     onResize : function(w, h){
10483 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10484 //        if(typeof w == 'number'){
10485 //            var x = w - this.trigger.getWidth();
10486 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10487 //            this.trigger.setStyle('left', x+'px');
10488 //        }
10489     },
10490
10491     // private
10492     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10493
10494     // private
10495     getResizeEl : function(){
10496         return this.inputEl();
10497     },
10498
10499     // private
10500     getPositionEl : function(){
10501         return this.inputEl();
10502     },
10503
10504     // private
10505     alignErrorIcon : function(){
10506         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10507     },
10508
10509     // private
10510     initEvents : function(){
10511         
10512         this.createList();
10513         
10514         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10515         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10516         if(!this.multiple && this.showToggleBtn){
10517             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10518             if(this.hideTrigger){
10519                 this.trigger.setDisplayed(false);
10520             }
10521             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10522         }
10523         
10524         if(this.multiple){
10525             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10526         }
10527         
10528         if(this.removable && !this.editable && !this.tickable){
10529             var close = this.closeTriggerEl();
10530             
10531             if(close){
10532                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10533                 close.on('click', this.removeBtnClick, this, close);
10534             }
10535         }
10536         
10537         //this.trigger.addClassOnOver('x-form-trigger-over');
10538         //this.trigger.addClassOnClick('x-form-trigger-click');
10539         
10540         //if(!this.width){
10541         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10542         //}
10543     },
10544     
10545     closeTriggerEl : function()
10546     {
10547         var close = this.el.select('.roo-combo-removable-btn', true).first();
10548         return close ? close : false;
10549     },
10550     
10551     removeBtnClick : function(e, h, el)
10552     {
10553         e.preventDefault();
10554         
10555         if(this.fireEvent("remove", this) !== false){
10556             this.reset();
10557             this.fireEvent("afterremove", this)
10558         }
10559     },
10560     
10561     createList : function()
10562     {
10563         this.list = Roo.get(document.body).createChild({
10564             tag: 'ul',
10565             cls: 'typeahead typeahead-long dropdown-menu',
10566             style: 'display:none'
10567         });
10568         
10569         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10570         
10571     },
10572
10573     // private
10574     initTrigger : function(){
10575        
10576     },
10577
10578     // private
10579     onDestroy : function(){
10580         if(this.trigger){
10581             this.trigger.removeAllListeners();
10582           //  this.trigger.remove();
10583         }
10584         //if(this.wrap){
10585         //    this.wrap.remove();
10586         //}
10587         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10588     },
10589
10590     // private
10591     onFocus : function(){
10592         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10593         /*
10594         if(!this.mimicing){
10595             this.wrap.addClass('x-trigger-wrap-focus');
10596             this.mimicing = true;
10597             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10598             if(this.monitorTab){
10599                 this.el.on("keydown", this.checkTab, this);
10600             }
10601         }
10602         */
10603     },
10604
10605     // private
10606     checkTab : function(e){
10607         if(e.getKey() == e.TAB){
10608             this.triggerBlur();
10609         }
10610     },
10611
10612     // private
10613     onBlur : function(){
10614         // do nothing
10615     },
10616
10617     // private
10618     mimicBlur : function(e, t){
10619         /*
10620         if(!this.wrap.contains(t) && this.validateBlur()){
10621             this.triggerBlur();
10622         }
10623         */
10624     },
10625
10626     // private
10627     triggerBlur : function(){
10628         this.mimicing = false;
10629         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10630         if(this.monitorTab){
10631             this.el.un("keydown", this.checkTab, this);
10632         }
10633         //this.wrap.removeClass('x-trigger-wrap-focus');
10634         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10635     },
10636
10637     // private
10638     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10639     validateBlur : function(e, t){
10640         return true;
10641     },
10642
10643     // private
10644     onDisable : function(){
10645         this.inputEl().dom.disabled = true;
10646         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10647         //if(this.wrap){
10648         //    this.wrap.addClass('x-item-disabled');
10649         //}
10650     },
10651
10652     // private
10653     onEnable : function(){
10654         this.inputEl().dom.disabled = false;
10655         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10656         //if(this.wrap){
10657         //    this.el.removeClass('x-item-disabled');
10658         //}
10659     },
10660
10661     // private
10662     onShow : function(){
10663         var ae = this.getActionEl();
10664         
10665         if(ae){
10666             ae.dom.style.display = '';
10667             ae.dom.style.visibility = 'visible';
10668         }
10669     },
10670
10671     // private
10672     
10673     onHide : function(){
10674         var ae = this.getActionEl();
10675         ae.dom.style.display = 'none';
10676     },
10677
10678     /**
10679      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10680      * by an implementing function.
10681      * @method
10682      * @param {EventObject} e
10683      */
10684     onTriggerClick : Roo.emptyFn
10685 });
10686  /*
10687  * Based on:
10688  * Ext JS Library 1.1.1
10689  * Copyright(c) 2006-2007, Ext JS, LLC.
10690  *
10691  * Originally Released Under LGPL - original licence link has changed is not relivant.
10692  *
10693  * Fork - LGPL
10694  * <script type="text/javascript">
10695  */
10696
10697
10698 /**
10699  * @class Roo.data.SortTypes
10700  * @singleton
10701  * Defines the default sorting (casting?) comparison functions used when sorting data.
10702  */
10703 Roo.data.SortTypes = {
10704     /**
10705      * Default sort that does nothing
10706      * @param {Mixed} s The value being converted
10707      * @return {Mixed} The comparison value
10708      */
10709     none : function(s){
10710         return s;
10711     },
10712     
10713     /**
10714      * The regular expression used to strip tags
10715      * @type {RegExp}
10716      * @property
10717      */
10718     stripTagsRE : /<\/?[^>]+>/gi,
10719     
10720     /**
10721      * Strips all HTML tags to sort on text only
10722      * @param {Mixed} s The value being converted
10723      * @return {String} The comparison value
10724      */
10725     asText : function(s){
10726         return String(s).replace(this.stripTagsRE, "");
10727     },
10728     
10729     /**
10730      * Strips all HTML tags to sort on text only - Case insensitive
10731      * @param {Mixed} s The value being converted
10732      * @return {String} The comparison value
10733      */
10734     asUCText : function(s){
10735         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10736     },
10737     
10738     /**
10739      * Case insensitive string
10740      * @param {Mixed} s The value being converted
10741      * @return {String} The comparison value
10742      */
10743     asUCString : function(s) {
10744         return String(s).toUpperCase();
10745     },
10746     
10747     /**
10748      * Date sorting
10749      * @param {Mixed} s The value being converted
10750      * @return {Number} The comparison value
10751      */
10752     asDate : function(s) {
10753         if(!s){
10754             return 0;
10755         }
10756         if(s instanceof Date){
10757             return s.getTime();
10758         }
10759         return Date.parse(String(s));
10760     },
10761     
10762     /**
10763      * Float sorting
10764      * @param {Mixed} s The value being converted
10765      * @return {Float} The comparison value
10766      */
10767     asFloat : function(s) {
10768         var val = parseFloat(String(s).replace(/,/g, ""));
10769         if(isNaN(val)) {
10770             val = 0;
10771         }
10772         return val;
10773     },
10774     
10775     /**
10776      * Integer sorting
10777      * @param {Mixed} s The value being converted
10778      * @return {Number} The comparison value
10779      */
10780     asInt : function(s) {
10781         var val = parseInt(String(s).replace(/,/g, ""));
10782         if(isNaN(val)) {
10783             val = 0;
10784         }
10785         return val;
10786     }
10787 };/*
10788  * Based on:
10789  * Ext JS Library 1.1.1
10790  * Copyright(c) 2006-2007, Ext JS, LLC.
10791  *
10792  * Originally Released Under LGPL - original licence link has changed is not relivant.
10793  *
10794  * Fork - LGPL
10795  * <script type="text/javascript">
10796  */
10797
10798 /**
10799 * @class Roo.data.Record
10800  * Instances of this class encapsulate both record <em>definition</em> information, and record
10801  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10802  * to access Records cached in an {@link Roo.data.Store} object.<br>
10803  * <p>
10804  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10805  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10806  * objects.<br>
10807  * <p>
10808  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10809  * @constructor
10810  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10811  * {@link #create}. The parameters are the same.
10812  * @param {Array} data An associative Array of data values keyed by the field name.
10813  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10814  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10815  * not specified an integer id is generated.
10816  */
10817 Roo.data.Record = function(data, id){
10818     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10819     this.data = data;
10820 };
10821
10822 /**
10823  * Generate a constructor for a specific record layout.
10824  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10825  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10826  * Each field definition object may contain the following properties: <ul>
10827  * <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,
10828  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10829  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10830  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10831  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10832  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10833  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10834  * this may be omitted.</p></li>
10835  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10836  * <ul><li>auto (Default, implies no conversion)</li>
10837  * <li>string</li>
10838  * <li>int</li>
10839  * <li>float</li>
10840  * <li>boolean</li>
10841  * <li>date</li></ul></p></li>
10842  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10843  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10844  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10845  * by the Reader into an object that will be stored in the Record. It is passed the
10846  * following parameters:<ul>
10847  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10848  * </ul></p></li>
10849  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10850  * </ul>
10851  * <br>usage:<br><pre><code>
10852 var TopicRecord = Roo.data.Record.create(
10853     {name: 'title', mapping: 'topic_title'},
10854     {name: 'author', mapping: 'username'},
10855     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10856     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10857     {name: 'lastPoster', mapping: 'user2'},
10858     {name: 'excerpt', mapping: 'post_text'}
10859 );
10860
10861 var myNewRecord = new TopicRecord({
10862     title: 'Do my job please',
10863     author: 'noobie',
10864     totalPosts: 1,
10865     lastPost: new Date(),
10866     lastPoster: 'Animal',
10867     excerpt: 'No way dude!'
10868 });
10869 myStore.add(myNewRecord);
10870 </code></pre>
10871  * @method create
10872  * @static
10873  */
10874 Roo.data.Record.create = function(o){
10875     var f = function(){
10876         f.superclass.constructor.apply(this, arguments);
10877     };
10878     Roo.extend(f, Roo.data.Record);
10879     var p = f.prototype;
10880     p.fields = new Roo.util.MixedCollection(false, function(field){
10881         return field.name;
10882     });
10883     for(var i = 0, len = o.length; i < len; i++){
10884         p.fields.add(new Roo.data.Field(o[i]));
10885     }
10886     f.getField = function(name){
10887         return p.fields.get(name);  
10888     };
10889     return f;
10890 };
10891
10892 Roo.data.Record.AUTO_ID = 1000;
10893 Roo.data.Record.EDIT = 'edit';
10894 Roo.data.Record.REJECT = 'reject';
10895 Roo.data.Record.COMMIT = 'commit';
10896
10897 Roo.data.Record.prototype = {
10898     /**
10899      * Readonly flag - true if this record has been modified.
10900      * @type Boolean
10901      */
10902     dirty : false,
10903     editing : false,
10904     error: null,
10905     modified: null,
10906
10907     // private
10908     join : function(store){
10909         this.store = store;
10910     },
10911
10912     /**
10913      * Set the named field to the specified value.
10914      * @param {String} name The name of the field to set.
10915      * @param {Object} value The value to set the field to.
10916      */
10917     set : function(name, value){
10918         if(this.data[name] == value){
10919             return;
10920         }
10921         this.dirty = true;
10922         if(!this.modified){
10923             this.modified = {};
10924         }
10925         if(typeof this.modified[name] == 'undefined'){
10926             this.modified[name] = this.data[name];
10927         }
10928         this.data[name] = value;
10929         if(!this.editing && this.store){
10930             this.store.afterEdit(this);
10931         }       
10932     },
10933
10934     /**
10935      * Get the value of the named field.
10936      * @param {String} name The name of the field to get the value of.
10937      * @return {Object} The value of the field.
10938      */
10939     get : function(name){
10940         return this.data[name]; 
10941     },
10942
10943     // private
10944     beginEdit : function(){
10945         this.editing = true;
10946         this.modified = {}; 
10947     },
10948
10949     // private
10950     cancelEdit : function(){
10951         this.editing = false;
10952         delete this.modified;
10953     },
10954
10955     // private
10956     endEdit : function(){
10957         this.editing = false;
10958         if(this.dirty && this.store){
10959             this.store.afterEdit(this);
10960         }
10961     },
10962
10963     /**
10964      * Usually called by the {@link Roo.data.Store} which owns the Record.
10965      * Rejects all changes made to the Record since either creation, or the last commit operation.
10966      * Modified fields are reverted to their original values.
10967      * <p>
10968      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10969      * of reject operations.
10970      */
10971     reject : function(){
10972         var m = this.modified;
10973         for(var n in m){
10974             if(typeof m[n] != "function"){
10975                 this.data[n] = m[n];
10976             }
10977         }
10978         this.dirty = false;
10979         delete this.modified;
10980         this.editing = false;
10981         if(this.store){
10982             this.store.afterReject(this);
10983         }
10984     },
10985
10986     /**
10987      * Usually called by the {@link Roo.data.Store} which owns the Record.
10988      * Commits all changes made to the Record since either creation, or the last commit operation.
10989      * <p>
10990      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10991      * of commit operations.
10992      */
10993     commit : function(){
10994         this.dirty = false;
10995         delete this.modified;
10996         this.editing = false;
10997         if(this.store){
10998             this.store.afterCommit(this);
10999         }
11000     },
11001
11002     // private
11003     hasError : function(){
11004         return this.error != null;
11005     },
11006
11007     // private
11008     clearError : function(){
11009         this.error = null;
11010     },
11011
11012     /**
11013      * Creates a copy of this record.
11014      * @param {String} id (optional) A new record id if you don't want to use this record's id
11015      * @return {Record}
11016      */
11017     copy : function(newId) {
11018         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11019     }
11020 };/*
11021  * Based on:
11022  * Ext JS Library 1.1.1
11023  * Copyright(c) 2006-2007, Ext JS, LLC.
11024  *
11025  * Originally Released Under LGPL - original licence link has changed is not relivant.
11026  *
11027  * Fork - LGPL
11028  * <script type="text/javascript">
11029  */
11030
11031
11032
11033 /**
11034  * @class Roo.data.Store
11035  * @extends Roo.util.Observable
11036  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11037  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11038  * <p>
11039  * 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
11040  * has no knowledge of the format of the data returned by the Proxy.<br>
11041  * <p>
11042  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11043  * instances from the data object. These records are cached and made available through accessor functions.
11044  * @constructor
11045  * Creates a new Store.
11046  * @param {Object} config A config object containing the objects needed for the Store to access data,
11047  * and read the data into Records.
11048  */
11049 Roo.data.Store = function(config){
11050     this.data = new Roo.util.MixedCollection(false);
11051     this.data.getKey = function(o){
11052         return o.id;
11053     };
11054     this.baseParams = {};
11055     // private
11056     this.paramNames = {
11057         "start" : "start",
11058         "limit" : "limit",
11059         "sort" : "sort",
11060         "dir" : "dir",
11061         "multisort" : "_multisort"
11062     };
11063
11064     if(config && config.data){
11065         this.inlineData = config.data;
11066         delete config.data;
11067     }
11068
11069     Roo.apply(this, config);
11070     
11071     if(this.reader){ // reader passed
11072         this.reader = Roo.factory(this.reader, Roo.data);
11073         this.reader.xmodule = this.xmodule || false;
11074         if(!this.recordType){
11075             this.recordType = this.reader.recordType;
11076         }
11077         if(this.reader.onMetaChange){
11078             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11079         }
11080     }
11081
11082     if(this.recordType){
11083         this.fields = this.recordType.prototype.fields;
11084     }
11085     this.modified = [];
11086
11087     this.addEvents({
11088         /**
11089          * @event datachanged
11090          * Fires when the data cache has changed, and a widget which is using this Store
11091          * as a Record cache should refresh its view.
11092          * @param {Store} this
11093          */
11094         datachanged : true,
11095         /**
11096          * @event metachange
11097          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11098          * @param {Store} this
11099          * @param {Object} meta The JSON metadata
11100          */
11101         metachange : true,
11102         /**
11103          * @event add
11104          * Fires when Records have been added to the Store
11105          * @param {Store} this
11106          * @param {Roo.data.Record[]} records The array of Records added
11107          * @param {Number} index The index at which the record(s) were added
11108          */
11109         add : true,
11110         /**
11111          * @event remove
11112          * Fires when a Record has been removed from the Store
11113          * @param {Store} this
11114          * @param {Roo.data.Record} record The Record that was removed
11115          * @param {Number} index The index at which the record was removed
11116          */
11117         remove : true,
11118         /**
11119          * @event update
11120          * Fires when a Record has been updated
11121          * @param {Store} this
11122          * @param {Roo.data.Record} record The Record that was updated
11123          * @param {String} operation The update operation being performed.  Value may be one of:
11124          * <pre><code>
11125  Roo.data.Record.EDIT
11126  Roo.data.Record.REJECT
11127  Roo.data.Record.COMMIT
11128          * </code></pre>
11129          */
11130         update : true,
11131         /**
11132          * @event clear
11133          * Fires when the data cache has been cleared.
11134          * @param {Store} this
11135          */
11136         clear : true,
11137         /**
11138          * @event beforeload
11139          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11140          * the load action will be canceled.
11141          * @param {Store} this
11142          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11143          */
11144         beforeload : true,
11145         /**
11146          * @event beforeloadadd
11147          * Fires after a new set of Records has been loaded.
11148          * @param {Store} this
11149          * @param {Roo.data.Record[]} records The Records that were loaded
11150          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11151          */
11152         beforeloadadd : true,
11153         /**
11154          * @event load
11155          * Fires after a new set of Records has been loaded, before they are added to the store.
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          * @params {Object} return from reader
11160          */
11161         load : true,
11162         /**
11163          * @event loadexception
11164          * Fires if an exception occurs in the Proxy during loading.
11165          * Called with the signature of the Proxy's "loadexception" event.
11166          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11167          * 
11168          * @param {Proxy} 
11169          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11170          * @param {Object} load options 
11171          * @param {Object} jsonData from your request (normally this contains the Exception)
11172          */
11173         loadexception : true
11174     });
11175     
11176     if(this.proxy){
11177         this.proxy = Roo.factory(this.proxy, Roo.data);
11178         this.proxy.xmodule = this.xmodule || false;
11179         this.relayEvents(this.proxy,  ["loadexception"]);
11180     }
11181     this.sortToggle = {};
11182     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11183
11184     Roo.data.Store.superclass.constructor.call(this);
11185
11186     if(this.inlineData){
11187         this.loadData(this.inlineData);
11188         delete this.inlineData;
11189     }
11190 };
11191
11192 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11193      /**
11194     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11195     * without a remote query - used by combo/forms at present.
11196     */
11197     
11198     /**
11199     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11200     */
11201     /**
11202     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11203     */
11204     /**
11205     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11206     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11207     */
11208     /**
11209     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11210     * on any HTTP request
11211     */
11212     /**
11213     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11214     */
11215     /**
11216     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11217     */
11218     multiSort: false,
11219     /**
11220     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11221     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11222     */
11223     remoteSort : false,
11224
11225     /**
11226     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11227      * loaded or when a record is removed. (defaults to false).
11228     */
11229     pruneModifiedRecords : false,
11230
11231     // private
11232     lastOptions : null,
11233
11234     /**
11235      * Add Records to the Store and fires the add event.
11236      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11237      */
11238     add : function(records){
11239         records = [].concat(records);
11240         for(var i = 0, len = records.length; i < len; i++){
11241             records[i].join(this);
11242         }
11243         var index = this.data.length;
11244         this.data.addAll(records);
11245         this.fireEvent("add", this, records, index);
11246     },
11247
11248     /**
11249      * Remove a Record from the Store and fires the remove event.
11250      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11251      */
11252     remove : function(record){
11253         var index = this.data.indexOf(record);
11254         this.data.removeAt(index);
11255  
11256         if(this.pruneModifiedRecords){
11257             this.modified.remove(record);
11258         }
11259         this.fireEvent("remove", this, record, index);
11260     },
11261
11262     /**
11263      * Remove all Records from the Store and fires the clear event.
11264      */
11265     removeAll : function(){
11266         this.data.clear();
11267         if(this.pruneModifiedRecords){
11268             this.modified = [];
11269         }
11270         this.fireEvent("clear", this);
11271     },
11272
11273     /**
11274      * Inserts Records to the Store at the given index and fires the add event.
11275      * @param {Number} index The start index at which to insert the passed Records.
11276      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11277      */
11278     insert : function(index, records){
11279         records = [].concat(records);
11280         for(var i = 0, len = records.length; i < len; i++){
11281             this.data.insert(index, records[i]);
11282             records[i].join(this);
11283         }
11284         this.fireEvent("add", this, records, index);
11285     },
11286
11287     /**
11288      * Get the index within the cache of the passed Record.
11289      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11290      * @return {Number} The index of the passed Record. Returns -1 if not found.
11291      */
11292     indexOf : function(record){
11293         return this.data.indexOf(record);
11294     },
11295
11296     /**
11297      * Get the index within the cache of the Record with the passed id.
11298      * @param {String} id The id of the Record to find.
11299      * @return {Number} The index of the Record. Returns -1 if not found.
11300      */
11301     indexOfId : function(id){
11302         return this.data.indexOfKey(id);
11303     },
11304
11305     /**
11306      * Get the Record with the specified id.
11307      * @param {String} id The id of the Record to find.
11308      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11309      */
11310     getById : function(id){
11311         return this.data.key(id);
11312     },
11313
11314     /**
11315      * Get the Record at the specified index.
11316      * @param {Number} index The index of the Record to find.
11317      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11318      */
11319     getAt : function(index){
11320         return this.data.itemAt(index);
11321     },
11322
11323     /**
11324      * Returns a range of Records between specified indices.
11325      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11326      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11327      * @return {Roo.data.Record[]} An array of Records
11328      */
11329     getRange : function(start, end){
11330         return this.data.getRange(start, end);
11331     },
11332
11333     // private
11334     storeOptions : function(o){
11335         o = Roo.apply({}, o);
11336         delete o.callback;
11337         delete o.scope;
11338         this.lastOptions = o;
11339     },
11340
11341     /**
11342      * Loads the Record cache from the configured Proxy using the configured Reader.
11343      * <p>
11344      * If using remote paging, then the first load call must specify the <em>start</em>
11345      * and <em>limit</em> properties in the options.params property to establish the initial
11346      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11347      * <p>
11348      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11349      * and this call will return before the new data has been loaded. Perform any post-processing
11350      * in a callback function, or in a "load" event handler.</strong>
11351      * <p>
11352      * @param {Object} options An object containing properties which control loading options:<ul>
11353      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11354      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11355      * passed the following arguments:<ul>
11356      * <li>r : Roo.data.Record[]</li>
11357      * <li>options: Options object from the load call</li>
11358      * <li>success: Boolean success indicator</li></ul></li>
11359      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11360      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11361      * </ul>
11362      */
11363     load : function(options){
11364         options = options || {};
11365         if(this.fireEvent("beforeload", this, options) !== false){
11366             this.storeOptions(options);
11367             var p = Roo.apply(options.params || {}, this.baseParams);
11368             // if meta was not loaded from remote source.. try requesting it.
11369             if (!this.reader.metaFromRemote) {
11370                 p._requestMeta = 1;
11371             }
11372             if(this.sortInfo && this.remoteSort){
11373                 var pn = this.paramNames;
11374                 p[pn["sort"]] = this.sortInfo.field;
11375                 p[pn["dir"]] = this.sortInfo.direction;
11376             }
11377             if (this.multiSort) {
11378                 var pn = this.paramNames;
11379                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11380             }
11381             
11382             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11383         }
11384     },
11385
11386     /**
11387      * Reloads the Record cache from the configured Proxy using the configured Reader and
11388      * the options from the last load operation performed.
11389      * @param {Object} options (optional) An object containing properties which may override the options
11390      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11391      * the most recently used options are reused).
11392      */
11393     reload : function(options){
11394         this.load(Roo.applyIf(options||{}, this.lastOptions));
11395     },
11396
11397     // private
11398     // Called as a callback by the Reader during a load operation.
11399     loadRecords : function(o, options, success){
11400         if(!o || success === false){
11401             if(success !== false){
11402                 this.fireEvent("load", this, [], options, o);
11403             }
11404             if(options.callback){
11405                 options.callback.call(options.scope || this, [], options, false);
11406             }
11407             return;
11408         }
11409         // if data returned failure - throw an exception.
11410         if (o.success === false) {
11411             // show a message if no listener is registered.
11412             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11413                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11414             }
11415             // loadmask wil be hooked into this..
11416             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11417             return;
11418         }
11419         var r = o.records, t = o.totalRecords || r.length;
11420         
11421         this.fireEvent("beforeloadadd", this, r, options, o);
11422         
11423         if(!options || options.add !== true){
11424             if(this.pruneModifiedRecords){
11425                 this.modified = [];
11426             }
11427             for(var i = 0, len = r.length; i < len; i++){
11428                 r[i].join(this);
11429             }
11430             if(this.snapshot){
11431                 this.data = this.snapshot;
11432                 delete this.snapshot;
11433             }
11434             this.data.clear();
11435             this.data.addAll(r);
11436             this.totalLength = t;
11437             this.applySort();
11438             this.fireEvent("datachanged", this);
11439         }else{
11440             this.totalLength = Math.max(t, this.data.length+r.length);
11441             this.add(r);
11442         }
11443         
11444         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11445                 
11446             var e = new Roo.data.Record({});
11447
11448             e.set(this.parent.displayField, this.parent.emptyTitle);
11449             e.set(this.parent.valueField, '');
11450
11451             this.insert(0, e);
11452         }
11453             
11454         this.fireEvent("load", this, r, options, o);
11455         if(options.callback){
11456             options.callback.call(options.scope || this, r, options, true);
11457         }
11458     },
11459
11460
11461     /**
11462      * Loads data from a passed data block. A Reader which understands the format of the data
11463      * must have been configured in the constructor.
11464      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11465      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11466      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11467      */
11468     loadData : function(o, append){
11469         var r = this.reader.readRecords(o);
11470         this.loadRecords(r, {add: append}, true);
11471     },
11472
11473     /**
11474      * Gets the number of cached records.
11475      * <p>
11476      * <em>If using paging, this may not be the total size of the dataset. If the data object
11477      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11478      * the data set size</em>
11479      */
11480     getCount : function(){
11481         return this.data.length || 0;
11482     },
11483
11484     /**
11485      * Gets the total number of records in the dataset as returned by the server.
11486      * <p>
11487      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11488      * the dataset size</em>
11489      */
11490     getTotalCount : function(){
11491         return this.totalLength || 0;
11492     },
11493
11494     /**
11495      * Returns the sort state of the Store as an object with two properties:
11496      * <pre><code>
11497  field {String} The name of the field by which the Records are sorted
11498  direction {String} The sort order, "ASC" or "DESC"
11499      * </code></pre>
11500      */
11501     getSortState : function(){
11502         return this.sortInfo;
11503     },
11504
11505     // private
11506     applySort : function(){
11507         if(this.sortInfo && !this.remoteSort){
11508             var s = this.sortInfo, f = s.field;
11509             var st = this.fields.get(f).sortType;
11510             var fn = function(r1, r2){
11511                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11512                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11513             };
11514             this.data.sort(s.direction, fn);
11515             if(this.snapshot && this.snapshot != this.data){
11516                 this.snapshot.sort(s.direction, fn);
11517             }
11518         }
11519     },
11520
11521     /**
11522      * Sets the default sort column and order to be used by the next load operation.
11523      * @param {String} fieldName The name of the field to sort by.
11524      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11525      */
11526     setDefaultSort : function(field, dir){
11527         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11528     },
11529
11530     /**
11531      * Sort the Records.
11532      * If remote sorting is used, the sort is performed on the server, and the cache is
11533      * reloaded. If local sorting is used, the cache is sorted internally.
11534      * @param {String} fieldName The name of the field to sort by.
11535      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11536      */
11537     sort : function(fieldName, dir){
11538         var f = this.fields.get(fieldName);
11539         if(!dir){
11540             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11541             
11542             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11543                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11544             }else{
11545                 dir = f.sortDir;
11546             }
11547         }
11548         this.sortToggle[f.name] = dir;
11549         this.sortInfo = {field: f.name, direction: dir};
11550         if(!this.remoteSort){
11551             this.applySort();
11552             this.fireEvent("datachanged", this);
11553         }else{
11554             this.load(this.lastOptions);
11555         }
11556     },
11557
11558     /**
11559      * Calls the specified function for each of the Records in the cache.
11560      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11561      * Returning <em>false</em> aborts and exits the iteration.
11562      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11563      */
11564     each : function(fn, scope){
11565         this.data.each(fn, scope);
11566     },
11567
11568     /**
11569      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11570      * (e.g., during paging).
11571      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11572      */
11573     getModifiedRecords : function(){
11574         return this.modified;
11575     },
11576
11577     // private
11578     createFilterFn : function(property, value, anyMatch){
11579         if(!value.exec){ // not a regex
11580             value = String(value);
11581             if(value.length == 0){
11582                 return false;
11583             }
11584             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11585         }
11586         return function(r){
11587             return value.test(r.data[property]);
11588         };
11589     },
11590
11591     /**
11592      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11593      * @param {String} property A field on your records
11594      * @param {Number} start The record index to start at (defaults to 0)
11595      * @param {Number} end The last record index to include (defaults to length - 1)
11596      * @return {Number} The sum
11597      */
11598     sum : function(property, start, end){
11599         var rs = this.data.items, v = 0;
11600         start = start || 0;
11601         end = (end || end === 0) ? end : rs.length-1;
11602
11603         for(var i = start; i <= end; i++){
11604             v += (rs[i].data[property] || 0);
11605         }
11606         return v;
11607     },
11608
11609     /**
11610      * Filter the records by a specified property.
11611      * @param {String} field A field on your records
11612      * @param {String/RegExp} value Either a string that the field
11613      * should start with or a RegExp to test against the field
11614      * @param {Boolean} anyMatch True to match any part not just the beginning
11615      */
11616     filter : function(property, value, anyMatch){
11617         var fn = this.createFilterFn(property, value, anyMatch);
11618         return fn ? this.filterBy(fn) : this.clearFilter();
11619     },
11620
11621     /**
11622      * Filter by a function. The specified function will be called with each
11623      * record in this data source. If the function returns true the record is included,
11624      * otherwise it is filtered.
11625      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11626      * @param {Object} scope (optional) The scope of the function (defaults to this)
11627      */
11628     filterBy : function(fn, scope){
11629         this.snapshot = this.snapshot || this.data;
11630         this.data = this.queryBy(fn, scope||this);
11631         this.fireEvent("datachanged", this);
11632     },
11633
11634     /**
11635      * Query the records by a specified property.
11636      * @param {String} field A field on your records
11637      * @param {String/RegExp} value Either a string that the field
11638      * should start with or a RegExp to test against the field
11639      * @param {Boolean} anyMatch True to match any part not just the beginning
11640      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11641      */
11642     query : function(property, value, anyMatch){
11643         var fn = this.createFilterFn(property, value, anyMatch);
11644         return fn ? this.queryBy(fn) : this.data.clone();
11645     },
11646
11647     /**
11648      * Query by a function. The specified function will be called with each
11649      * record in this data source. If the function returns true the record is included
11650      * in the results.
11651      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11652      * @param {Object} scope (optional) The scope of the function (defaults to this)
11653       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11654      **/
11655     queryBy : function(fn, scope){
11656         var data = this.snapshot || this.data;
11657         return data.filterBy(fn, scope||this);
11658     },
11659
11660     /**
11661      * Collects unique values for a particular dataIndex from this store.
11662      * @param {String} dataIndex The property to collect
11663      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11664      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11665      * @return {Array} An array of the unique values
11666      **/
11667     collect : function(dataIndex, allowNull, bypassFilter){
11668         var d = (bypassFilter === true && this.snapshot) ?
11669                 this.snapshot.items : this.data.items;
11670         var v, sv, r = [], l = {};
11671         for(var i = 0, len = d.length; i < len; i++){
11672             v = d[i].data[dataIndex];
11673             sv = String(v);
11674             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11675                 l[sv] = true;
11676                 r[r.length] = v;
11677             }
11678         }
11679         return r;
11680     },
11681
11682     /**
11683      * Revert to a view of the Record cache with no filtering applied.
11684      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11685      */
11686     clearFilter : function(suppressEvent){
11687         if(this.snapshot && this.snapshot != this.data){
11688             this.data = this.snapshot;
11689             delete this.snapshot;
11690             if(suppressEvent !== true){
11691                 this.fireEvent("datachanged", this);
11692             }
11693         }
11694     },
11695
11696     // private
11697     afterEdit : function(record){
11698         if(this.modified.indexOf(record) == -1){
11699             this.modified.push(record);
11700         }
11701         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11702     },
11703     
11704     // private
11705     afterReject : function(record){
11706         this.modified.remove(record);
11707         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11708     },
11709
11710     // private
11711     afterCommit : function(record){
11712         this.modified.remove(record);
11713         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11714     },
11715
11716     /**
11717      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11718      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11719      */
11720     commitChanges : function(){
11721         var m = this.modified.slice(0);
11722         this.modified = [];
11723         for(var i = 0, len = m.length; i < len; i++){
11724             m[i].commit();
11725         }
11726     },
11727
11728     /**
11729      * Cancel outstanding changes on all changed records.
11730      */
11731     rejectChanges : function(){
11732         var m = this.modified.slice(0);
11733         this.modified = [];
11734         for(var i = 0, len = m.length; i < len; i++){
11735             m[i].reject();
11736         }
11737     },
11738
11739     onMetaChange : function(meta, rtype, o){
11740         this.recordType = rtype;
11741         this.fields = rtype.prototype.fields;
11742         delete this.snapshot;
11743         this.sortInfo = meta.sortInfo || this.sortInfo;
11744         this.modified = [];
11745         this.fireEvent('metachange', this, this.reader.meta);
11746     },
11747     
11748     moveIndex : function(data, type)
11749     {
11750         var index = this.indexOf(data);
11751         
11752         var newIndex = index + type;
11753         
11754         this.remove(data);
11755         
11756         this.insert(newIndex, data);
11757         
11758     }
11759 });/*
11760  * Based on:
11761  * Ext JS Library 1.1.1
11762  * Copyright(c) 2006-2007, Ext JS, LLC.
11763  *
11764  * Originally Released Under LGPL - original licence link has changed is not relivant.
11765  *
11766  * Fork - LGPL
11767  * <script type="text/javascript">
11768  */
11769
11770 /**
11771  * @class Roo.data.SimpleStore
11772  * @extends Roo.data.Store
11773  * Small helper class to make creating Stores from Array data easier.
11774  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11775  * @cfg {Array} fields An array of field definition objects, or field name strings.
11776  * @cfg {Array} data The multi-dimensional array of data
11777  * @constructor
11778  * @param {Object} config
11779  */
11780 Roo.data.SimpleStore = function(config){
11781     Roo.data.SimpleStore.superclass.constructor.call(this, {
11782         isLocal : true,
11783         reader: new Roo.data.ArrayReader({
11784                 id: config.id
11785             },
11786             Roo.data.Record.create(config.fields)
11787         ),
11788         proxy : new Roo.data.MemoryProxy(config.data)
11789     });
11790     this.load();
11791 };
11792 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11793  * Based on:
11794  * Ext JS Library 1.1.1
11795  * Copyright(c) 2006-2007, Ext JS, LLC.
11796  *
11797  * Originally Released Under LGPL - original licence link has changed is not relivant.
11798  *
11799  * Fork - LGPL
11800  * <script type="text/javascript">
11801  */
11802
11803 /**
11804 /**
11805  * @extends Roo.data.Store
11806  * @class Roo.data.JsonStore
11807  * Small helper class to make creating Stores for JSON data easier. <br/>
11808 <pre><code>
11809 var store = new Roo.data.JsonStore({
11810     url: 'get-images.php',
11811     root: 'images',
11812     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11813 });
11814 </code></pre>
11815  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11816  * JsonReader and HttpProxy (unless inline data is provided).</b>
11817  * @cfg {Array} fields An array of field definition objects, or field name strings.
11818  * @constructor
11819  * @param {Object} config
11820  */
11821 Roo.data.JsonStore = function(c){
11822     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11823         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11824         reader: new Roo.data.JsonReader(c, c.fields)
11825     }));
11826 };
11827 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11828  * Based on:
11829  * Ext JS Library 1.1.1
11830  * Copyright(c) 2006-2007, Ext JS, LLC.
11831  *
11832  * Originally Released Under LGPL - original licence link has changed is not relivant.
11833  *
11834  * Fork - LGPL
11835  * <script type="text/javascript">
11836  */
11837
11838  
11839 Roo.data.Field = function(config){
11840     if(typeof config == "string"){
11841         config = {name: config};
11842     }
11843     Roo.apply(this, config);
11844     
11845     if(!this.type){
11846         this.type = "auto";
11847     }
11848     
11849     var st = Roo.data.SortTypes;
11850     // named sortTypes are supported, here we look them up
11851     if(typeof this.sortType == "string"){
11852         this.sortType = st[this.sortType];
11853     }
11854     
11855     // set default sortType for strings and dates
11856     if(!this.sortType){
11857         switch(this.type){
11858             case "string":
11859                 this.sortType = st.asUCString;
11860                 break;
11861             case "date":
11862                 this.sortType = st.asDate;
11863                 break;
11864             default:
11865                 this.sortType = st.none;
11866         }
11867     }
11868
11869     // define once
11870     var stripRe = /[\$,%]/g;
11871
11872     // prebuilt conversion function for this field, instead of
11873     // switching every time we're reading a value
11874     if(!this.convert){
11875         var cv, dateFormat = this.dateFormat;
11876         switch(this.type){
11877             case "":
11878             case "auto":
11879             case undefined:
11880                 cv = function(v){ return v; };
11881                 break;
11882             case "string":
11883                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11884                 break;
11885             case "int":
11886                 cv = function(v){
11887                     return v !== undefined && v !== null && v !== '' ?
11888                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11889                     };
11890                 break;
11891             case "float":
11892                 cv = function(v){
11893                     return v !== undefined && v !== null && v !== '' ?
11894                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11895                     };
11896                 break;
11897             case "bool":
11898             case "boolean":
11899                 cv = function(v){ return v === true || v === "true" || v == 1; };
11900                 break;
11901             case "date":
11902                 cv = function(v){
11903                     if(!v){
11904                         return '';
11905                     }
11906                     if(v instanceof Date){
11907                         return v;
11908                     }
11909                     if(dateFormat){
11910                         if(dateFormat == "timestamp"){
11911                             return new Date(v*1000);
11912                         }
11913                         return Date.parseDate(v, dateFormat);
11914                     }
11915                     var parsed = Date.parse(v);
11916                     return parsed ? new Date(parsed) : null;
11917                 };
11918              break;
11919             
11920         }
11921         this.convert = cv;
11922     }
11923 };
11924
11925 Roo.data.Field.prototype = {
11926     dateFormat: null,
11927     defaultValue: "",
11928     mapping: null,
11929     sortType : null,
11930     sortDir : "ASC"
11931 };/*
11932  * Based on:
11933  * Ext JS Library 1.1.1
11934  * Copyright(c) 2006-2007, Ext JS, LLC.
11935  *
11936  * Originally Released Under LGPL - original licence link has changed is not relivant.
11937  *
11938  * Fork - LGPL
11939  * <script type="text/javascript">
11940  */
11941  
11942 // Base class for reading structured data from a data source.  This class is intended to be
11943 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11944
11945 /**
11946  * @class Roo.data.DataReader
11947  * Base class for reading structured data from a data source.  This class is intended to be
11948  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11949  */
11950
11951 Roo.data.DataReader = function(meta, recordType){
11952     
11953     this.meta = meta;
11954     
11955     this.recordType = recordType instanceof Array ? 
11956         Roo.data.Record.create(recordType) : recordType;
11957 };
11958
11959 Roo.data.DataReader.prototype = {
11960      /**
11961      * Create an empty record
11962      * @param {Object} data (optional) - overlay some values
11963      * @return {Roo.data.Record} record created.
11964      */
11965     newRow :  function(d) {
11966         var da =  {};
11967         this.recordType.prototype.fields.each(function(c) {
11968             switch( c.type) {
11969                 case 'int' : da[c.name] = 0; break;
11970                 case 'date' : da[c.name] = new Date(); break;
11971                 case 'float' : da[c.name] = 0.0; break;
11972                 case 'boolean' : da[c.name] = false; break;
11973                 default : da[c.name] = ""; break;
11974             }
11975             
11976         });
11977         return new this.recordType(Roo.apply(da, d));
11978     }
11979     
11980 };/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990
11991 /**
11992  * @class Roo.data.DataProxy
11993  * @extends Roo.data.Observable
11994  * This class is an abstract base class for implementations which provide retrieval of
11995  * unformatted data objects.<br>
11996  * <p>
11997  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11998  * (of the appropriate type which knows how to parse the data object) to provide a block of
11999  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12000  * <p>
12001  * Custom implementations must implement the load method as described in
12002  * {@link Roo.data.HttpProxy#load}.
12003  */
12004 Roo.data.DataProxy = function(){
12005     this.addEvents({
12006         /**
12007          * @event beforeload
12008          * Fires before a network request is made to retrieve a data object.
12009          * @param {Object} This DataProxy object.
12010          * @param {Object} params The params parameter to the load function.
12011          */
12012         beforeload : true,
12013         /**
12014          * @event load
12015          * Fires before the load method's callback is called.
12016          * @param {Object} This DataProxy object.
12017          * @param {Object} o The data object.
12018          * @param {Object} arg The callback argument object passed to the load function.
12019          */
12020         load : true,
12021         /**
12022          * @event loadexception
12023          * Fires if an Exception occurs during data retrieval.
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          * @param {Object} e The Exception.
12028          */
12029         loadexception : true
12030     });
12031     Roo.data.DataProxy.superclass.constructor.call(this);
12032 };
12033
12034 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12035
12036     /**
12037      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12038      */
12039 /*
12040  * Based on:
12041  * Ext JS Library 1.1.1
12042  * Copyright(c) 2006-2007, Ext JS, LLC.
12043  *
12044  * Originally Released Under LGPL - original licence link has changed is not relivant.
12045  *
12046  * Fork - LGPL
12047  * <script type="text/javascript">
12048  */
12049 /**
12050  * @class Roo.data.MemoryProxy
12051  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12052  * to the Reader when its load method is called.
12053  * @constructor
12054  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12055  */
12056 Roo.data.MemoryProxy = function(data){
12057     if (data.data) {
12058         data = data.data;
12059     }
12060     Roo.data.MemoryProxy.superclass.constructor.call(this);
12061     this.data = data;
12062 };
12063
12064 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12065     
12066     /**
12067      * Load data from the requested source (in this case an in-memory
12068      * data object passed to the constructor), read the data object into
12069      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12070      * process that block using the passed callback.
12071      * @param {Object} params This parameter is not used by the MemoryProxy class.
12072      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12073      * object into a block of Roo.data.Records.
12074      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12075      * The function must be passed <ul>
12076      * <li>The Record block object</li>
12077      * <li>The "arg" argument from the load function</li>
12078      * <li>A boolean success indicator</li>
12079      * </ul>
12080      * @param {Object} scope The scope in which to call the callback
12081      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12082      */
12083     load : function(params, reader, callback, scope, arg){
12084         params = params || {};
12085         var result;
12086         try {
12087             result = reader.readRecords(this.data);
12088         }catch(e){
12089             this.fireEvent("loadexception", this, arg, null, e);
12090             callback.call(scope, null, arg, false);
12091             return;
12092         }
12093         callback.call(scope, result, arg, true);
12094     },
12095     
12096     // private
12097     update : function(params, records){
12098         
12099     }
12100 });/*
12101  * Based on:
12102  * Ext JS Library 1.1.1
12103  * Copyright(c) 2006-2007, Ext JS, LLC.
12104  *
12105  * Originally Released Under LGPL - original licence link has changed is not relivant.
12106  *
12107  * Fork - LGPL
12108  * <script type="text/javascript">
12109  */
12110 /**
12111  * @class Roo.data.HttpProxy
12112  * @extends Roo.data.DataProxy
12113  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12114  * configured to reference a certain URL.<br><br>
12115  * <p>
12116  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12117  * from which the running page was served.<br><br>
12118  * <p>
12119  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12120  * <p>
12121  * Be aware that to enable the browser to parse an XML document, the server must set
12122  * the Content-Type header in the HTTP response to "text/xml".
12123  * @constructor
12124  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12125  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12126  * will be used to make the request.
12127  */
12128 Roo.data.HttpProxy = function(conn){
12129     Roo.data.HttpProxy.superclass.constructor.call(this);
12130     // is conn a conn config or a real conn?
12131     this.conn = conn;
12132     this.useAjax = !conn || !conn.events;
12133   
12134 };
12135
12136 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12137     // thse are take from connection...
12138     
12139     /**
12140      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12141      */
12142     /**
12143      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12144      * extra parameters to each request made by this object. (defaults to undefined)
12145      */
12146     /**
12147      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12148      *  to each request made by this object. (defaults to undefined)
12149      */
12150     /**
12151      * @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)
12152      */
12153     /**
12154      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12155      */
12156      /**
12157      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12158      * @type Boolean
12159      */
12160   
12161
12162     /**
12163      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12164      * @type Boolean
12165      */
12166     /**
12167      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12168      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12169      * a finer-grained basis than the DataProxy events.
12170      */
12171     getConnection : function(){
12172         return this.useAjax ? Roo.Ajax : this.conn;
12173     },
12174
12175     /**
12176      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12177      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12178      * process that block using the passed callback.
12179      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12180      * for the request to the remote server.
12181      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12182      * object into a block of Roo.data.Records.
12183      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12184      * The function must be passed <ul>
12185      * <li>The Record block object</li>
12186      * <li>The "arg" argument from the load function</li>
12187      * <li>A boolean success indicator</li>
12188      * </ul>
12189      * @param {Object} scope The scope in which to call the callback
12190      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12191      */
12192     load : function(params, reader, callback, scope, arg){
12193         if(this.fireEvent("beforeload", this, params) !== false){
12194             var  o = {
12195                 params : params || {},
12196                 request: {
12197                     callback : callback,
12198                     scope : scope,
12199                     arg : arg
12200                 },
12201                 reader: reader,
12202                 callback : this.loadResponse,
12203                 scope: this
12204             };
12205             if(this.useAjax){
12206                 Roo.applyIf(o, this.conn);
12207                 if(this.activeRequest){
12208                     Roo.Ajax.abort(this.activeRequest);
12209                 }
12210                 this.activeRequest = Roo.Ajax.request(o);
12211             }else{
12212                 this.conn.request(o);
12213             }
12214         }else{
12215             callback.call(scope||this, null, arg, false);
12216         }
12217     },
12218
12219     // private
12220     loadResponse : function(o, success, response){
12221         delete this.activeRequest;
12222         if(!success){
12223             this.fireEvent("loadexception", this, o, response);
12224             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12225             return;
12226         }
12227         var result;
12228         try {
12229             result = o.reader.read(response);
12230         }catch(e){
12231             this.fireEvent("loadexception", this, o, response, e);
12232             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12233             return;
12234         }
12235         
12236         this.fireEvent("load", this, o, o.request.arg);
12237         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12238     },
12239
12240     // private
12241     update : function(dataSet){
12242
12243     },
12244
12245     // private
12246     updateResponse : function(dataSet){
12247
12248     }
12249 });/*
12250  * Based on:
12251  * Ext JS Library 1.1.1
12252  * Copyright(c) 2006-2007, Ext JS, LLC.
12253  *
12254  * Originally Released Under LGPL - original licence link has changed is not relivant.
12255  *
12256  * Fork - LGPL
12257  * <script type="text/javascript">
12258  */
12259
12260 /**
12261  * @class Roo.data.ScriptTagProxy
12262  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12263  * other than the originating domain of the running page.<br><br>
12264  * <p>
12265  * <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
12266  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12267  * <p>
12268  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12269  * source code that is used as the source inside a &lt;script> tag.<br><br>
12270  * <p>
12271  * In order for the browser to process the returned data, the server must wrap the data object
12272  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12273  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12274  * depending on whether the callback name was passed:
12275  * <p>
12276  * <pre><code>
12277 boolean scriptTag = false;
12278 String cb = request.getParameter("callback");
12279 if (cb != null) {
12280     scriptTag = true;
12281     response.setContentType("text/javascript");
12282 } else {
12283     response.setContentType("application/x-json");
12284 }
12285 Writer out = response.getWriter();
12286 if (scriptTag) {
12287     out.write(cb + "(");
12288 }
12289 out.print(dataBlock.toJsonString());
12290 if (scriptTag) {
12291     out.write(");");
12292 }
12293 </pre></code>
12294  *
12295  * @constructor
12296  * @param {Object} config A configuration object.
12297  */
12298 Roo.data.ScriptTagProxy = function(config){
12299     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12300     Roo.apply(this, config);
12301     this.head = document.getElementsByTagName("head")[0];
12302 };
12303
12304 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12305
12306 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12307     /**
12308      * @cfg {String} url The URL from which to request the data object.
12309      */
12310     /**
12311      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12312      */
12313     timeout : 30000,
12314     /**
12315      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12316      * the server the name of the callback function set up by the load call to process the returned data object.
12317      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12318      * javascript output which calls this named function passing the data object as its only parameter.
12319      */
12320     callbackParam : "callback",
12321     /**
12322      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12323      * name to the request.
12324      */
12325     nocache : true,
12326
12327     /**
12328      * Load data from the configured URL, read the data object into
12329      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12330      * process that block using the passed callback.
12331      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12332      * for the request to the remote server.
12333      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12334      * object into a block of Roo.data.Records.
12335      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12336      * The function must be passed <ul>
12337      * <li>The Record block object</li>
12338      * <li>The "arg" argument from the load function</li>
12339      * <li>A boolean success indicator</li>
12340      * </ul>
12341      * @param {Object} scope The scope in which to call the callback
12342      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12343      */
12344     load : function(params, reader, callback, scope, arg){
12345         if(this.fireEvent("beforeload", this, params) !== false){
12346
12347             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12348
12349             var url = this.url;
12350             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12351             if(this.nocache){
12352                 url += "&_dc=" + (new Date().getTime());
12353             }
12354             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12355             var trans = {
12356                 id : transId,
12357                 cb : "stcCallback"+transId,
12358                 scriptId : "stcScript"+transId,
12359                 params : params,
12360                 arg : arg,
12361                 url : url,
12362                 callback : callback,
12363                 scope : scope,
12364                 reader : reader
12365             };
12366             var conn = this;
12367
12368             window[trans.cb] = function(o){
12369                 conn.handleResponse(o, trans);
12370             };
12371
12372             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12373
12374             if(this.autoAbort !== false){
12375                 this.abort();
12376             }
12377
12378             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12379
12380             var script = document.createElement("script");
12381             script.setAttribute("src", url);
12382             script.setAttribute("type", "text/javascript");
12383             script.setAttribute("id", trans.scriptId);
12384             this.head.appendChild(script);
12385
12386             this.trans = trans;
12387         }else{
12388             callback.call(scope||this, null, arg, false);
12389         }
12390     },
12391
12392     // private
12393     isLoading : function(){
12394         return this.trans ? true : false;
12395     },
12396
12397     /**
12398      * Abort the current server request.
12399      */
12400     abort : function(){
12401         if(this.isLoading()){
12402             this.destroyTrans(this.trans);
12403         }
12404     },
12405
12406     // private
12407     destroyTrans : function(trans, isLoaded){
12408         this.head.removeChild(document.getElementById(trans.scriptId));
12409         clearTimeout(trans.timeoutId);
12410         if(isLoaded){
12411             window[trans.cb] = undefined;
12412             try{
12413                 delete window[trans.cb];
12414             }catch(e){}
12415         }else{
12416             // if hasn't been loaded, wait for load to remove it to prevent script error
12417             window[trans.cb] = function(){
12418                 window[trans.cb] = undefined;
12419                 try{
12420                     delete window[trans.cb];
12421                 }catch(e){}
12422             };
12423         }
12424     },
12425
12426     // private
12427     handleResponse : function(o, trans){
12428         this.trans = false;
12429         this.destroyTrans(trans, true);
12430         var result;
12431         try {
12432             result = trans.reader.readRecords(o);
12433         }catch(e){
12434             this.fireEvent("loadexception", this, o, trans.arg, e);
12435             trans.callback.call(trans.scope||window, null, trans.arg, false);
12436             return;
12437         }
12438         this.fireEvent("load", this, o, trans.arg);
12439         trans.callback.call(trans.scope||window, result, trans.arg, true);
12440     },
12441
12442     // private
12443     handleFailure : function(trans){
12444         this.trans = false;
12445         this.destroyTrans(trans, false);
12446         this.fireEvent("loadexception", this, null, trans.arg);
12447         trans.callback.call(trans.scope||window, null, trans.arg, false);
12448     }
12449 });/*
12450  * Based on:
12451  * Ext JS Library 1.1.1
12452  * Copyright(c) 2006-2007, Ext JS, LLC.
12453  *
12454  * Originally Released Under LGPL - original licence link has changed is not relivant.
12455  *
12456  * Fork - LGPL
12457  * <script type="text/javascript">
12458  */
12459
12460 /**
12461  * @class Roo.data.JsonReader
12462  * @extends Roo.data.DataReader
12463  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12464  * based on mappings in a provided Roo.data.Record constructor.
12465  * 
12466  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12467  * in the reply previously. 
12468  * 
12469  * <p>
12470  * Example code:
12471  * <pre><code>
12472 var RecordDef = Roo.data.Record.create([
12473     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12474     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12475 ]);
12476 var myReader = new Roo.data.JsonReader({
12477     totalProperty: "results",    // The property which contains the total dataset size (optional)
12478     root: "rows",                // The property which contains an Array of row objects
12479     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12480 }, RecordDef);
12481 </code></pre>
12482  * <p>
12483  * This would consume a JSON file like this:
12484  * <pre><code>
12485 { 'results': 2, 'rows': [
12486     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12487     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12488 }
12489 </code></pre>
12490  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12491  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12492  * paged from the remote server.
12493  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12494  * @cfg {String} root name of the property which contains the Array of row objects.
12495  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12496  * @cfg {Array} fields Array of field definition objects
12497  * @constructor
12498  * Create a new JsonReader
12499  * @param {Object} meta Metadata configuration options
12500  * @param {Object} recordType Either an Array of field definition objects,
12501  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12502  */
12503 Roo.data.JsonReader = function(meta, recordType){
12504     
12505     meta = meta || {};
12506     // set some defaults:
12507     Roo.applyIf(meta, {
12508         totalProperty: 'total',
12509         successProperty : 'success',
12510         root : 'data',
12511         id : 'id'
12512     });
12513     
12514     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12515 };
12516 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12517     
12518     /**
12519      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12520      * Used by Store query builder to append _requestMeta to params.
12521      * 
12522      */
12523     metaFromRemote : false,
12524     /**
12525      * This method is only used by a DataProxy which has retrieved data from a remote server.
12526      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12527      * @return {Object} data A data block which is used by an Roo.data.Store object as
12528      * a cache of Roo.data.Records.
12529      */
12530     read : function(response){
12531         var json = response.responseText;
12532        
12533         var o = /* eval:var:o */ eval("("+json+")");
12534         if(!o) {
12535             throw {message: "JsonReader.read: Json object not found"};
12536         }
12537         
12538         if(o.metaData){
12539             
12540             delete this.ef;
12541             this.metaFromRemote = true;
12542             this.meta = o.metaData;
12543             this.recordType = Roo.data.Record.create(o.metaData.fields);
12544             this.onMetaChange(this.meta, this.recordType, o);
12545         }
12546         return this.readRecords(o);
12547     },
12548
12549     // private function a store will implement
12550     onMetaChange : function(meta, recordType, o){
12551
12552     },
12553
12554     /**
12555          * @ignore
12556          */
12557     simpleAccess: function(obj, subsc) {
12558         return obj[subsc];
12559     },
12560
12561         /**
12562          * @ignore
12563          */
12564     getJsonAccessor: function(){
12565         var re = /[\[\.]/;
12566         return function(expr) {
12567             try {
12568                 return(re.test(expr))
12569                     ? new Function("obj", "return obj." + expr)
12570                     : function(obj){
12571                         return obj[expr];
12572                     };
12573             } catch(e){}
12574             return Roo.emptyFn;
12575         };
12576     }(),
12577
12578     /**
12579      * Create a data block containing Roo.data.Records from an XML document.
12580      * @param {Object} o An object which contains an Array of row objects in the property specified
12581      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12582      * which contains the total size of the dataset.
12583      * @return {Object} data A data block which is used by an Roo.data.Store object as
12584      * a cache of Roo.data.Records.
12585      */
12586     readRecords : function(o){
12587         /**
12588          * After any data loads, the raw JSON data is available for further custom processing.
12589          * @type Object
12590          */
12591         this.o = o;
12592         var s = this.meta, Record = this.recordType,
12593             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12594
12595 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12596         if (!this.ef) {
12597             if(s.totalProperty) {
12598                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12599                 }
12600                 if(s.successProperty) {
12601                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12602                 }
12603                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12604                 if (s.id) {
12605                         var g = this.getJsonAccessor(s.id);
12606                         this.getId = function(rec) {
12607                                 var r = g(rec);  
12608                                 return (r === undefined || r === "") ? null : r;
12609                         };
12610                 } else {
12611                         this.getId = function(){return null;};
12612                 }
12613             this.ef = [];
12614             for(var jj = 0; jj < fl; jj++){
12615                 f = fi[jj];
12616                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12617                 this.ef[jj] = this.getJsonAccessor(map);
12618             }
12619         }
12620
12621         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12622         if(s.totalProperty){
12623             var vt = parseInt(this.getTotal(o), 10);
12624             if(!isNaN(vt)){
12625                 totalRecords = vt;
12626             }
12627         }
12628         if(s.successProperty){
12629             var vs = this.getSuccess(o);
12630             if(vs === false || vs === 'false'){
12631                 success = false;
12632             }
12633         }
12634         var records = [];
12635         for(var i = 0; i < c; i++){
12636                 var n = root[i];
12637             var values = {};
12638             var id = this.getId(n);
12639             for(var j = 0; j < fl; j++){
12640                 f = fi[j];
12641             var v = this.ef[j](n);
12642             if (!f.convert) {
12643                 Roo.log('missing convert for ' + f.name);
12644                 Roo.log(f);
12645                 continue;
12646             }
12647             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12648             }
12649             var record = new Record(values, id);
12650             record.json = n;
12651             records[i] = record;
12652         }
12653         return {
12654             raw : o,
12655             success : success,
12656             records : records,
12657             totalRecords : totalRecords
12658         };
12659     }
12660 });/*
12661  * Based on:
12662  * Ext JS Library 1.1.1
12663  * Copyright(c) 2006-2007, Ext JS, LLC.
12664  *
12665  * Originally Released Under LGPL - original licence link has changed is not relivant.
12666  *
12667  * Fork - LGPL
12668  * <script type="text/javascript">
12669  */
12670
12671 /**
12672  * @class Roo.data.ArrayReader
12673  * @extends Roo.data.DataReader
12674  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12675  * Each element of that Array represents a row of data fields. The
12676  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12677  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12678  * <p>
12679  * Example code:.
12680  * <pre><code>
12681 var RecordDef = Roo.data.Record.create([
12682     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12683     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12684 ]);
12685 var myReader = new Roo.data.ArrayReader({
12686     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12687 }, RecordDef);
12688 </code></pre>
12689  * <p>
12690  * This would consume an Array like this:
12691  * <pre><code>
12692 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12693   </code></pre>
12694  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12695  * @constructor
12696  * Create a new JsonReader
12697  * @param {Object} meta Metadata configuration options.
12698  * @param {Object} recordType Either an Array of field definition objects
12699  * as specified to {@link Roo.data.Record#create},
12700  * or an {@link Roo.data.Record} object
12701  * created using {@link Roo.data.Record#create}.
12702  */
12703 Roo.data.ArrayReader = function(meta, recordType){
12704     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12705 };
12706
12707 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12708     /**
12709      * Create a data block containing Roo.data.Records from an XML document.
12710      * @param {Object} o An Array of row objects which represents the dataset.
12711      * @return {Object} data A data block which is used by an Roo.data.Store object as
12712      * a cache of Roo.data.Records.
12713      */
12714     readRecords : function(o){
12715         var sid = this.meta ? this.meta.id : null;
12716         var recordType = this.recordType, fields = recordType.prototype.fields;
12717         var records = [];
12718         var root = o;
12719             for(var i = 0; i < root.length; i++){
12720                     var n = root[i];
12721                 var values = {};
12722                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12723                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12724                 var f = fields.items[j];
12725                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12726                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12727                 v = f.convert(v);
12728                 values[f.name] = v;
12729             }
12730                 var record = new recordType(values, id);
12731                 record.json = n;
12732                 records[records.length] = record;
12733             }
12734             return {
12735                 records : records,
12736                 totalRecords : records.length
12737             };
12738     }
12739 });/*
12740  * - LGPL
12741  * * 
12742  */
12743
12744 /**
12745  * @class Roo.bootstrap.ComboBox
12746  * @extends Roo.bootstrap.TriggerField
12747  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12748  * @cfg {Boolean} append (true|false) default false
12749  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12750  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12751  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12752  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12753  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12754  * @cfg {Boolean} animate default true
12755  * @cfg {Boolean} emptyResultText only for touch device
12756  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12757  * @cfg {String} emptyTitle default ''
12758  * @constructor
12759  * Create a new ComboBox.
12760  * @param {Object} config Configuration options
12761  */
12762 Roo.bootstrap.ComboBox = function(config){
12763     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12764     this.addEvents({
12765         /**
12766          * @event expand
12767          * Fires when the dropdown list is expanded
12768         * @param {Roo.bootstrap.ComboBox} combo This combo box
12769         */
12770         'expand' : true,
12771         /**
12772          * @event collapse
12773          * Fires when the dropdown list is collapsed
12774         * @param {Roo.bootstrap.ComboBox} combo This combo box
12775         */
12776         'collapse' : true,
12777         /**
12778          * @event beforeselect
12779          * Fires before a list item is selected. Return false to cancel the selection.
12780         * @param {Roo.bootstrap.ComboBox} combo This combo box
12781         * @param {Roo.data.Record} record The data record returned from the underlying store
12782         * @param {Number} index The index of the selected item in the dropdown list
12783         */
12784         'beforeselect' : true,
12785         /**
12786          * @event select
12787          * Fires when a list item is selected
12788         * @param {Roo.bootstrap.ComboBox} combo This combo box
12789         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12790         * @param {Number} index The index of the selected item in the dropdown list
12791         */
12792         'select' : true,
12793         /**
12794          * @event beforequery
12795          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12796          * The event object passed has these properties:
12797         * @param {Roo.bootstrap.ComboBox} combo This combo box
12798         * @param {String} query The query
12799         * @param {Boolean} forceAll true to force "all" query
12800         * @param {Boolean} cancel true to cancel the query
12801         * @param {Object} e The query event object
12802         */
12803         'beforequery': true,
12804          /**
12805          * @event add
12806          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12807         * @param {Roo.bootstrap.ComboBox} combo This combo box
12808         */
12809         'add' : true,
12810         /**
12811          * @event edit
12812          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12813         * @param {Roo.bootstrap.ComboBox} combo This combo box
12814         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12815         */
12816         'edit' : true,
12817         /**
12818          * @event remove
12819          * Fires when the remove value from the combobox array
12820         * @param {Roo.bootstrap.ComboBox} combo This combo box
12821         */
12822         'remove' : true,
12823         /**
12824          * @event afterremove
12825          * Fires when the remove value from the combobox array
12826         * @param {Roo.bootstrap.ComboBox} combo This combo box
12827         */
12828         'afterremove' : true,
12829         /**
12830          * @event specialfilter
12831          * Fires when specialfilter
12832             * @param {Roo.bootstrap.ComboBox} combo This combo box
12833             */
12834         'specialfilter' : true,
12835         /**
12836          * @event tick
12837          * Fires when tick the element
12838             * @param {Roo.bootstrap.ComboBox} combo This combo box
12839             */
12840         'tick' : true,
12841         /**
12842          * @event touchviewdisplay
12843          * Fires when touch view require special display (default is using displayField)
12844             * @param {Roo.bootstrap.ComboBox} combo This combo box
12845             * @param {Object} cfg set html .
12846             */
12847         'touchviewdisplay' : true
12848         
12849     });
12850     
12851     this.item = [];
12852     this.tickItems = [];
12853     
12854     this.selectedIndex = -1;
12855     if(this.mode == 'local'){
12856         if(config.queryDelay === undefined){
12857             this.queryDelay = 10;
12858         }
12859         if(config.minChars === undefined){
12860             this.minChars = 0;
12861         }
12862     }
12863 };
12864
12865 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12866      
12867     /**
12868      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12869      * rendering into an Roo.Editor, defaults to false)
12870      */
12871     /**
12872      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12873      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12874      */
12875     /**
12876      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12877      */
12878     /**
12879      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12880      * the dropdown list (defaults to undefined, with no header element)
12881      */
12882
12883      /**
12884      * @cfg {String/Roo.Template} tpl The template to use to render the output
12885      */
12886      
12887      /**
12888      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12889      */
12890     listWidth: undefined,
12891     /**
12892      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12893      * mode = 'remote' or 'text' if mode = 'local')
12894      */
12895     displayField: undefined,
12896     
12897     /**
12898      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12899      * mode = 'remote' or 'value' if mode = 'local'). 
12900      * Note: use of a valueField requires the user make a selection
12901      * in order for a value to be mapped.
12902      */
12903     valueField: undefined,
12904     /**
12905      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12906      */
12907     modalTitle : '',
12908     
12909     /**
12910      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12911      * field's data value (defaults to the underlying DOM element's name)
12912      */
12913     hiddenName: undefined,
12914     /**
12915      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12916      */
12917     listClass: '',
12918     /**
12919      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12920      */
12921     selectedClass: 'active',
12922     
12923     /**
12924      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12925      */
12926     shadow:'sides',
12927     /**
12928      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12929      * anchor positions (defaults to 'tl-bl')
12930      */
12931     listAlign: 'tl-bl?',
12932     /**
12933      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12934      */
12935     maxHeight: 300,
12936     /**
12937      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12938      * query specified by the allQuery config option (defaults to 'query')
12939      */
12940     triggerAction: 'query',
12941     /**
12942      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12943      * (defaults to 4, does not apply if editable = false)
12944      */
12945     minChars : 4,
12946     /**
12947      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12948      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12949      */
12950     typeAhead: false,
12951     /**
12952      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12953      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12954      */
12955     queryDelay: 500,
12956     /**
12957      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12958      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12959      */
12960     pageSize: 0,
12961     /**
12962      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12963      * when editable = true (defaults to false)
12964      */
12965     selectOnFocus:false,
12966     /**
12967      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12968      */
12969     queryParam: 'query',
12970     /**
12971      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12972      * when mode = 'remote' (defaults to 'Loading...')
12973      */
12974     loadingText: 'Loading...',
12975     /**
12976      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12977      */
12978     resizable: false,
12979     /**
12980      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12981      */
12982     handleHeight : 8,
12983     /**
12984      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12985      * traditional select (defaults to true)
12986      */
12987     editable: true,
12988     /**
12989      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12990      */
12991     allQuery: '',
12992     /**
12993      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12994      */
12995     mode: 'remote',
12996     /**
12997      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12998      * listWidth has a higher value)
12999      */
13000     minListWidth : 70,
13001     /**
13002      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13003      * allow the user to set arbitrary text into the field (defaults to false)
13004      */
13005     forceSelection:false,
13006     /**
13007      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13008      * if typeAhead = true (defaults to 250)
13009      */
13010     typeAheadDelay : 250,
13011     /**
13012      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13013      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13014      */
13015     valueNotFoundText : undefined,
13016     /**
13017      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13018      */
13019     blockFocus : false,
13020     
13021     /**
13022      * @cfg {Boolean} disableClear Disable showing of clear button.
13023      */
13024     disableClear : false,
13025     /**
13026      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13027      */
13028     alwaysQuery : false,
13029     
13030     /**
13031      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13032      */
13033     multiple : false,
13034     
13035     /**
13036      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13037      */
13038     invalidClass : "has-warning",
13039     
13040     /**
13041      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13042      */
13043     validClass : "has-success",
13044     
13045     /**
13046      * @cfg {Boolean} specialFilter (true|false) special filter default false
13047      */
13048     specialFilter : false,
13049     
13050     /**
13051      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13052      */
13053     mobileTouchView : true,
13054     
13055     /**
13056      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13057      */
13058     useNativeIOS : false,
13059     
13060     /**
13061      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13062      */
13063     mobile_restrict_height : false,
13064     
13065     ios_options : false,
13066     
13067     //private
13068     addicon : false,
13069     editicon: false,
13070     
13071     page: 0,
13072     hasQuery: false,
13073     append: false,
13074     loadNext: false,
13075     autoFocus : true,
13076     tickable : false,
13077     btnPosition : 'right',
13078     triggerList : true,
13079     showToggleBtn : true,
13080     animate : true,
13081     emptyResultText: 'Empty',
13082     triggerText : 'Select',
13083     emptyTitle : '',
13084     
13085     // element that contains real text value.. (when hidden is used..)
13086     
13087     getAutoCreate : function()
13088     {   
13089         var cfg = false;
13090         //render
13091         /*
13092          * Render classic select for iso
13093          */
13094         
13095         if(Roo.isIOS && this.useNativeIOS){
13096             cfg = this.getAutoCreateNativeIOS();
13097             return cfg;
13098         }
13099         
13100         /*
13101          * Touch Devices
13102          */
13103         
13104         if(Roo.isTouch && this.mobileTouchView){
13105             cfg = this.getAutoCreateTouchView();
13106             return cfg;;
13107         }
13108         
13109         /*
13110          *  Normal ComboBox
13111          */
13112         if(!this.tickable){
13113             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13114             return cfg;
13115         }
13116         
13117         /*
13118          *  ComboBox with tickable selections
13119          */
13120              
13121         var align = this.labelAlign || this.parentLabelAlign();
13122         
13123         cfg = {
13124             cls : 'form-group roo-combobox-tickable' //input-group
13125         };
13126         
13127         var btn_text_select = '';
13128         var btn_text_done = '';
13129         var btn_text_cancel = '';
13130         
13131         if (this.btn_text_show) {
13132             btn_text_select = 'Select';
13133             btn_text_done = 'Done';
13134             btn_text_cancel = 'Cancel'; 
13135         }
13136         
13137         var buttons = {
13138             tag : 'div',
13139             cls : 'tickable-buttons',
13140             cn : [
13141                 {
13142                     tag : 'button',
13143                     type : 'button',
13144                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13145                     //html : this.triggerText
13146                     html: btn_text_select
13147                 },
13148                 {
13149                     tag : 'button',
13150                     type : 'button',
13151                     name : 'ok',
13152                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13153                     //html : 'Done'
13154                     html: btn_text_done
13155                 },
13156                 {
13157                     tag : 'button',
13158                     type : 'button',
13159                     name : 'cancel',
13160                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13161                     //html : 'Cancel'
13162                     html: btn_text_cancel
13163                 }
13164             ]
13165         };
13166         
13167         if(this.editable){
13168             buttons.cn.unshift({
13169                 tag: 'input',
13170                 cls: 'roo-select2-search-field-input'
13171             });
13172         }
13173         
13174         var _this = this;
13175         
13176         Roo.each(buttons.cn, function(c){
13177             if (_this.size) {
13178                 c.cls += ' btn-' + _this.size;
13179             }
13180
13181             if (_this.disabled) {
13182                 c.disabled = true;
13183             }
13184         });
13185         
13186         var box = {
13187             tag: 'div',
13188             cn: [
13189                 {
13190                     tag: 'input',
13191                     type : 'hidden',
13192                     cls: 'form-hidden-field'
13193                 },
13194                 {
13195                     tag: 'ul',
13196                     cls: 'roo-select2-choices',
13197                     cn:[
13198                         {
13199                             tag: 'li',
13200                             cls: 'roo-select2-search-field',
13201                             cn: [
13202                                 buttons
13203                             ]
13204                         }
13205                     ]
13206                 }
13207             ]
13208         };
13209         
13210         var combobox = {
13211             cls: 'roo-select2-container input-group roo-select2-container-multi',
13212             cn: [
13213                 box
13214 //                {
13215 //                    tag: 'ul',
13216 //                    cls: 'typeahead typeahead-long dropdown-menu',
13217 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13218 //                }
13219             ]
13220         };
13221         
13222         if(this.hasFeedback && !this.allowBlank){
13223             
13224             var feedback = {
13225                 tag: 'span',
13226                 cls: 'glyphicon form-control-feedback'
13227             };
13228
13229             combobox.cn.push(feedback);
13230         }
13231         
13232         
13233         if (align ==='left' && this.fieldLabel.length) {
13234             
13235             cfg.cls += ' roo-form-group-label-left';
13236             
13237             cfg.cn = [
13238                 {
13239                     tag : 'i',
13240                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13241                     tooltip : 'This field is required'
13242                 },
13243                 {
13244                     tag: 'label',
13245                     'for' :  id,
13246                     cls : 'control-label',
13247                     html : this.fieldLabel
13248
13249                 },
13250                 {
13251                     cls : "", 
13252                     cn: [
13253                         combobox
13254                     ]
13255                 }
13256
13257             ];
13258             
13259             var labelCfg = cfg.cn[1];
13260             var contentCfg = cfg.cn[2];
13261             
13262
13263             if(this.indicatorpos == 'right'){
13264                 
13265                 cfg.cn = [
13266                     {
13267                         tag: 'label',
13268                         'for' :  id,
13269                         cls : 'control-label',
13270                         cn : [
13271                             {
13272                                 tag : 'span',
13273                                 html : this.fieldLabel
13274                             },
13275                             {
13276                                 tag : 'i',
13277                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13278                                 tooltip : 'This field is required'
13279                             }
13280                         ]
13281                     },
13282                     {
13283                         cls : "",
13284                         cn: [
13285                             combobox
13286                         ]
13287                     }
13288
13289                 ];
13290                 
13291                 
13292                 
13293                 labelCfg = cfg.cn[0];
13294                 contentCfg = cfg.cn[1];
13295             
13296             }
13297             
13298             if(this.labelWidth > 12){
13299                 labelCfg.style = "width: " + this.labelWidth + 'px';
13300             }
13301             
13302             if(this.labelWidth < 13 && this.labelmd == 0){
13303                 this.labelmd = this.labelWidth;
13304             }
13305             
13306             if(this.labellg > 0){
13307                 labelCfg.cls += ' col-lg-' + this.labellg;
13308                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13309             }
13310             
13311             if(this.labelmd > 0){
13312                 labelCfg.cls += ' col-md-' + this.labelmd;
13313                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13314             }
13315             
13316             if(this.labelsm > 0){
13317                 labelCfg.cls += ' col-sm-' + this.labelsm;
13318                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13319             }
13320             
13321             if(this.labelxs > 0){
13322                 labelCfg.cls += ' col-xs-' + this.labelxs;
13323                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13324             }
13325                 
13326                 
13327         } else if ( this.fieldLabel.length) {
13328 //                Roo.log(" label");
13329                  cfg.cn = [
13330                     {
13331                         tag : 'i',
13332                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13333                         tooltip : 'This field is required'
13334                     },
13335                     {
13336                         tag: 'label',
13337                         //cls : 'input-group-addon',
13338                         html : this.fieldLabel
13339                     },
13340                     combobox
13341                 ];
13342                 
13343                 if(this.indicatorpos == 'right'){
13344                     cfg.cn = [
13345                         {
13346                             tag: 'label',
13347                             //cls : 'input-group-addon',
13348                             html : this.fieldLabel
13349                         },
13350                         {
13351                             tag : 'i',
13352                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13353                             tooltip : 'This field is required'
13354                         },
13355                         combobox
13356                     ];
13357                     
13358                 }
13359
13360         } else {
13361             
13362 //                Roo.log(" no label && no align");
13363                 cfg = combobox
13364                      
13365                 
13366         }
13367          
13368         var settings=this;
13369         ['xs','sm','md','lg'].map(function(size){
13370             if (settings[size]) {
13371                 cfg.cls += ' col-' + size + '-' + settings[size];
13372             }
13373         });
13374         
13375         return cfg;
13376         
13377     },
13378     
13379     _initEventsCalled : false,
13380     
13381     // private
13382     initEvents: function()
13383     {   
13384         if (this._initEventsCalled) { // as we call render... prevent looping...
13385             return;
13386         }
13387         this._initEventsCalled = true;
13388         
13389         if (!this.store) {
13390             throw "can not find store for combo";
13391         }
13392         
13393         this.indicator = this.indicatorEl();
13394         
13395         this.store = Roo.factory(this.store, Roo.data);
13396         this.store.parent = this;
13397         
13398         // if we are building from html. then this element is so complex, that we can not really
13399         // use the rendered HTML.
13400         // so we have to trash and replace the previous code.
13401         if (Roo.XComponent.build_from_html) {
13402             // remove this element....
13403             var e = this.el.dom, k=0;
13404             while (e ) { e = e.previousSibling;  ++k;}
13405
13406             this.el.remove();
13407             
13408             this.el=false;
13409             this.rendered = false;
13410             
13411             this.render(this.parent().getChildContainer(true), k);
13412         }
13413         
13414         if(Roo.isIOS && this.useNativeIOS){
13415             this.initIOSView();
13416             return;
13417         }
13418         
13419         /*
13420          * Touch Devices
13421          */
13422         
13423         if(Roo.isTouch && this.mobileTouchView){
13424             this.initTouchView();
13425             return;
13426         }
13427         
13428         if(this.tickable){
13429             this.initTickableEvents();
13430             return;
13431         }
13432         
13433         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13434         
13435         if(this.hiddenName){
13436             
13437             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13438             
13439             this.hiddenField.dom.value =
13440                 this.hiddenValue !== undefined ? this.hiddenValue :
13441                 this.value !== undefined ? this.value : '';
13442
13443             // prevent input submission
13444             this.el.dom.removeAttribute('name');
13445             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13446              
13447              
13448         }
13449         //if(Roo.isGecko){
13450         //    this.el.dom.setAttribute('autocomplete', 'off');
13451         //}
13452         
13453         var cls = 'x-combo-list';
13454         
13455         //this.list = new Roo.Layer({
13456         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13457         //});
13458         
13459         var _this = this;
13460         
13461         (function(){
13462             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13463             _this.list.setWidth(lw);
13464         }).defer(100);
13465         
13466         this.list.on('mouseover', this.onViewOver, this);
13467         this.list.on('mousemove', this.onViewMove, this);
13468         this.list.on('scroll', this.onViewScroll, this);
13469         
13470         /*
13471         this.list.swallowEvent('mousewheel');
13472         this.assetHeight = 0;
13473
13474         if(this.title){
13475             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13476             this.assetHeight += this.header.getHeight();
13477         }
13478
13479         this.innerList = this.list.createChild({cls:cls+'-inner'});
13480         this.innerList.on('mouseover', this.onViewOver, this);
13481         this.innerList.on('mousemove', this.onViewMove, this);
13482         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13483         
13484         if(this.allowBlank && !this.pageSize && !this.disableClear){
13485             this.footer = this.list.createChild({cls:cls+'-ft'});
13486             this.pageTb = new Roo.Toolbar(this.footer);
13487            
13488         }
13489         if(this.pageSize){
13490             this.footer = this.list.createChild({cls:cls+'-ft'});
13491             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13492                     {pageSize: this.pageSize});
13493             
13494         }
13495         
13496         if (this.pageTb && this.allowBlank && !this.disableClear) {
13497             var _this = this;
13498             this.pageTb.add(new Roo.Toolbar.Fill(), {
13499                 cls: 'x-btn-icon x-btn-clear',
13500                 text: '&#160;',
13501                 handler: function()
13502                 {
13503                     _this.collapse();
13504                     _this.clearValue();
13505                     _this.onSelect(false, -1);
13506                 }
13507             });
13508         }
13509         if (this.footer) {
13510             this.assetHeight += this.footer.getHeight();
13511         }
13512         */
13513             
13514         if(!this.tpl){
13515             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13516         }
13517
13518         this.view = new Roo.View(this.list, this.tpl, {
13519             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13520         });
13521         //this.view.wrapEl.setDisplayed(false);
13522         this.view.on('click', this.onViewClick, this);
13523         
13524         
13525         this.store.on('beforeload', this.onBeforeLoad, this);
13526         this.store.on('load', this.onLoad, this);
13527         this.store.on('loadexception', this.onLoadException, this);
13528         /*
13529         if(this.resizable){
13530             this.resizer = new Roo.Resizable(this.list,  {
13531                pinned:true, handles:'se'
13532             });
13533             this.resizer.on('resize', function(r, w, h){
13534                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13535                 this.listWidth = w;
13536                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13537                 this.restrictHeight();
13538             }, this);
13539             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13540         }
13541         */
13542         if(!this.editable){
13543             this.editable = true;
13544             this.setEditable(false);
13545         }
13546         
13547         /*
13548         
13549         if (typeof(this.events.add.listeners) != 'undefined') {
13550             
13551             this.addicon = this.wrap.createChild(
13552                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13553        
13554             this.addicon.on('click', function(e) {
13555                 this.fireEvent('add', this);
13556             }, this);
13557         }
13558         if (typeof(this.events.edit.listeners) != 'undefined') {
13559             
13560             this.editicon = this.wrap.createChild(
13561                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13562             if (this.addicon) {
13563                 this.editicon.setStyle('margin-left', '40px');
13564             }
13565             this.editicon.on('click', function(e) {
13566                 
13567                 // we fire even  if inothing is selected..
13568                 this.fireEvent('edit', this, this.lastData );
13569                 
13570             }, this);
13571         }
13572         */
13573         
13574         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13575             "up" : function(e){
13576                 this.inKeyMode = true;
13577                 this.selectPrev();
13578             },
13579
13580             "down" : function(e){
13581                 if(!this.isExpanded()){
13582                     this.onTriggerClick();
13583                 }else{
13584                     this.inKeyMode = true;
13585                     this.selectNext();
13586                 }
13587             },
13588
13589             "enter" : function(e){
13590 //                this.onViewClick();
13591                 //return true;
13592                 this.collapse();
13593                 
13594                 if(this.fireEvent("specialkey", this, e)){
13595                     this.onViewClick(false);
13596                 }
13597                 
13598                 return true;
13599             },
13600
13601             "esc" : function(e){
13602                 this.collapse();
13603             },
13604
13605             "tab" : function(e){
13606                 this.collapse();
13607                 
13608                 if(this.fireEvent("specialkey", this, e)){
13609                     this.onViewClick(false);
13610                 }
13611                 
13612                 return true;
13613             },
13614
13615             scope : this,
13616
13617             doRelay : function(foo, bar, hname){
13618                 if(hname == 'down' || this.scope.isExpanded()){
13619                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13620                 }
13621                 return true;
13622             },
13623
13624             forceKeyDown: true
13625         });
13626         
13627         
13628         this.queryDelay = Math.max(this.queryDelay || 10,
13629                 this.mode == 'local' ? 10 : 250);
13630         
13631         
13632         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13633         
13634         if(this.typeAhead){
13635             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13636         }
13637         if(this.editable !== false){
13638             this.inputEl().on("keyup", this.onKeyUp, this);
13639         }
13640         if(this.forceSelection){
13641             this.inputEl().on('blur', this.doForce, this);
13642         }
13643         
13644         if(this.multiple){
13645             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13646             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13647         }
13648     },
13649     
13650     initTickableEvents: function()
13651     {   
13652         this.createList();
13653         
13654         if(this.hiddenName){
13655             
13656             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13657             
13658             this.hiddenField.dom.value =
13659                 this.hiddenValue !== undefined ? this.hiddenValue :
13660                 this.value !== undefined ? this.value : '';
13661
13662             // prevent input submission
13663             this.el.dom.removeAttribute('name');
13664             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13665              
13666              
13667         }
13668         
13669 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13670         
13671         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13672         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13673         if(this.triggerList){
13674             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13675         }
13676          
13677         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13678         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13679         
13680         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13681         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13682         
13683         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13684         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13685         
13686         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13687         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13688         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13689         
13690         this.okBtn.hide();
13691         this.cancelBtn.hide();
13692         
13693         var _this = this;
13694         
13695         (function(){
13696             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13697             _this.list.setWidth(lw);
13698         }).defer(100);
13699         
13700         this.list.on('mouseover', this.onViewOver, this);
13701         this.list.on('mousemove', this.onViewMove, this);
13702         
13703         this.list.on('scroll', this.onViewScroll, this);
13704         
13705         if(!this.tpl){
13706             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13707                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13708         }
13709
13710         this.view = new Roo.View(this.list, this.tpl, {
13711             singleSelect:true,
13712             tickable:true,
13713             parent:this,
13714             store: this.store,
13715             selectedClass: this.selectedClass
13716         });
13717         
13718         //this.view.wrapEl.setDisplayed(false);
13719         this.view.on('click', this.onViewClick, this);
13720         
13721         
13722         
13723         this.store.on('beforeload', this.onBeforeLoad, this);
13724         this.store.on('load', this.onLoad, this);
13725         this.store.on('loadexception', this.onLoadException, this);
13726         
13727         if(this.editable){
13728             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13729                 "up" : function(e){
13730                     this.inKeyMode = true;
13731                     this.selectPrev();
13732                 },
13733
13734                 "down" : function(e){
13735                     this.inKeyMode = true;
13736                     this.selectNext();
13737                 },
13738
13739                 "enter" : function(e){
13740                     if(this.fireEvent("specialkey", this, e)){
13741                         this.onViewClick(false);
13742                     }
13743                     
13744                     return true;
13745                 },
13746
13747                 "esc" : function(e){
13748                     this.onTickableFooterButtonClick(e, false, false);
13749                 },
13750
13751                 "tab" : function(e){
13752                     this.fireEvent("specialkey", this, e);
13753                     
13754                     this.onTickableFooterButtonClick(e, false, false);
13755                     
13756                     return true;
13757                 },
13758
13759                 scope : this,
13760
13761                 doRelay : function(e, fn, key){
13762                     if(this.scope.isExpanded()){
13763                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13764                     }
13765                     return true;
13766                 },
13767
13768                 forceKeyDown: true
13769             });
13770         }
13771         
13772         this.queryDelay = Math.max(this.queryDelay || 10,
13773                 this.mode == 'local' ? 10 : 250);
13774         
13775         
13776         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13777         
13778         if(this.typeAhead){
13779             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13780         }
13781         
13782         if(this.editable !== false){
13783             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13784         }
13785         
13786         this.indicator = this.indicatorEl();
13787         
13788         if(this.indicator){
13789             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13790             this.indicator.hide();
13791         }
13792         
13793     },
13794
13795     onDestroy : function(){
13796         if(this.view){
13797             this.view.setStore(null);
13798             this.view.el.removeAllListeners();
13799             this.view.el.remove();
13800             this.view.purgeListeners();
13801         }
13802         if(this.list){
13803             this.list.dom.innerHTML  = '';
13804         }
13805         
13806         if(this.store){
13807             this.store.un('beforeload', this.onBeforeLoad, this);
13808             this.store.un('load', this.onLoad, this);
13809             this.store.un('loadexception', this.onLoadException, this);
13810         }
13811         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13812     },
13813
13814     // private
13815     fireKey : function(e){
13816         if(e.isNavKeyPress() && !this.list.isVisible()){
13817             this.fireEvent("specialkey", this, e);
13818         }
13819     },
13820
13821     // private
13822     onResize: function(w, h){
13823 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13824 //        
13825 //        if(typeof w != 'number'){
13826 //            // we do not handle it!?!?
13827 //            return;
13828 //        }
13829 //        var tw = this.trigger.getWidth();
13830 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13831 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13832 //        var x = w - tw;
13833 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13834 //            
13835 //        //this.trigger.setStyle('left', x+'px');
13836 //        
13837 //        if(this.list && this.listWidth === undefined){
13838 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13839 //            this.list.setWidth(lw);
13840 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13841 //        }
13842         
13843     
13844         
13845     },
13846
13847     /**
13848      * Allow or prevent the user from directly editing the field text.  If false is passed,
13849      * the user will only be able to select from the items defined in the dropdown list.  This method
13850      * is the runtime equivalent of setting the 'editable' config option at config time.
13851      * @param {Boolean} value True to allow the user to directly edit the field text
13852      */
13853     setEditable : function(value){
13854         if(value == this.editable){
13855             return;
13856         }
13857         this.editable = value;
13858         if(!value){
13859             this.inputEl().dom.setAttribute('readOnly', true);
13860             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13861             this.inputEl().addClass('x-combo-noedit');
13862         }else{
13863             this.inputEl().dom.setAttribute('readOnly', false);
13864             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13865             this.inputEl().removeClass('x-combo-noedit');
13866         }
13867     },
13868
13869     // private
13870     
13871     onBeforeLoad : function(combo,opts){
13872         if(!this.hasFocus){
13873             return;
13874         }
13875          if (!opts.add) {
13876             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13877          }
13878         this.restrictHeight();
13879         this.selectedIndex = -1;
13880     },
13881
13882     // private
13883     onLoad : function(){
13884         
13885         this.hasQuery = false;
13886         
13887         if(!this.hasFocus){
13888             return;
13889         }
13890         
13891         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13892             this.loading.hide();
13893         }
13894         
13895         if(this.store.getCount() > 0){
13896             
13897             this.expand();
13898             this.restrictHeight();
13899             if(this.lastQuery == this.allQuery){
13900                 if(this.editable && !this.tickable){
13901                     this.inputEl().dom.select();
13902                 }
13903                 
13904                 if(
13905                     !this.selectByValue(this.value, true) &&
13906                     this.autoFocus && 
13907                     (
13908                         !this.store.lastOptions ||
13909                         typeof(this.store.lastOptions.add) == 'undefined' || 
13910                         this.store.lastOptions.add != true
13911                     )
13912                 ){
13913                     this.select(0, true);
13914                 }
13915             }else{
13916                 if(this.autoFocus){
13917                     this.selectNext();
13918                 }
13919                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13920                     this.taTask.delay(this.typeAheadDelay);
13921                 }
13922             }
13923         }else{
13924             this.onEmptyResults();
13925         }
13926         
13927         //this.el.focus();
13928     },
13929     // private
13930     onLoadException : function()
13931     {
13932         this.hasQuery = false;
13933         
13934         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13935             this.loading.hide();
13936         }
13937         
13938         if(this.tickable && this.editable){
13939             return;
13940         }
13941         
13942         this.collapse();
13943         // only causes errors at present
13944         //Roo.log(this.store.reader.jsonData);
13945         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13946             // fixme
13947             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13948         //}
13949         
13950         
13951     },
13952     // private
13953     onTypeAhead : function(){
13954         if(this.store.getCount() > 0){
13955             var r = this.store.getAt(0);
13956             var newValue = r.data[this.displayField];
13957             var len = newValue.length;
13958             var selStart = this.getRawValue().length;
13959             
13960             if(selStart != len){
13961                 this.setRawValue(newValue);
13962                 this.selectText(selStart, newValue.length);
13963             }
13964         }
13965     },
13966
13967     // private
13968     onSelect : function(record, index){
13969         
13970         if(this.fireEvent('beforeselect', this, record, index) !== false){
13971         
13972             this.setFromData(index > -1 ? record.data : false);
13973             
13974             this.collapse();
13975             this.fireEvent('select', this, record, index);
13976         }
13977     },
13978
13979     /**
13980      * Returns the currently selected field value or empty string if no value is set.
13981      * @return {String} value The selected value
13982      */
13983     getValue : function()
13984     {
13985         if(Roo.isIOS && this.useNativeIOS){
13986             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13987         }
13988         
13989         if(this.multiple){
13990             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13991         }
13992         
13993         if(this.valueField){
13994             return typeof this.value != 'undefined' ? this.value : '';
13995         }else{
13996             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13997         }
13998     },
13999     
14000     getRawValue : function()
14001     {
14002         if(Roo.isIOS && this.useNativeIOS){
14003             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14004         }
14005         
14006         var v = this.inputEl().getValue();
14007         
14008         return v;
14009     },
14010
14011     /**
14012      * Clears any text/value currently set in the field
14013      */
14014     clearValue : function(){
14015         
14016         if(this.hiddenField){
14017             this.hiddenField.dom.value = '';
14018         }
14019         this.value = '';
14020         this.setRawValue('');
14021         this.lastSelectionText = '';
14022         this.lastData = false;
14023         
14024         var close = this.closeTriggerEl();
14025         
14026         if(close){
14027             close.hide();
14028         }
14029         
14030         this.validate();
14031         
14032     },
14033
14034     /**
14035      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14036      * will be displayed in the field.  If the value does not match the data value of an existing item,
14037      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14038      * Otherwise the field will be blank (although the value will still be set).
14039      * @param {String} value The value to match
14040      */
14041     setValue : function(v)
14042     {
14043         if(Roo.isIOS && this.useNativeIOS){
14044             this.setIOSValue(v);
14045             return;
14046         }
14047         
14048         if(this.multiple){
14049             this.syncValue();
14050             return;
14051         }
14052         
14053         var text = v;
14054         if(this.valueField){
14055             var r = this.findRecord(this.valueField, v);
14056             if(r){
14057                 text = r.data[this.displayField];
14058             }else if(this.valueNotFoundText !== undefined){
14059                 text = this.valueNotFoundText;
14060             }
14061         }
14062         this.lastSelectionText = text;
14063         if(this.hiddenField){
14064             this.hiddenField.dom.value = v;
14065         }
14066         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14067         this.value = v;
14068         
14069         var close = this.closeTriggerEl();
14070         
14071         if(close){
14072             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14073         }
14074         
14075         this.validate();
14076     },
14077     /**
14078      * @property {Object} the last set data for the element
14079      */
14080     
14081     lastData : false,
14082     /**
14083      * Sets the value of the field based on a object which is related to the record format for the store.
14084      * @param {Object} value the value to set as. or false on reset?
14085      */
14086     setFromData : function(o){
14087         
14088         if(this.multiple){
14089             this.addItem(o);
14090             return;
14091         }
14092             
14093         var dv = ''; // display value
14094         var vv = ''; // value value..
14095         this.lastData = o;
14096         if (this.displayField) {
14097             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14098         } else {
14099             // this is an error condition!!!
14100             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14101         }
14102         
14103         if(this.valueField){
14104             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14105         }
14106         
14107         var close = this.closeTriggerEl();
14108         
14109         if(close){
14110             if(dv.length || vv * 1 > 0){
14111                 close.show() ;
14112                 this.blockFocus=true;
14113             } else {
14114                 close.hide();
14115             }             
14116         }
14117         
14118         if(this.hiddenField){
14119             this.hiddenField.dom.value = vv;
14120             
14121             this.lastSelectionText = dv;
14122             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14123             this.value = vv;
14124             return;
14125         }
14126         // no hidden field.. - we store the value in 'value', but still display
14127         // display field!!!!
14128         this.lastSelectionText = dv;
14129         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14130         this.value = vv;
14131         
14132         
14133         
14134     },
14135     // private
14136     reset : function(){
14137         // overridden so that last data is reset..
14138         
14139         if(this.multiple){
14140             this.clearItem();
14141             return;
14142         }
14143         
14144         this.setValue(this.originalValue);
14145         //this.clearInvalid();
14146         this.lastData = false;
14147         if (this.view) {
14148             this.view.clearSelections();
14149         }
14150         
14151         this.validate();
14152     },
14153     // private
14154     findRecord : function(prop, value){
14155         var record;
14156         if(this.store.getCount() > 0){
14157             this.store.each(function(r){
14158                 if(r.data[prop] == value){
14159                     record = r;
14160                     return false;
14161                 }
14162                 return true;
14163             });
14164         }
14165         return record;
14166     },
14167     
14168     getName: function()
14169     {
14170         // returns hidden if it's set..
14171         if (!this.rendered) {return ''};
14172         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14173         
14174     },
14175     // private
14176     onViewMove : function(e, t){
14177         this.inKeyMode = false;
14178     },
14179
14180     // private
14181     onViewOver : function(e, t){
14182         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14183             return;
14184         }
14185         var item = this.view.findItemFromChild(t);
14186         
14187         if(item){
14188             var index = this.view.indexOf(item);
14189             this.select(index, false);
14190         }
14191     },
14192
14193     // private
14194     onViewClick : function(view, doFocus, el, e)
14195     {
14196         var index = this.view.getSelectedIndexes()[0];
14197         
14198         var r = this.store.getAt(index);
14199         
14200         if(this.tickable){
14201             
14202             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14203                 return;
14204             }
14205             
14206             var rm = false;
14207             var _this = this;
14208             
14209             Roo.each(this.tickItems, function(v,k){
14210                 
14211                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14212                     Roo.log(v);
14213                     _this.tickItems.splice(k, 1);
14214                     
14215                     if(typeof(e) == 'undefined' && view == false){
14216                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14217                     }
14218                     
14219                     rm = true;
14220                     return;
14221                 }
14222             });
14223             
14224             if(rm){
14225                 return;
14226             }
14227             
14228             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14229                 this.tickItems.push(r.data);
14230             }
14231             
14232             if(typeof(e) == 'undefined' && view == false){
14233                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14234             }
14235                     
14236             return;
14237         }
14238         
14239         if(r){
14240             this.onSelect(r, index);
14241         }
14242         if(doFocus !== false && !this.blockFocus){
14243             this.inputEl().focus();
14244         }
14245     },
14246
14247     // private
14248     restrictHeight : function(){
14249         //this.innerList.dom.style.height = '';
14250         //var inner = this.innerList.dom;
14251         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14252         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14253         //this.list.beginUpdate();
14254         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14255         this.list.alignTo(this.inputEl(), this.listAlign);
14256         this.list.alignTo(this.inputEl(), this.listAlign);
14257         //this.list.endUpdate();
14258     },
14259
14260     // private
14261     onEmptyResults : function(){
14262         
14263         if(this.tickable && this.editable){
14264             this.hasFocus = false;
14265             this.restrictHeight();
14266             return;
14267         }
14268         
14269         this.collapse();
14270     },
14271
14272     /**
14273      * Returns true if the dropdown list is expanded, else false.
14274      */
14275     isExpanded : function(){
14276         return this.list.isVisible();
14277     },
14278
14279     /**
14280      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14281      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14282      * @param {String} value The data value of the item to select
14283      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14284      * selected item if it is not currently in view (defaults to true)
14285      * @return {Boolean} True if the value matched an item in the list, else false
14286      */
14287     selectByValue : function(v, scrollIntoView){
14288         if(v !== undefined && v !== null){
14289             var r = this.findRecord(this.valueField || this.displayField, v);
14290             if(r){
14291                 this.select(this.store.indexOf(r), scrollIntoView);
14292                 return true;
14293             }
14294         }
14295         return false;
14296     },
14297
14298     /**
14299      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14300      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14301      * @param {Number} index The zero-based index of the list item to select
14302      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14303      * selected item if it is not currently in view (defaults to true)
14304      */
14305     select : function(index, scrollIntoView){
14306         this.selectedIndex = index;
14307         this.view.select(index);
14308         if(scrollIntoView !== false){
14309             var el = this.view.getNode(index);
14310             /*
14311              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14312              */
14313             if(el){
14314                 this.list.scrollChildIntoView(el, false);
14315             }
14316         }
14317     },
14318
14319     // private
14320     selectNext : function(){
14321         var ct = this.store.getCount();
14322         if(ct > 0){
14323             if(this.selectedIndex == -1){
14324                 this.select(0);
14325             }else if(this.selectedIndex < ct-1){
14326                 this.select(this.selectedIndex+1);
14327             }
14328         }
14329     },
14330
14331     // private
14332     selectPrev : function(){
14333         var ct = this.store.getCount();
14334         if(ct > 0){
14335             if(this.selectedIndex == -1){
14336                 this.select(0);
14337             }else if(this.selectedIndex != 0){
14338                 this.select(this.selectedIndex-1);
14339             }
14340         }
14341     },
14342
14343     // private
14344     onKeyUp : function(e){
14345         if(this.editable !== false && !e.isSpecialKey()){
14346             this.lastKey = e.getKey();
14347             this.dqTask.delay(this.queryDelay);
14348         }
14349     },
14350
14351     // private
14352     validateBlur : function(){
14353         return !this.list || !this.list.isVisible();   
14354     },
14355
14356     // private
14357     initQuery : function(){
14358         
14359         var v = this.getRawValue();
14360         
14361         if(this.tickable && this.editable){
14362             v = this.tickableInputEl().getValue();
14363         }
14364         
14365         this.doQuery(v);
14366     },
14367
14368     // private
14369     doForce : function(){
14370         if(this.inputEl().dom.value.length > 0){
14371             this.inputEl().dom.value =
14372                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14373              
14374         }
14375     },
14376
14377     /**
14378      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14379      * query allowing the query action to be canceled if needed.
14380      * @param {String} query The SQL query to execute
14381      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14382      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14383      * saved in the current store (defaults to false)
14384      */
14385     doQuery : function(q, forceAll){
14386         
14387         if(q === undefined || q === null){
14388             q = '';
14389         }
14390         var qe = {
14391             query: q,
14392             forceAll: forceAll,
14393             combo: this,
14394             cancel:false
14395         };
14396         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14397             return false;
14398         }
14399         q = qe.query;
14400         
14401         forceAll = qe.forceAll;
14402         if(forceAll === true || (q.length >= this.minChars)){
14403             
14404             this.hasQuery = true;
14405             
14406             if(this.lastQuery != q || this.alwaysQuery){
14407                 this.lastQuery = q;
14408                 if(this.mode == 'local'){
14409                     this.selectedIndex = -1;
14410                     if(forceAll){
14411                         this.store.clearFilter();
14412                     }else{
14413                         
14414                         if(this.specialFilter){
14415                             this.fireEvent('specialfilter', this);
14416                             this.onLoad();
14417                             return;
14418                         }
14419                         
14420                         this.store.filter(this.displayField, q);
14421                     }
14422                     
14423                     this.store.fireEvent("datachanged", this.store);
14424                     
14425                     this.onLoad();
14426                     
14427                     
14428                 }else{
14429                     
14430                     this.store.baseParams[this.queryParam] = q;
14431                     
14432                     var options = {params : this.getParams(q)};
14433                     
14434                     if(this.loadNext){
14435                         options.add = true;
14436                         options.params.start = this.page * this.pageSize;
14437                     }
14438                     
14439                     this.store.load(options);
14440                     
14441                     /*
14442                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14443                      *  we should expand the list on onLoad
14444                      *  so command out it
14445                      */
14446 //                    this.expand();
14447                 }
14448             }else{
14449                 this.selectedIndex = -1;
14450                 this.onLoad();   
14451             }
14452         }
14453         
14454         this.loadNext = false;
14455     },
14456     
14457     // private
14458     getParams : function(q){
14459         var p = {};
14460         //p[this.queryParam] = q;
14461         
14462         if(this.pageSize){
14463             p.start = 0;
14464             p.limit = this.pageSize;
14465         }
14466         return p;
14467     },
14468
14469     /**
14470      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14471      */
14472     collapse : function(){
14473         if(!this.isExpanded()){
14474             return;
14475         }
14476         
14477         this.list.hide();
14478         
14479         this.hasFocus = false;
14480         
14481         if(this.tickable){
14482             this.okBtn.hide();
14483             this.cancelBtn.hide();
14484             this.trigger.show();
14485             
14486             if(this.editable){
14487                 this.tickableInputEl().dom.value = '';
14488                 this.tickableInputEl().blur();
14489             }
14490             
14491         }
14492         
14493         Roo.get(document).un('mousedown', this.collapseIf, this);
14494         Roo.get(document).un('mousewheel', this.collapseIf, this);
14495         if (!this.editable) {
14496             Roo.get(document).un('keydown', this.listKeyPress, this);
14497         }
14498         this.fireEvent('collapse', this);
14499         
14500         this.validate();
14501     },
14502
14503     // private
14504     collapseIf : function(e){
14505         var in_combo  = e.within(this.el);
14506         var in_list =  e.within(this.list);
14507         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14508         
14509         if (in_combo || in_list || is_list) {
14510             //e.stopPropagation();
14511             return;
14512         }
14513         
14514         if(this.tickable){
14515             this.onTickableFooterButtonClick(e, false, false);
14516         }
14517
14518         this.collapse();
14519         
14520     },
14521
14522     /**
14523      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14524      */
14525     expand : function(){
14526        
14527         if(this.isExpanded() || !this.hasFocus){
14528             return;
14529         }
14530         
14531         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14532         this.list.setWidth(lw);
14533         
14534         Roo.log('expand');
14535         
14536         this.list.show();
14537         
14538         this.restrictHeight();
14539         
14540         if(this.tickable){
14541             
14542             this.tickItems = Roo.apply([], this.item);
14543             
14544             this.okBtn.show();
14545             this.cancelBtn.show();
14546             this.trigger.hide();
14547             
14548             if(this.editable){
14549                 this.tickableInputEl().focus();
14550             }
14551             
14552         }
14553         
14554         Roo.get(document).on('mousedown', this.collapseIf, this);
14555         Roo.get(document).on('mousewheel', this.collapseIf, this);
14556         if (!this.editable) {
14557             Roo.get(document).on('keydown', this.listKeyPress, this);
14558         }
14559         
14560         this.fireEvent('expand', this);
14561     },
14562
14563     // private
14564     // Implements the default empty TriggerField.onTriggerClick function
14565     onTriggerClick : function(e)
14566     {
14567         Roo.log('trigger click');
14568         
14569         if(this.disabled || !this.triggerList){
14570             return;
14571         }
14572         
14573         this.page = 0;
14574         this.loadNext = false;
14575         
14576         if(this.isExpanded()){
14577             this.collapse();
14578             if (!this.blockFocus) {
14579                 this.inputEl().focus();
14580             }
14581             
14582         }else {
14583             this.hasFocus = true;
14584             if(this.triggerAction == 'all') {
14585                 this.doQuery(this.allQuery, true);
14586             } else {
14587                 this.doQuery(this.getRawValue());
14588             }
14589             if (!this.blockFocus) {
14590                 this.inputEl().focus();
14591             }
14592         }
14593     },
14594     
14595     onTickableTriggerClick : function(e)
14596     {
14597         if(this.disabled){
14598             return;
14599         }
14600         
14601         this.page = 0;
14602         this.loadNext = false;
14603         this.hasFocus = true;
14604         
14605         if(this.triggerAction == 'all') {
14606             this.doQuery(this.allQuery, true);
14607         } else {
14608             this.doQuery(this.getRawValue());
14609         }
14610     },
14611     
14612     onSearchFieldClick : function(e)
14613     {
14614         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14615             this.onTickableFooterButtonClick(e, false, false);
14616             return;
14617         }
14618         
14619         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14620             return;
14621         }
14622         
14623         this.page = 0;
14624         this.loadNext = false;
14625         this.hasFocus = true;
14626         
14627         if(this.triggerAction == 'all') {
14628             this.doQuery(this.allQuery, true);
14629         } else {
14630             this.doQuery(this.getRawValue());
14631         }
14632     },
14633     
14634     listKeyPress : function(e)
14635     {
14636         //Roo.log('listkeypress');
14637         // scroll to first matching element based on key pres..
14638         if (e.isSpecialKey()) {
14639             return false;
14640         }
14641         var k = String.fromCharCode(e.getKey()).toUpperCase();
14642         //Roo.log(k);
14643         var match  = false;
14644         var csel = this.view.getSelectedNodes();
14645         var cselitem = false;
14646         if (csel.length) {
14647             var ix = this.view.indexOf(csel[0]);
14648             cselitem  = this.store.getAt(ix);
14649             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14650                 cselitem = false;
14651             }
14652             
14653         }
14654         
14655         this.store.each(function(v) { 
14656             if (cselitem) {
14657                 // start at existing selection.
14658                 if (cselitem.id == v.id) {
14659                     cselitem = false;
14660                 }
14661                 return true;
14662             }
14663                 
14664             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14665                 match = this.store.indexOf(v);
14666                 return false;
14667             }
14668             return true;
14669         }, this);
14670         
14671         if (match === false) {
14672             return true; // no more action?
14673         }
14674         // scroll to?
14675         this.view.select(match);
14676         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14677         sn.scrollIntoView(sn.dom.parentNode, false);
14678     },
14679     
14680     onViewScroll : function(e, t){
14681         
14682         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){
14683             return;
14684         }
14685         
14686         this.hasQuery = true;
14687         
14688         this.loading = this.list.select('.loading', true).first();
14689         
14690         if(this.loading === null){
14691             this.list.createChild({
14692                 tag: 'div',
14693                 cls: 'loading roo-select2-more-results roo-select2-active',
14694                 html: 'Loading more results...'
14695             });
14696             
14697             this.loading = this.list.select('.loading', true).first();
14698             
14699             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14700             
14701             this.loading.hide();
14702         }
14703         
14704         this.loading.show();
14705         
14706         var _combo = this;
14707         
14708         this.page++;
14709         this.loadNext = true;
14710         
14711         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14712         
14713         return;
14714     },
14715     
14716     addItem : function(o)
14717     {   
14718         var dv = ''; // display value
14719         
14720         if (this.displayField) {
14721             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14722         } else {
14723             // this is an error condition!!!
14724             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14725         }
14726         
14727         if(!dv.length){
14728             return;
14729         }
14730         
14731         var choice = this.choices.createChild({
14732             tag: 'li',
14733             cls: 'roo-select2-search-choice',
14734             cn: [
14735                 {
14736                     tag: 'div',
14737                     html: dv
14738                 },
14739                 {
14740                     tag: 'a',
14741                     href: '#',
14742                     cls: 'roo-select2-search-choice-close fa fa-times',
14743                     tabindex: '-1'
14744                 }
14745             ]
14746             
14747         }, this.searchField);
14748         
14749         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14750         
14751         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14752         
14753         this.item.push(o);
14754         
14755         this.lastData = o;
14756         
14757         this.syncValue();
14758         
14759         this.inputEl().dom.value = '';
14760         
14761         this.validate();
14762     },
14763     
14764     onRemoveItem : function(e, _self, o)
14765     {
14766         e.preventDefault();
14767         
14768         this.lastItem = Roo.apply([], this.item);
14769         
14770         var index = this.item.indexOf(o.data) * 1;
14771         
14772         if( index < 0){
14773             Roo.log('not this item?!');
14774             return;
14775         }
14776         
14777         this.item.splice(index, 1);
14778         o.item.remove();
14779         
14780         this.syncValue();
14781         
14782         this.fireEvent('remove', this, e);
14783         
14784         this.validate();
14785         
14786     },
14787     
14788     syncValue : function()
14789     {
14790         if(!this.item.length){
14791             this.clearValue();
14792             return;
14793         }
14794             
14795         var value = [];
14796         var _this = this;
14797         Roo.each(this.item, function(i){
14798             if(_this.valueField){
14799                 value.push(i[_this.valueField]);
14800                 return;
14801             }
14802
14803             value.push(i);
14804         });
14805
14806         this.value = value.join(',');
14807
14808         if(this.hiddenField){
14809             this.hiddenField.dom.value = this.value;
14810         }
14811         
14812         this.store.fireEvent("datachanged", this.store);
14813         
14814         this.validate();
14815     },
14816     
14817     clearItem : function()
14818     {
14819         if(!this.multiple){
14820             return;
14821         }
14822         
14823         this.item = [];
14824         
14825         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14826            c.remove();
14827         });
14828         
14829         this.syncValue();
14830         
14831         this.validate();
14832         
14833         if(this.tickable && !Roo.isTouch){
14834             this.view.refresh();
14835         }
14836     },
14837     
14838     inputEl: function ()
14839     {
14840         if(Roo.isIOS && this.useNativeIOS){
14841             return this.el.select('select.roo-ios-select', true).first();
14842         }
14843         
14844         if(Roo.isTouch && this.mobileTouchView){
14845             return this.el.select('input.form-control',true).first();
14846         }
14847         
14848         if(this.tickable){
14849             return this.searchField;
14850         }
14851         
14852         return this.el.select('input.form-control',true).first();
14853     },
14854     
14855     onTickableFooterButtonClick : function(e, btn, el)
14856     {
14857         e.preventDefault();
14858         
14859         this.lastItem = Roo.apply([], this.item);
14860         
14861         if(btn && btn.name == 'cancel'){
14862             this.tickItems = Roo.apply([], this.item);
14863             this.collapse();
14864             return;
14865         }
14866         
14867         this.clearItem();
14868         
14869         var _this = this;
14870         
14871         Roo.each(this.tickItems, function(o){
14872             _this.addItem(o);
14873         });
14874         
14875         this.collapse();
14876         
14877     },
14878     
14879     validate : function()
14880     {
14881         if(this.getVisibilityEl().hasClass('hidden')){
14882             return true;
14883         }
14884         
14885         var v = this.getRawValue();
14886         
14887         if(this.multiple){
14888             v = this.getValue();
14889         }
14890         
14891         if(this.disabled || this.allowBlank || v.length){
14892             this.markValid();
14893             return true;
14894         }
14895         
14896         this.markInvalid();
14897         return false;
14898     },
14899     
14900     tickableInputEl : function()
14901     {
14902         if(!this.tickable || !this.editable){
14903             return this.inputEl();
14904         }
14905         
14906         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14907     },
14908     
14909     
14910     getAutoCreateTouchView : function()
14911     {
14912         var id = Roo.id();
14913         
14914         var cfg = {
14915             cls: 'form-group' //input-group
14916         };
14917         
14918         var input =  {
14919             tag: 'input',
14920             id : id,
14921             type : this.inputType,
14922             cls : 'form-control x-combo-noedit',
14923             autocomplete: 'new-password',
14924             placeholder : this.placeholder || '',
14925             readonly : true
14926         };
14927         
14928         if (this.name) {
14929             input.name = this.name;
14930         }
14931         
14932         if (this.size) {
14933             input.cls += ' input-' + this.size;
14934         }
14935         
14936         if (this.disabled) {
14937             input.disabled = true;
14938         }
14939         
14940         var inputblock = {
14941             cls : '',
14942             cn : [
14943                 input
14944             ]
14945         };
14946         
14947         if(this.before){
14948             inputblock.cls += ' input-group';
14949             
14950             inputblock.cn.unshift({
14951                 tag :'span',
14952                 cls : 'input-group-addon',
14953                 html : this.before
14954             });
14955         }
14956         
14957         if(this.removable && !this.multiple){
14958             inputblock.cls += ' roo-removable';
14959             
14960             inputblock.cn.push({
14961                 tag: 'button',
14962                 html : 'x',
14963                 cls : 'roo-combo-removable-btn close'
14964             });
14965         }
14966
14967         if(this.hasFeedback && !this.allowBlank){
14968             
14969             inputblock.cls += ' has-feedback';
14970             
14971             inputblock.cn.push({
14972                 tag: 'span',
14973                 cls: 'glyphicon form-control-feedback'
14974             });
14975             
14976         }
14977         
14978         if (this.after) {
14979             
14980             inputblock.cls += (this.before) ? '' : ' input-group';
14981             
14982             inputblock.cn.push({
14983                 tag :'span',
14984                 cls : 'input-group-addon',
14985                 html : this.after
14986             });
14987         }
14988
14989         var box = {
14990             tag: 'div',
14991             cn: [
14992                 {
14993                     tag: 'input',
14994                     type : 'hidden',
14995                     cls: 'form-hidden-field'
14996                 },
14997                 inputblock
14998             ]
14999             
15000         };
15001         
15002         if(this.multiple){
15003             box = {
15004                 tag: 'div',
15005                 cn: [
15006                     {
15007                         tag: 'input',
15008                         type : 'hidden',
15009                         cls: 'form-hidden-field'
15010                     },
15011                     {
15012                         tag: 'ul',
15013                         cls: 'roo-select2-choices',
15014                         cn:[
15015                             {
15016                                 tag: 'li',
15017                                 cls: 'roo-select2-search-field',
15018                                 cn: [
15019
15020                                     inputblock
15021                                 ]
15022                             }
15023                         ]
15024                     }
15025                 ]
15026             }
15027         };
15028         
15029         var combobox = {
15030             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15031             cn: [
15032                 box
15033             ]
15034         };
15035         
15036         if(!this.multiple && this.showToggleBtn){
15037             
15038             var caret = {
15039                         tag: 'span',
15040                         cls: 'caret'
15041             };
15042             
15043             if (this.caret != false) {
15044                 caret = {
15045                      tag: 'i',
15046                      cls: 'fa fa-' + this.caret
15047                 };
15048                 
15049             }
15050             
15051             combobox.cn.push({
15052                 tag :'span',
15053                 cls : 'input-group-addon btn dropdown-toggle',
15054                 cn : [
15055                     caret,
15056                     {
15057                         tag: 'span',
15058                         cls: 'combobox-clear',
15059                         cn  : [
15060                             {
15061                                 tag : 'i',
15062                                 cls: 'icon-remove'
15063                             }
15064                         ]
15065                     }
15066                 ]
15067
15068             })
15069         }
15070         
15071         if(this.multiple){
15072             combobox.cls += ' roo-select2-container-multi';
15073         }
15074         
15075         var align = this.labelAlign || this.parentLabelAlign();
15076         
15077         if (align ==='left' && this.fieldLabel.length) {
15078
15079             cfg.cn = [
15080                 {
15081                    tag : 'i',
15082                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15083                    tooltip : 'This field is required'
15084                 },
15085                 {
15086                     tag: 'label',
15087                     cls : 'control-label',
15088                     html : this.fieldLabel
15089
15090                 },
15091                 {
15092                     cls : '', 
15093                     cn: [
15094                         combobox
15095                     ]
15096                 }
15097             ];
15098             
15099             var labelCfg = cfg.cn[1];
15100             var contentCfg = cfg.cn[2];
15101             
15102
15103             if(this.indicatorpos == 'right'){
15104                 cfg.cn = [
15105                     {
15106                         tag: 'label',
15107                         'for' :  id,
15108                         cls : 'control-label',
15109                         cn : [
15110                             {
15111                                 tag : 'span',
15112                                 html : this.fieldLabel
15113                             },
15114                             {
15115                                 tag : 'i',
15116                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15117                                 tooltip : 'This field is required'
15118                             }
15119                         ]
15120                     },
15121                     {
15122                         cls : "",
15123                         cn: [
15124                             combobox
15125                         ]
15126                     }
15127
15128                 ];
15129                 
15130                 labelCfg = cfg.cn[0];
15131                 contentCfg = cfg.cn[1];
15132             }
15133             
15134            
15135             
15136             if(this.labelWidth > 12){
15137                 labelCfg.style = "width: " + this.labelWidth + 'px';
15138             }
15139             
15140             if(this.labelWidth < 13 && this.labelmd == 0){
15141                 this.labelmd = this.labelWidth;
15142             }
15143             
15144             if(this.labellg > 0){
15145                 labelCfg.cls += ' col-lg-' + this.labellg;
15146                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15147             }
15148             
15149             if(this.labelmd > 0){
15150                 labelCfg.cls += ' col-md-' + this.labelmd;
15151                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15152             }
15153             
15154             if(this.labelsm > 0){
15155                 labelCfg.cls += ' col-sm-' + this.labelsm;
15156                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15157             }
15158             
15159             if(this.labelxs > 0){
15160                 labelCfg.cls += ' col-xs-' + this.labelxs;
15161                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15162             }
15163                 
15164                 
15165         } else if ( this.fieldLabel.length) {
15166             cfg.cn = [
15167                 {
15168                    tag : 'i',
15169                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15170                    tooltip : 'This field is required'
15171                 },
15172                 {
15173                     tag: 'label',
15174                     cls : 'control-label',
15175                     html : this.fieldLabel
15176
15177                 },
15178                 {
15179                     cls : '', 
15180                     cn: [
15181                         combobox
15182                     ]
15183                 }
15184             ];
15185             
15186             if(this.indicatorpos == 'right'){
15187                 cfg.cn = [
15188                     {
15189                         tag: 'label',
15190                         cls : 'control-label',
15191                         html : this.fieldLabel,
15192                         cn : [
15193                             {
15194                                tag : 'i',
15195                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15196                                tooltip : 'This field is required'
15197                             }
15198                         ]
15199                     },
15200                     {
15201                         cls : '', 
15202                         cn: [
15203                             combobox
15204                         ]
15205                     }
15206                 ];
15207             }
15208         } else {
15209             cfg.cn = combobox;    
15210         }
15211         
15212         
15213         var settings = this;
15214         
15215         ['xs','sm','md','lg'].map(function(size){
15216             if (settings[size]) {
15217                 cfg.cls += ' col-' + size + '-' + settings[size];
15218             }
15219         });
15220         
15221         return cfg;
15222     },
15223     
15224     initTouchView : function()
15225     {
15226         this.renderTouchView();
15227         
15228         this.touchViewEl.on('scroll', function(){
15229             this.el.dom.scrollTop = 0;
15230         }, this);
15231         
15232         this.originalValue = this.getValue();
15233         
15234         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15235         
15236         this.inputEl().on("click", this.showTouchView, this);
15237         if (this.triggerEl) {
15238             this.triggerEl.on("click", this.showTouchView, this);
15239         }
15240         
15241         
15242         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15243         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15244         
15245         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15246         
15247         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15248         this.store.on('load', this.onTouchViewLoad, this);
15249         this.store.on('loadexception', this.onTouchViewLoadException, this);
15250         
15251         if(this.hiddenName){
15252             
15253             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15254             
15255             this.hiddenField.dom.value =
15256                 this.hiddenValue !== undefined ? this.hiddenValue :
15257                 this.value !== undefined ? this.value : '';
15258         
15259             this.el.dom.removeAttribute('name');
15260             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15261         }
15262         
15263         if(this.multiple){
15264             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15265             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15266         }
15267         
15268         if(this.removable && !this.multiple){
15269             var close = this.closeTriggerEl();
15270             if(close){
15271                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15272                 close.on('click', this.removeBtnClick, this, close);
15273             }
15274         }
15275         /*
15276          * fix the bug in Safari iOS8
15277          */
15278         this.inputEl().on("focus", function(e){
15279             document.activeElement.blur();
15280         }, this);
15281         
15282         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15283         
15284         return;
15285         
15286         
15287     },
15288     
15289     renderTouchView : function()
15290     {
15291         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15292         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15293         
15294         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15295         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15296         
15297         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15298         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15299         this.touchViewBodyEl.setStyle('overflow', 'auto');
15300         
15301         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15302         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15303         
15304         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15305         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15306         
15307     },
15308     
15309     showTouchView : function()
15310     {
15311         if(this.disabled){
15312             return;
15313         }
15314         
15315         this.touchViewHeaderEl.hide();
15316
15317         if(this.modalTitle.length){
15318             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15319             this.touchViewHeaderEl.show();
15320         }
15321
15322         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15323         this.touchViewEl.show();
15324
15325         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15326         
15327         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15328         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15329
15330         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15331
15332         if(this.modalTitle.length){
15333             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15334         }
15335         
15336         this.touchViewBodyEl.setHeight(bodyHeight);
15337
15338         if(this.animate){
15339             var _this = this;
15340             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15341         }else{
15342             this.touchViewEl.addClass('in');
15343         }
15344         
15345         if(this._touchViewMask){
15346             Roo.get(document.body).addClass("x-body-masked");
15347             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15348             this._touchViewMask.setStyle('z-index', 10000);
15349             this._touchViewMask.addClass('show');
15350         }
15351         
15352         this.doTouchViewQuery();
15353         
15354     },
15355     
15356     hideTouchView : function()
15357     {
15358         this.touchViewEl.removeClass('in');
15359
15360         if(this.animate){
15361             var _this = this;
15362             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15363         }else{
15364             this.touchViewEl.setStyle('display', 'none');
15365         }
15366         
15367         if(this._touchViewMask){
15368             this._touchViewMask.removeClass('show');
15369             Roo.get(document.body).removeClass("x-body-masked");
15370         }
15371     },
15372     
15373     setTouchViewValue : function()
15374     {
15375         if(this.multiple){
15376             this.clearItem();
15377         
15378             var _this = this;
15379
15380             Roo.each(this.tickItems, function(o){
15381                 this.addItem(o);
15382             }, this);
15383         }
15384         
15385         this.hideTouchView();
15386     },
15387     
15388     doTouchViewQuery : function()
15389     {
15390         var qe = {
15391             query: '',
15392             forceAll: true,
15393             combo: this,
15394             cancel:false
15395         };
15396         
15397         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15398             return false;
15399         }
15400         
15401         if(!this.alwaysQuery || this.mode == 'local'){
15402             this.onTouchViewLoad();
15403             return;
15404         }
15405         
15406         this.store.load();
15407     },
15408     
15409     onTouchViewBeforeLoad : function(combo,opts)
15410     {
15411         return;
15412     },
15413
15414     // private
15415     onTouchViewLoad : function()
15416     {
15417         if(this.store.getCount() < 1){
15418             this.onTouchViewEmptyResults();
15419             return;
15420         }
15421         
15422         this.clearTouchView();
15423         
15424         var rawValue = this.getRawValue();
15425         
15426         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15427         
15428         this.tickItems = [];
15429         
15430         this.store.data.each(function(d, rowIndex){
15431             var row = this.touchViewListGroup.createChild(template);
15432             
15433             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15434                 row.addClass(d.data.cls);
15435             }
15436             
15437             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15438                 var cfg = {
15439                     data : d.data,
15440                     html : d.data[this.displayField]
15441                 };
15442                 
15443                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15444                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15445                 }
15446             }
15447             row.removeClass('selected');
15448             if(!this.multiple && this.valueField &&
15449                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15450             {
15451                 // radio buttons..
15452                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15453                 row.addClass('selected');
15454             }
15455             
15456             if(this.multiple && this.valueField &&
15457                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15458             {
15459                 
15460                 // checkboxes...
15461                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15462                 this.tickItems.push(d.data);
15463             }
15464             
15465             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15466             
15467         }, this);
15468         
15469         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15470         
15471         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15472
15473         if(this.modalTitle.length){
15474             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15475         }
15476
15477         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15478         
15479         if(this.mobile_restrict_height && listHeight < bodyHeight){
15480             this.touchViewBodyEl.setHeight(listHeight);
15481         }
15482         
15483         var _this = this;
15484         
15485         if(firstChecked && listHeight > bodyHeight){
15486             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15487         }
15488         
15489     },
15490     
15491     onTouchViewLoadException : function()
15492     {
15493         this.hideTouchView();
15494     },
15495     
15496     onTouchViewEmptyResults : function()
15497     {
15498         this.clearTouchView();
15499         
15500         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15501         
15502         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15503         
15504     },
15505     
15506     clearTouchView : function()
15507     {
15508         this.touchViewListGroup.dom.innerHTML = '';
15509     },
15510     
15511     onTouchViewClick : function(e, el, o)
15512     {
15513         e.preventDefault();
15514         
15515         var row = o.row;
15516         var rowIndex = o.rowIndex;
15517         
15518         var r = this.store.getAt(rowIndex);
15519         
15520         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15521             
15522             if(!this.multiple){
15523                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15524                     c.dom.removeAttribute('checked');
15525                 }, this);
15526
15527                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15528
15529                 this.setFromData(r.data);
15530
15531                 var close = this.closeTriggerEl();
15532
15533                 if(close){
15534                     close.show();
15535                 }
15536
15537                 this.hideTouchView();
15538
15539                 this.fireEvent('select', this, r, rowIndex);
15540
15541                 return;
15542             }
15543
15544             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15545                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15546                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15547                 return;
15548             }
15549
15550             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15551             this.addItem(r.data);
15552             this.tickItems.push(r.data);
15553         }
15554     },
15555     
15556     getAutoCreateNativeIOS : function()
15557     {
15558         var cfg = {
15559             cls: 'form-group' //input-group,
15560         };
15561         
15562         var combobox =  {
15563             tag: 'select',
15564             cls : 'roo-ios-select'
15565         };
15566         
15567         if (this.name) {
15568             combobox.name = this.name;
15569         }
15570         
15571         if (this.disabled) {
15572             combobox.disabled = true;
15573         }
15574         
15575         var settings = this;
15576         
15577         ['xs','sm','md','lg'].map(function(size){
15578             if (settings[size]) {
15579                 cfg.cls += ' col-' + size + '-' + settings[size];
15580             }
15581         });
15582         
15583         cfg.cn = combobox;
15584         
15585         return cfg;
15586         
15587     },
15588     
15589     initIOSView : function()
15590     {
15591         this.store.on('load', this.onIOSViewLoad, this);
15592         
15593         return;
15594     },
15595     
15596     onIOSViewLoad : function()
15597     {
15598         if(this.store.getCount() < 1){
15599             return;
15600         }
15601         
15602         this.clearIOSView();
15603         
15604         if(this.allowBlank) {
15605             
15606             var default_text = '-- SELECT --';
15607             
15608             if(this.placeholder.length){
15609                 default_text = this.placeholder;
15610             }
15611             
15612             if(this.emptyTitle.length){
15613                 default_text += ' - ' + this.emptyTitle + ' -';
15614             }
15615             
15616             var opt = this.inputEl().createChild({
15617                 tag: 'option',
15618                 value : 0,
15619                 html : default_text
15620             });
15621             
15622             var o = {};
15623             o[this.valueField] = 0;
15624             o[this.displayField] = default_text;
15625             
15626             this.ios_options.push({
15627                 data : o,
15628                 el : opt
15629             });
15630             
15631         }
15632         
15633         this.store.data.each(function(d, rowIndex){
15634             
15635             var html = '';
15636             
15637             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15638                 html = d.data[this.displayField];
15639             }
15640             
15641             var value = '';
15642             
15643             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15644                 value = d.data[this.valueField];
15645             }
15646             
15647             var option = {
15648                 tag: 'option',
15649                 value : value,
15650                 html : html
15651             };
15652             
15653             if(this.value == d.data[this.valueField]){
15654                 option['selected'] = true;
15655             }
15656             
15657             var opt = this.inputEl().createChild(option);
15658             
15659             this.ios_options.push({
15660                 data : d.data,
15661                 el : opt
15662             });
15663             
15664         }, this);
15665         
15666         this.inputEl().on('change', function(){
15667            this.fireEvent('select', this);
15668         }, this);
15669         
15670     },
15671     
15672     clearIOSView: function()
15673     {
15674         this.inputEl().dom.innerHTML = '';
15675         
15676         this.ios_options = [];
15677     },
15678     
15679     setIOSValue: function(v)
15680     {
15681         this.value = v;
15682         
15683         if(!this.ios_options){
15684             return;
15685         }
15686         
15687         Roo.each(this.ios_options, function(opts){
15688            
15689            opts.el.dom.removeAttribute('selected');
15690            
15691            if(opts.data[this.valueField] != v){
15692                return;
15693            }
15694            
15695            opts.el.dom.setAttribute('selected', true);
15696            
15697         }, this);
15698     }
15699
15700     /** 
15701     * @cfg {Boolean} grow 
15702     * @hide 
15703     */
15704     /** 
15705     * @cfg {Number} growMin 
15706     * @hide 
15707     */
15708     /** 
15709     * @cfg {Number} growMax 
15710     * @hide 
15711     */
15712     /**
15713      * @hide
15714      * @method autoSize
15715      */
15716 });
15717
15718 Roo.apply(Roo.bootstrap.ComboBox,  {
15719     
15720     header : {
15721         tag: 'div',
15722         cls: 'modal-header',
15723         cn: [
15724             {
15725                 tag: 'h4',
15726                 cls: 'modal-title'
15727             }
15728         ]
15729     },
15730     
15731     body : {
15732         tag: 'div',
15733         cls: 'modal-body',
15734         cn: [
15735             {
15736                 tag: 'ul',
15737                 cls: 'list-group'
15738             }
15739         ]
15740     },
15741     
15742     listItemRadio : {
15743         tag: 'li',
15744         cls: 'list-group-item',
15745         cn: [
15746             {
15747                 tag: 'span',
15748                 cls: 'roo-combobox-list-group-item-value'
15749             },
15750             {
15751                 tag: 'div',
15752                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15753                 cn: [
15754                     {
15755                         tag: 'input',
15756                         type: 'radio'
15757                     },
15758                     {
15759                         tag: 'label'
15760                     }
15761                 ]
15762             }
15763         ]
15764     },
15765     
15766     listItemCheckbox : {
15767         tag: 'li',
15768         cls: 'list-group-item',
15769         cn: [
15770             {
15771                 tag: 'span',
15772                 cls: 'roo-combobox-list-group-item-value'
15773             },
15774             {
15775                 tag: 'div',
15776                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15777                 cn: [
15778                     {
15779                         tag: 'input',
15780                         type: 'checkbox'
15781                     },
15782                     {
15783                         tag: 'label'
15784                     }
15785                 ]
15786             }
15787         ]
15788     },
15789     
15790     emptyResult : {
15791         tag: 'div',
15792         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15793     },
15794     
15795     footer : {
15796         tag: 'div',
15797         cls: 'modal-footer',
15798         cn: [
15799             {
15800                 tag: 'div',
15801                 cls: 'row',
15802                 cn: [
15803                     {
15804                         tag: 'div',
15805                         cls: 'col-xs-6 text-left',
15806                         cn: {
15807                             tag: 'button',
15808                             cls: 'btn btn-danger roo-touch-view-cancel',
15809                             html: 'Cancel'
15810                         }
15811                     },
15812                     {
15813                         tag: 'div',
15814                         cls: 'col-xs-6 text-right',
15815                         cn: {
15816                             tag: 'button',
15817                             cls: 'btn btn-success roo-touch-view-ok',
15818                             html: 'OK'
15819                         }
15820                     }
15821                 ]
15822             }
15823         ]
15824         
15825     }
15826 });
15827
15828 Roo.apply(Roo.bootstrap.ComboBox,  {
15829     
15830     touchViewTemplate : {
15831         tag: 'div',
15832         cls: 'modal fade roo-combobox-touch-view',
15833         cn: [
15834             {
15835                 tag: 'div',
15836                 cls: 'modal-dialog',
15837                 style : 'position:fixed', // we have to fix position....
15838                 cn: [
15839                     {
15840                         tag: 'div',
15841                         cls: 'modal-content',
15842                         cn: [
15843                             Roo.bootstrap.ComboBox.header,
15844                             Roo.bootstrap.ComboBox.body,
15845                             Roo.bootstrap.ComboBox.footer
15846                         ]
15847                     }
15848                 ]
15849             }
15850         ]
15851     }
15852 });/*
15853  * Based on:
15854  * Ext JS Library 1.1.1
15855  * Copyright(c) 2006-2007, Ext JS, LLC.
15856  *
15857  * Originally Released Under LGPL - original licence link has changed is not relivant.
15858  *
15859  * Fork - LGPL
15860  * <script type="text/javascript">
15861  */
15862
15863 /**
15864  * @class Roo.View
15865  * @extends Roo.util.Observable
15866  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15867  * This class also supports single and multi selection modes. <br>
15868  * Create a data model bound view:
15869  <pre><code>
15870  var store = new Roo.data.Store(...);
15871
15872  var view = new Roo.View({
15873     el : "my-element",
15874     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15875  
15876     singleSelect: true,
15877     selectedClass: "ydataview-selected",
15878     store: store
15879  });
15880
15881  // listen for node click?
15882  view.on("click", function(vw, index, node, e){
15883  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15884  });
15885
15886  // load XML data
15887  dataModel.load("foobar.xml");
15888  </code></pre>
15889  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15890  * <br><br>
15891  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15892  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15893  * 
15894  * Note: old style constructor is still suported (container, template, config)
15895  * 
15896  * @constructor
15897  * Create a new View
15898  * @param {Object} config The config object
15899  * 
15900  */
15901 Roo.View = function(config, depreciated_tpl, depreciated_config){
15902     
15903     this.parent = false;
15904     
15905     if (typeof(depreciated_tpl) == 'undefined') {
15906         // new way.. - universal constructor.
15907         Roo.apply(this, config);
15908         this.el  = Roo.get(this.el);
15909     } else {
15910         // old format..
15911         this.el  = Roo.get(config);
15912         this.tpl = depreciated_tpl;
15913         Roo.apply(this, depreciated_config);
15914     }
15915     this.wrapEl  = this.el.wrap().wrap();
15916     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15917     
15918     
15919     if(typeof(this.tpl) == "string"){
15920         this.tpl = new Roo.Template(this.tpl);
15921     } else {
15922         // support xtype ctors..
15923         this.tpl = new Roo.factory(this.tpl, Roo);
15924     }
15925     
15926     
15927     this.tpl.compile();
15928     
15929     /** @private */
15930     this.addEvents({
15931         /**
15932          * @event beforeclick
15933          * Fires before a click is processed. Returns false to cancel the default action.
15934          * @param {Roo.View} this
15935          * @param {Number} index The index of the target node
15936          * @param {HTMLElement} node The target node
15937          * @param {Roo.EventObject} e The raw event object
15938          */
15939             "beforeclick" : true,
15940         /**
15941          * @event click
15942          * Fires when a template node is clicked.
15943          * @param {Roo.View} this
15944          * @param {Number} index The index of the target node
15945          * @param {HTMLElement} node The target node
15946          * @param {Roo.EventObject} e The raw event object
15947          */
15948             "click" : true,
15949         /**
15950          * @event dblclick
15951          * Fires when a template node is double clicked.
15952          * @param {Roo.View} this
15953          * @param {Number} index The index of the target node
15954          * @param {HTMLElement} node The target node
15955          * @param {Roo.EventObject} e The raw event object
15956          */
15957             "dblclick" : true,
15958         /**
15959          * @event contextmenu
15960          * Fires when a template node is right clicked.
15961          * @param {Roo.View} this
15962          * @param {Number} index The index of the target node
15963          * @param {HTMLElement} node The target node
15964          * @param {Roo.EventObject} e The raw event object
15965          */
15966             "contextmenu" : true,
15967         /**
15968          * @event selectionchange
15969          * Fires when the selected nodes change.
15970          * @param {Roo.View} this
15971          * @param {Array} selections Array of the selected nodes
15972          */
15973             "selectionchange" : true,
15974     
15975         /**
15976          * @event beforeselect
15977          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15978          * @param {Roo.View} this
15979          * @param {HTMLElement} node The node to be selected
15980          * @param {Array} selections Array of currently selected nodes
15981          */
15982             "beforeselect" : true,
15983         /**
15984          * @event preparedata
15985          * Fires on every row to render, to allow you to change the data.
15986          * @param {Roo.View} this
15987          * @param {Object} data to be rendered (change this)
15988          */
15989           "preparedata" : true
15990           
15991           
15992         });
15993
15994
15995
15996     this.el.on({
15997         "click": this.onClick,
15998         "dblclick": this.onDblClick,
15999         "contextmenu": this.onContextMenu,
16000         scope:this
16001     });
16002
16003     this.selections = [];
16004     this.nodes = [];
16005     this.cmp = new Roo.CompositeElementLite([]);
16006     if(this.store){
16007         this.store = Roo.factory(this.store, Roo.data);
16008         this.setStore(this.store, true);
16009     }
16010     
16011     if ( this.footer && this.footer.xtype) {
16012            
16013          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16014         
16015         this.footer.dataSource = this.store;
16016         this.footer.container = fctr;
16017         this.footer = Roo.factory(this.footer, Roo);
16018         fctr.insertFirst(this.el);
16019         
16020         // this is a bit insane - as the paging toolbar seems to detach the el..
16021 //        dom.parentNode.parentNode.parentNode
16022          // they get detached?
16023     }
16024     
16025     
16026     Roo.View.superclass.constructor.call(this);
16027     
16028     
16029 };
16030
16031 Roo.extend(Roo.View, Roo.util.Observable, {
16032     
16033      /**
16034      * @cfg {Roo.data.Store} store Data store to load data from.
16035      */
16036     store : false,
16037     
16038     /**
16039      * @cfg {String|Roo.Element} el The container element.
16040      */
16041     el : '',
16042     
16043     /**
16044      * @cfg {String|Roo.Template} tpl The template used by this View 
16045      */
16046     tpl : false,
16047     /**
16048      * @cfg {String} dataName the named area of the template to use as the data area
16049      *                          Works with domtemplates roo-name="name"
16050      */
16051     dataName: false,
16052     /**
16053      * @cfg {String} selectedClass The css class to add to selected nodes
16054      */
16055     selectedClass : "x-view-selected",
16056      /**
16057      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16058      */
16059     emptyText : "",
16060     
16061     /**
16062      * @cfg {String} text to display on mask (default Loading)
16063      */
16064     mask : false,
16065     /**
16066      * @cfg {Boolean} multiSelect Allow multiple selection
16067      */
16068     multiSelect : false,
16069     /**
16070      * @cfg {Boolean} singleSelect Allow single selection
16071      */
16072     singleSelect:  false,
16073     
16074     /**
16075      * @cfg {Boolean} toggleSelect - selecting 
16076      */
16077     toggleSelect : false,
16078     
16079     /**
16080      * @cfg {Boolean} tickable - selecting 
16081      */
16082     tickable : false,
16083     
16084     /**
16085      * Returns the element this view is bound to.
16086      * @return {Roo.Element}
16087      */
16088     getEl : function(){
16089         return this.wrapEl;
16090     },
16091     
16092     
16093
16094     /**
16095      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16096      */
16097     refresh : function(){
16098         //Roo.log('refresh');
16099         var t = this.tpl;
16100         
16101         // if we are using something like 'domtemplate', then
16102         // the what gets used is:
16103         // t.applySubtemplate(NAME, data, wrapping data..)
16104         // the outer template then get' applied with
16105         //     the store 'extra data'
16106         // and the body get's added to the
16107         //      roo-name="data" node?
16108         //      <span class='roo-tpl-{name}'></span> ?????
16109         
16110         
16111         
16112         this.clearSelections();
16113         this.el.update("");
16114         var html = [];
16115         var records = this.store.getRange();
16116         if(records.length < 1) {
16117             
16118             // is this valid??  = should it render a template??
16119             
16120             this.el.update(this.emptyText);
16121             return;
16122         }
16123         var el = this.el;
16124         if (this.dataName) {
16125             this.el.update(t.apply(this.store.meta)); //????
16126             el = this.el.child('.roo-tpl-' + this.dataName);
16127         }
16128         
16129         for(var i = 0, len = records.length; i < len; i++){
16130             var data = this.prepareData(records[i].data, i, records[i]);
16131             this.fireEvent("preparedata", this, data, i, records[i]);
16132             
16133             var d = Roo.apply({}, data);
16134             
16135             if(this.tickable){
16136                 Roo.apply(d, {'roo-id' : Roo.id()});
16137                 
16138                 var _this = this;
16139             
16140                 Roo.each(this.parent.item, function(item){
16141                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16142                         return;
16143                     }
16144                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16145                 });
16146             }
16147             
16148             html[html.length] = Roo.util.Format.trim(
16149                 this.dataName ?
16150                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16151                     t.apply(d)
16152             );
16153         }
16154         
16155         
16156         
16157         el.update(html.join(""));
16158         this.nodes = el.dom.childNodes;
16159         this.updateIndexes(0);
16160     },
16161     
16162
16163     /**
16164      * Function to override to reformat the data that is sent to
16165      * the template for each node.
16166      * DEPRICATED - use the preparedata event handler.
16167      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16168      * a JSON object for an UpdateManager bound view).
16169      */
16170     prepareData : function(data, index, record)
16171     {
16172         this.fireEvent("preparedata", this, data, index, record);
16173         return data;
16174     },
16175
16176     onUpdate : function(ds, record){
16177         // Roo.log('on update');   
16178         this.clearSelections();
16179         var index = this.store.indexOf(record);
16180         var n = this.nodes[index];
16181         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16182         n.parentNode.removeChild(n);
16183         this.updateIndexes(index, index);
16184     },
16185
16186     
16187     
16188 // --------- FIXME     
16189     onAdd : function(ds, records, index)
16190     {
16191         //Roo.log(['on Add', ds, records, index] );        
16192         this.clearSelections();
16193         if(this.nodes.length == 0){
16194             this.refresh();
16195             return;
16196         }
16197         var n = this.nodes[index];
16198         for(var i = 0, len = records.length; i < len; i++){
16199             var d = this.prepareData(records[i].data, i, records[i]);
16200             if(n){
16201                 this.tpl.insertBefore(n, d);
16202             }else{
16203                 
16204                 this.tpl.append(this.el, d);
16205             }
16206         }
16207         this.updateIndexes(index);
16208     },
16209
16210     onRemove : function(ds, record, index){
16211        // Roo.log('onRemove');
16212         this.clearSelections();
16213         var el = this.dataName  ?
16214             this.el.child('.roo-tpl-' + this.dataName) :
16215             this.el; 
16216         
16217         el.dom.removeChild(this.nodes[index]);
16218         this.updateIndexes(index);
16219     },
16220
16221     /**
16222      * Refresh an individual node.
16223      * @param {Number} index
16224      */
16225     refreshNode : function(index){
16226         this.onUpdate(this.store, this.store.getAt(index));
16227     },
16228
16229     updateIndexes : function(startIndex, endIndex){
16230         var ns = this.nodes;
16231         startIndex = startIndex || 0;
16232         endIndex = endIndex || ns.length - 1;
16233         for(var i = startIndex; i <= endIndex; i++){
16234             ns[i].nodeIndex = i;
16235         }
16236     },
16237
16238     /**
16239      * Changes the data store this view uses and refresh the view.
16240      * @param {Store} store
16241      */
16242     setStore : function(store, initial){
16243         if(!initial && this.store){
16244             this.store.un("datachanged", this.refresh);
16245             this.store.un("add", this.onAdd);
16246             this.store.un("remove", this.onRemove);
16247             this.store.un("update", this.onUpdate);
16248             this.store.un("clear", this.refresh);
16249             this.store.un("beforeload", this.onBeforeLoad);
16250             this.store.un("load", this.onLoad);
16251             this.store.un("loadexception", this.onLoad);
16252         }
16253         if(store){
16254           
16255             store.on("datachanged", this.refresh, this);
16256             store.on("add", this.onAdd, this);
16257             store.on("remove", this.onRemove, this);
16258             store.on("update", this.onUpdate, this);
16259             store.on("clear", this.refresh, this);
16260             store.on("beforeload", this.onBeforeLoad, this);
16261             store.on("load", this.onLoad, this);
16262             store.on("loadexception", this.onLoad, this);
16263         }
16264         
16265         if(store){
16266             this.refresh();
16267         }
16268     },
16269     /**
16270      * onbeforeLoad - masks the loading area.
16271      *
16272      */
16273     onBeforeLoad : function(store,opts)
16274     {
16275          //Roo.log('onBeforeLoad');   
16276         if (!opts.add) {
16277             this.el.update("");
16278         }
16279         this.el.mask(this.mask ? this.mask : "Loading" ); 
16280     },
16281     onLoad : function ()
16282     {
16283         this.el.unmask();
16284     },
16285     
16286
16287     /**
16288      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16289      * @param {HTMLElement} node
16290      * @return {HTMLElement} The template node
16291      */
16292     findItemFromChild : function(node){
16293         var el = this.dataName  ?
16294             this.el.child('.roo-tpl-' + this.dataName,true) :
16295             this.el.dom; 
16296         
16297         if(!node || node.parentNode == el){
16298                     return node;
16299             }
16300             var p = node.parentNode;
16301             while(p && p != el){
16302             if(p.parentNode == el){
16303                 return p;
16304             }
16305             p = p.parentNode;
16306         }
16307             return null;
16308     },
16309
16310     /** @ignore */
16311     onClick : function(e){
16312         var item = this.findItemFromChild(e.getTarget());
16313         if(item){
16314             var index = this.indexOf(item);
16315             if(this.onItemClick(item, index, e) !== false){
16316                 this.fireEvent("click", this, index, item, e);
16317             }
16318         }else{
16319             this.clearSelections();
16320         }
16321     },
16322
16323     /** @ignore */
16324     onContextMenu : function(e){
16325         var item = this.findItemFromChild(e.getTarget());
16326         if(item){
16327             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16328         }
16329     },
16330
16331     /** @ignore */
16332     onDblClick : function(e){
16333         var item = this.findItemFromChild(e.getTarget());
16334         if(item){
16335             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16336         }
16337     },
16338
16339     onItemClick : function(item, index, e)
16340     {
16341         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16342             return false;
16343         }
16344         if (this.toggleSelect) {
16345             var m = this.isSelected(item) ? 'unselect' : 'select';
16346             //Roo.log(m);
16347             var _t = this;
16348             _t[m](item, true, false);
16349             return true;
16350         }
16351         if(this.multiSelect || this.singleSelect){
16352             if(this.multiSelect && e.shiftKey && this.lastSelection){
16353                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16354             }else{
16355                 this.select(item, this.multiSelect && e.ctrlKey);
16356                 this.lastSelection = item;
16357             }
16358             
16359             if(!this.tickable){
16360                 e.preventDefault();
16361             }
16362             
16363         }
16364         return true;
16365     },
16366
16367     /**
16368      * Get the number of selected nodes.
16369      * @return {Number}
16370      */
16371     getSelectionCount : function(){
16372         return this.selections.length;
16373     },
16374
16375     /**
16376      * Get the currently selected nodes.
16377      * @return {Array} An array of HTMLElements
16378      */
16379     getSelectedNodes : function(){
16380         return this.selections;
16381     },
16382
16383     /**
16384      * Get the indexes of the selected nodes.
16385      * @return {Array}
16386      */
16387     getSelectedIndexes : function(){
16388         var indexes = [], s = this.selections;
16389         for(var i = 0, len = s.length; i < len; i++){
16390             indexes.push(s[i].nodeIndex);
16391         }
16392         return indexes;
16393     },
16394
16395     /**
16396      * Clear all selections
16397      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16398      */
16399     clearSelections : function(suppressEvent){
16400         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16401             this.cmp.elements = this.selections;
16402             this.cmp.removeClass(this.selectedClass);
16403             this.selections = [];
16404             if(!suppressEvent){
16405                 this.fireEvent("selectionchange", this, this.selections);
16406             }
16407         }
16408     },
16409
16410     /**
16411      * Returns true if the passed node is selected
16412      * @param {HTMLElement/Number} node The node or node index
16413      * @return {Boolean}
16414      */
16415     isSelected : function(node){
16416         var s = this.selections;
16417         if(s.length < 1){
16418             return false;
16419         }
16420         node = this.getNode(node);
16421         return s.indexOf(node) !== -1;
16422     },
16423
16424     /**
16425      * Selects nodes.
16426      * @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
16427      * @param {Boolean} keepExisting (optional) true to keep existing selections
16428      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16429      */
16430     select : function(nodeInfo, keepExisting, suppressEvent){
16431         if(nodeInfo instanceof Array){
16432             if(!keepExisting){
16433                 this.clearSelections(true);
16434             }
16435             for(var i = 0, len = nodeInfo.length; i < len; i++){
16436                 this.select(nodeInfo[i], true, true);
16437             }
16438             return;
16439         } 
16440         var node = this.getNode(nodeInfo);
16441         if(!node || this.isSelected(node)){
16442             return; // already selected.
16443         }
16444         if(!keepExisting){
16445             this.clearSelections(true);
16446         }
16447         
16448         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16449             Roo.fly(node).addClass(this.selectedClass);
16450             this.selections.push(node);
16451             if(!suppressEvent){
16452                 this.fireEvent("selectionchange", this, this.selections);
16453             }
16454         }
16455         
16456         
16457     },
16458       /**
16459      * Unselects nodes.
16460      * @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
16461      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16462      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16463      */
16464     unselect : function(nodeInfo, keepExisting, suppressEvent)
16465     {
16466         if(nodeInfo instanceof Array){
16467             Roo.each(this.selections, function(s) {
16468                 this.unselect(s, nodeInfo);
16469             }, this);
16470             return;
16471         }
16472         var node = this.getNode(nodeInfo);
16473         if(!node || !this.isSelected(node)){
16474             //Roo.log("not selected");
16475             return; // not selected.
16476         }
16477         // fireevent???
16478         var ns = [];
16479         Roo.each(this.selections, function(s) {
16480             if (s == node ) {
16481                 Roo.fly(node).removeClass(this.selectedClass);
16482
16483                 return;
16484             }
16485             ns.push(s);
16486         },this);
16487         
16488         this.selections= ns;
16489         this.fireEvent("selectionchange", this, this.selections);
16490     },
16491
16492     /**
16493      * Gets a template node.
16494      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16495      * @return {HTMLElement} The node or null if it wasn't found
16496      */
16497     getNode : function(nodeInfo){
16498         if(typeof nodeInfo == "string"){
16499             return document.getElementById(nodeInfo);
16500         }else if(typeof nodeInfo == "number"){
16501             return this.nodes[nodeInfo];
16502         }
16503         return nodeInfo;
16504     },
16505
16506     /**
16507      * Gets a range template nodes.
16508      * @param {Number} startIndex
16509      * @param {Number} endIndex
16510      * @return {Array} An array of nodes
16511      */
16512     getNodes : function(start, end){
16513         var ns = this.nodes;
16514         start = start || 0;
16515         end = typeof end == "undefined" ? ns.length - 1 : end;
16516         var nodes = [];
16517         if(start <= end){
16518             for(var i = start; i <= end; i++){
16519                 nodes.push(ns[i]);
16520             }
16521         } else{
16522             for(var i = start; i >= end; i--){
16523                 nodes.push(ns[i]);
16524             }
16525         }
16526         return nodes;
16527     },
16528
16529     /**
16530      * Finds the index of the passed node
16531      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16532      * @return {Number} The index of the node or -1
16533      */
16534     indexOf : function(node){
16535         node = this.getNode(node);
16536         if(typeof node.nodeIndex == "number"){
16537             return node.nodeIndex;
16538         }
16539         var ns = this.nodes;
16540         for(var i = 0, len = ns.length; i < len; i++){
16541             if(ns[i] == node){
16542                 return i;
16543             }
16544         }
16545         return -1;
16546     }
16547 });
16548 /*
16549  * - LGPL
16550  *
16551  * based on jquery fullcalendar
16552  * 
16553  */
16554
16555 Roo.bootstrap = Roo.bootstrap || {};
16556 /**
16557  * @class Roo.bootstrap.Calendar
16558  * @extends Roo.bootstrap.Component
16559  * Bootstrap Calendar class
16560  * @cfg {Boolean} loadMask (true|false) default false
16561  * @cfg {Object} header generate the user specific header of the calendar, default false
16562
16563  * @constructor
16564  * Create a new Container
16565  * @param {Object} config The config object
16566  */
16567
16568
16569
16570 Roo.bootstrap.Calendar = function(config){
16571     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16572      this.addEvents({
16573         /**
16574              * @event select
16575              * Fires when a date is selected
16576              * @param {DatePicker} this
16577              * @param {Date} date The selected date
16578              */
16579         'select': true,
16580         /**
16581              * @event monthchange
16582              * Fires when the displayed month changes 
16583              * @param {DatePicker} this
16584              * @param {Date} date The selected month
16585              */
16586         'monthchange': true,
16587         /**
16588              * @event evententer
16589              * Fires when mouse over an event
16590              * @param {Calendar} this
16591              * @param {event} Event
16592              */
16593         'evententer': true,
16594         /**
16595              * @event eventleave
16596              * Fires when the mouse leaves an
16597              * @param {Calendar} this
16598              * @param {event}
16599              */
16600         'eventleave': true,
16601         /**
16602              * @event eventclick
16603              * Fires when the mouse click an
16604              * @param {Calendar} this
16605              * @param {event}
16606              */
16607         'eventclick': true
16608         
16609     });
16610
16611 };
16612
16613 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16614     
16615      /**
16616      * @cfg {Number} startDay
16617      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16618      */
16619     startDay : 0,
16620     
16621     loadMask : false,
16622     
16623     header : false,
16624       
16625     getAutoCreate : function(){
16626         
16627         
16628         var fc_button = function(name, corner, style, content ) {
16629             return Roo.apply({},{
16630                 tag : 'span',
16631                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16632                          (corner.length ?
16633                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16634                             ''
16635                         ),
16636                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16637                 unselectable: 'on'
16638             });
16639         };
16640         
16641         var header = {};
16642         
16643         if(!this.header){
16644             header = {
16645                 tag : 'table',
16646                 cls : 'fc-header',
16647                 style : 'width:100%',
16648                 cn : [
16649                     {
16650                         tag: 'tr',
16651                         cn : [
16652                             {
16653                                 tag : 'td',
16654                                 cls : 'fc-header-left',
16655                                 cn : [
16656                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16657                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16658                                     { tag: 'span', cls: 'fc-header-space' },
16659                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16660
16661
16662                                 ]
16663                             },
16664
16665                             {
16666                                 tag : 'td',
16667                                 cls : 'fc-header-center',
16668                                 cn : [
16669                                     {
16670                                         tag: 'span',
16671                                         cls: 'fc-header-title',
16672                                         cn : {
16673                                             tag: 'H2',
16674                                             html : 'month / year'
16675                                         }
16676                                     }
16677
16678                                 ]
16679                             },
16680                             {
16681                                 tag : 'td',
16682                                 cls : 'fc-header-right',
16683                                 cn : [
16684                               /*      fc_button('month', 'left', '', 'month' ),
16685                                     fc_button('week', '', '', 'week' ),
16686                                     fc_button('day', 'right', '', 'day' )
16687                                 */    
16688
16689                                 ]
16690                             }
16691
16692                         ]
16693                     }
16694                 ]
16695             };
16696         }
16697         
16698         header = this.header;
16699         
16700        
16701         var cal_heads = function() {
16702             var ret = [];
16703             // fixme - handle this.
16704             
16705             for (var i =0; i < Date.dayNames.length; i++) {
16706                 var d = Date.dayNames[i];
16707                 ret.push({
16708                     tag: 'th',
16709                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16710                     html : d.substring(0,3)
16711                 });
16712                 
16713             }
16714             ret[0].cls += ' fc-first';
16715             ret[6].cls += ' fc-last';
16716             return ret;
16717         };
16718         var cal_cell = function(n) {
16719             return  {
16720                 tag: 'td',
16721                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16722                 cn : [
16723                     {
16724                         cn : [
16725                             {
16726                                 cls: 'fc-day-number',
16727                                 html: 'D'
16728                             },
16729                             {
16730                                 cls: 'fc-day-content',
16731                              
16732                                 cn : [
16733                                      {
16734                                         style: 'position: relative;' // height: 17px;
16735                                     }
16736                                 ]
16737                             }
16738                             
16739                             
16740                         ]
16741                     }
16742                 ]
16743                 
16744             }
16745         };
16746         var cal_rows = function() {
16747             
16748             var ret = [];
16749             for (var r = 0; r < 6; r++) {
16750                 var row= {
16751                     tag : 'tr',
16752                     cls : 'fc-week',
16753                     cn : []
16754                 };
16755                 
16756                 for (var i =0; i < Date.dayNames.length; i++) {
16757                     var d = Date.dayNames[i];
16758                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16759
16760                 }
16761                 row.cn[0].cls+=' fc-first';
16762                 row.cn[0].cn[0].style = 'min-height:90px';
16763                 row.cn[6].cls+=' fc-last';
16764                 ret.push(row);
16765                 
16766             }
16767             ret[0].cls += ' fc-first';
16768             ret[4].cls += ' fc-prev-last';
16769             ret[5].cls += ' fc-last';
16770             return ret;
16771             
16772         };
16773         
16774         var cal_table = {
16775             tag: 'table',
16776             cls: 'fc-border-separate',
16777             style : 'width:100%',
16778             cellspacing  : 0,
16779             cn : [
16780                 { 
16781                     tag: 'thead',
16782                     cn : [
16783                         { 
16784                             tag: 'tr',
16785                             cls : 'fc-first fc-last',
16786                             cn : cal_heads()
16787                         }
16788                     ]
16789                 },
16790                 { 
16791                     tag: 'tbody',
16792                     cn : cal_rows()
16793                 }
16794                   
16795             ]
16796         };
16797          
16798          var cfg = {
16799             cls : 'fc fc-ltr',
16800             cn : [
16801                 header,
16802                 {
16803                     cls : 'fc-content',
16804                     style : "position: relative;",
16805                     cn : [
16806                         {
16807                             cls : 'fc-view fc-view-month fc-grid',
16808                             style : 'position: relative',
16809                             unselectable : 'on',
16810                             cn : [
16811                                 {
16812                                     cls : 'fc-event-container',
16813                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16814                                 },
16815                                 cal_table
16816                             ]
16817                         }
16818                     ]
16819     
16820                 }
16821            ] 
16822             
16823         };
16824         
16825          
16826         
16827         return cfg;
16828     },
16829     
16830     
16831     initEvents : function()
16832     {
16833         if(!this.store){
16834             throw "can not find store for calendar";
16835         }
16836         
16837         var mark = {
16838             tag: "div",
16839             cls:"x-dlg-mask",
16840             style: "text-align:center",
16841             cn: [
16842                 {
16843                     tag: "div",
16844                     style: "background-color:white;width:50%;margin:250 auto",
16845                     cn: [
16846                         {
16847                             tag: "img",
16848                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16849                         },
16850                         {
16851                             tag: "span",
16852                             html: "Loading"
16853                         }
16854                         
16855                     ]
16856                 }
16857             ]
16858         };
16859         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16860         
16861         var size = this.el.select('.fc-content', true).first().getSize();
16862         this.maskEl.setSize(size.width, size.height);
16863         this.maskEl.enableDisplayMode("block");
16864         if(!this.loadMask){
16865             this.maskEl.hide();
16866         }
16867         
16868         this.store = Roo.factory(this.store, Roo.data);
16869         this.store.on('load', this.onLoad, this);
16870         this.store.on('beforeload', this.onBeforeLoad, this);
16871         
16872         this.resize();
16873         
16874         this.cells = this.el.select('.fc-day',true);
16875         //Roo.log(this.cells);
16876         this.textNodes = this.el.query('.fc-day-number');
16877         this.cells.addClassOnOver('fc-state-hover');
16878         
16879         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16880         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16881         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16882         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16883         
16884         this.on('monthchange', this.onMonthChange, this);
16885         
16886         this.update(new Date().clearTime());
16887     },
16888     
16889     resize : function() {
16890         var sz  = this.el.getSize();
16891         
16892         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16893         this.el.select('.fc-day-content div',true).setHeight(34);
16894     },
16895     
16896     
16897     // private
16898     showPrevMonth : function(e){
16899         this.update(this.activeDate.add("mo", -1));
16900     },
16901     showToday : function(e){
16902         this.update(new Date().clearTime());
16903     },
16904     // private
16905     showNextMonth : function(e){
16906         this.update(this.activeDate.add("mo", 1));
16907     },
16908
16909     // private
16910     showPrevYear : function(){
16911         this.update(this.activeDate.add("y", -1));
16912     },
16913
16914     // private
16915     showNextYear : function(){
16916         this.update(this.activeDate.add("y", 1));
16917     },
16918
16919     
16920    // private
16921     update : function(date)
16922     {
16923         var vd = this.activeDate;
16924         this.activeDate = date;
16925 //        if(vd && this.el){
16926 //            var t = date.getTime();
16927 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16928 //                Roo.log('using add remove');
16929 //                
16930 //                this.fireEvent('monthchange', this, date);
16931 //                
16932 //                this.cells.removeClass("fc-state-highlight");
16933 //                this.cells.each(function(c){
16934 //                   if(c.dateValue == t){
16935 //                       c.addClass("fc-state-highlight");
16936 //                       setTimeout(function(){
16937 //                            try{c.dom.firstChild.focus();}catch(e){}
16938 //                       }, 50);
16939 //                       return false;
16940 //                   }
16941 //                   return true;
16942 //                });
16943 //                return;
16944 //            }
16945 //        }
16946         
16947         var days = date.getDaysInMonth();
16948         
16949         var firstOfMonth = date.getFirstDateOfMonth();
16950         var startingPos = firstOfMonth.getDay()-this.startDay;
16951         
16952         if(startingPos < this.startDay){
16953             startingPos += 7;
16954         }
16955         
16956         var pm = date.add(Date.MONTH, -1);
16957         var prevStart = pm.getDaysInMonth()-startingPos;
16958 //        
16959         this.cells = this.el.select('.fc-day',true);
16960         this.textNodes = this.el.query('.fc-day-number');
16961         this.cells.addClassOnOver('fc-state-hover');
16962         
16963         var cells = this.cells.elements;
16964         var textEls = this.textNodes;
16965         
16966         Roo.each(cells, function(cell){
16967             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16968         });
16969         
16970         days += startingPos;
16971
16972         // convert everything to numbers so it's fast
16973         var day = 86400000;
16974         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16975         //Roo.log(d);
16976         //Roo.log(pm);
16977         //Roo.log(prevStart);
16978         
16979         var today = new Date().clearTime().getTime();
16980         var sel = date.clearTime().getTime();
16981         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16982         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16983         var ddMatch = this.disabledDatesRE;
16984         var ddText = this.disabledDatesText;
16985         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16986         var ddaysText = this.disabledDaysText;
16987         var format = this.format;
16988         
16989         var setCellClass = function(cal, cell){
16990             cell.row = 0;
16991             cell.events = [];
16992             cell.more = [];
16993             //Roo.log('set Cell Class');
16994             cell.title = "";
16995             var t = d.getTime();
16996             
16997             //Roo.log(d);
16998             
16999             cell.dateValue = t;
17000             if(t == today){
17001                 cell.className += " fc-today";
17002                 cell.className += " fc-state-highlight";
17003                 cell.title = cal.todayText;
17004             }
17005             if(t == sel){
17006                 // disable highlight in other month..
17007                 //cell.className += " fc-state-highlight";
17008                 
17009             }
17010             // disabling
17011             if(t < min) {
17012                 cell.className = " fc-state-disabled";
17013                 cell.title = cal.minText;
17014                 return;
17015             }
17016             if(t > max) {
17017                 cell.className = " fc-state-disabled";
17018                 cell.title = cal.maxText;
17019                 return;
17020             }
17021             if(ddays){
17022                 if(ddays.indexOf(d.getDay()) != -1){
17023                     cell.title = ddaysText;
17024                     cell.className = " fc-state-disabled";
17025                 }
17026             }
17027             if(ddMatch && format){
17028                 var fvalue = d.dateFormat(format);
17029                 if(ddMatch.test(fvalue)){
17030                     cell.title = ddText.replace("%0", fvalue);
17031                     cell.className = " fc-state-disabled";
17032                 }
17033             }
17034             
17035             if (!cell.initialClassName) {
17036                 cell.initialClassName = cell.dom.className;
17037             }
17038             
17039             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17040         };
17041
17042         var i = 0;
17043         
17044         for(; i < startingPos; i++) {
17045             textEls[i].innerHTML = (++prevStart);
17046             d.setDate(d.getDate()+1);
17047             
17048             cells[i].className = "fc-past fc-other-month";
17049             setCellClass(this, cells[i]);
17050         }
17051         
17052         var intDay = 0;
17053         
17054         for(; i < days; i++){
17055             intDay = i - startingPos + 1;
17056             textEls[i].innerHTML = (intDay);
17057             d.setDate(d.getDate()+1);
17058             
17059             cells[i].className = ''; // "x-date-active";
17060             setCellClass(this, cells[i]);
17061         }
17062         var extraDays = 0;
17063         
17064         for(; i < 42; i++) {
17065             textEls[i].innerHTML = (++extraDays);
17066             d.setDate(d.getDate()+1);
17067             
17068             cells[i].className = "fc-future fc-other-month";
17069             setCellClass(this, cells[i]);
17070         }
17071         
17072         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17073         
17074         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17075         
17076         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17077         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17078         
17079         if(totalRows != 6){
17080             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17081             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17082         }
17083         
17084         this.fireEvent('monthchange', this, date);
17085         
17086         
17087         /*
17088         if(!this.internalRender){
17089             var main = this.el.dom.firstChild;
17090             var w = main.offsetWidth;
17091             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17092             Roo.fly(main).setWidth(w);
17093             this.internalRender = true;
17094             // opera does not respect the auto grow header center column
17095             // then, after it gets a width opera refuses to recalculate
17096             // without a second pass
17097             if(Roo.isOpera && !this.secondPass){
17098                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17099                 this.secondPass = true;
17100                 this.update.defer(10, this, [date]);
17101             }
17102         }
17103         */
17104         
17105     },
17106     
17107     findCell : function(dt) {
17108         dt = dt.clearTime().getTime();
17109         var ret = false;
17110         this.cells.each(function(c){
17111             //Roo.log("check " +c.dateValue + '?=' + dt);
17112             if(c.dateValue == dt){
17113                 ret = c;
17114                 return false;
17115             }
17116             return true;
17117         });
17118         
17119         return ret;
17120     },
17121     
17122     findCells : function(ev) {
17123         var s = ev.start.clone().clearTime().getTime();
17124        // Roo.log(s);
17125         var e= ev.end.clone().clearTime().getTime();
17126        // Roo.log(e);
17127         var ret = [];
17128         this.cells.each(function(c){
17129              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17130             
17131             if(c.dateValue > e){
17132                 return ;
17133             }
17134             if(c.dateValue < s){
17135                 return ;
17136             }
17137             ret.push(c);
17138         });
17139         
17140         return ret;    
17141     },
17142     
17143 //    findBestRow: function(cells)
17144 //    {
17145 //        var ret = 0;
17146 //        
17147 //        for (var i =0 ; i < cells.length;i++) {
17148 //            ret  = Math.max(cells[i].rows || 0,ret);
17149 //        }
17150 //        return ret;
17151 //        
17152 //    },
17153     
17154     
17155     addItem : function(ev)
17156     {
17157         // look for vertical location slot in
17158         var cells = this.findCells(ev);
17159         
17160 //        ev.row = this.findBestRow(cells);
17161         
17162         // work out the location.
17163         
17164         var crow = false;
17165         var rows = [];
17166         for(var i =0; i < cells.length; i++) {
17167             
17168             cells[i].row = cells[0].row;
17169             
17170             if(i == 0){
17171                 cells[i].row = cells[i].row + 1;
17172             }
17173             
17174             if (!crow) {
17175                 crow = {
17176                     start : cells[i],
17177                     end :  cells[i]
17178                 };
17179                 continue;
17180             }
17181             if (crow.start.getY() == cells[i].getY()) {
17182                 // on same row.
17183                 crow.end = cells[i];
17184                 continue;
17185             }
17186             // different row.
17187             rows.push(crow);
17188             crow = {
17189                 start: cells[i],
17190                 end : cells[i]
17191             };
17192             
17193         }
17194         
17195         rows.push(crow);
17196         ev.els = [];
17197         ev.rows = rows;
17198         ev.cells = cells;
17199         
17200         cells[0].events.push(ev);
17201         
17202         this.calevents.push(ev);
17203     },
17204     
17205     clearEvents: function() {
17206         
17207         if(!this.calevents){
17208             return;
17209         }
17210         
17211         Roo.each(this.cells.elements, function(c){
17212             c.row = 0;
17213             c.events = [];
17214             c.more = [];
17215         });
17216         
17217         Roo.each(this.calevents, function(e) {
17218             Roo.each(e.els, function(el) {
17219                 el.un('mouseenter' ,this.onEventEnter, this);
17220                 el.un('mouseleave' ,this.onEventLeave, this);
17221                 el.remove();
17222             },this);
17223         },this);
17224         
17225         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17226             e.remove();
17227         });
17228         
17229     },
17230     
17231     renderEvents: function()
17232     {   
17233         var _this = this;
17234         
17235         this.cells.each(function(c) {
17236             
17237             if(c.row < 5){
17238                 return;
17239             }
17240             
17241             var ev = c.events;
17242             
17243             var r = 4;
17244             if(c.row != c.events.length){
17245                 r = 4 - (4 - (c.row - c.events.length));
17246             }
17247             
17248             c.events = ev.slice(0, r);
17249             c.more = ev.slice(r);
17250             
17251             if(c.more.length && c.more.length == 1){
17252                 c.events.push(c.more.pop());
17253             }
17254             
17255             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17256             
17257         });
17258             
17259         this.cells.each(function(c) {
17260             
17261             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17262             
17263             
17264             for (var e = 0; e < c.events.length; e++){
17265                 var ev = c.events[e];
17266                 var rows = ev.rows;
17267                 
17268                 for(var i = 0; i < rows.length; i++) {
17269                 
17270                     // how many rows should it span..
17271
17272                     var  cfg = {
17273                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17274                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17275
17276                         unselectable : "on",
17277                         cn : [
17278                             {
17279                                 cls: 'fc-event-inner',
17280                                 cn : [
17281     //                                {
17282     //                                  tag:'span',
17283     //                                  cls: 'fc-event-time',
17284     //                                  html : cells.length > 1 ? '' : ev.time
17285     //                                },
17286                                     {
17287                                       tag:'span',
17288                                       cls: 'fc-event-title',
17289                                       html : String.format('{0}', ev.title)
17290                                     }
17291
17292
17293                                 ]
17294                             },
17295                             {
17296                                 cls: 'ui-resizable-handle ui-resizable-e',
17297                                 html : '&nbsp;&nbsp;&nbsp'
17298                             }
17299
17300                         ]
17301                     };
17302
17303                     if (i == 0) {
17304                         cfg.cls += ' fc-event-start';
17305                     }
17306                     if ((i+1) == rows.length) {
17307                         cfg.cls += ' fc-event-end';
17308                     }
17309
17310                     var ctr = _this.el.select('.fc-event-container',true).first();
17311                     var cg = ctr.createChild(cfg);
17312
17313                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17314                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17315
17316                     var r = (c.more.length) ? 1 : 0;
17317                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17318                     cg.setWidth(ebox.right - sbox.x -2);
17319
17320                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17321                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17322                     cg.on('click', _this.onEventClick, _this, ev);
17323
17324                     ev.els.push(cg);
17325                     
17326                 }
17327                 
17328             }
17329             
17330             
17331             if(c.more.length){
17332                 var  cfg = {
17333                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17334                     style : 'position: absolute',
17335                     unselectable : "on",
17336                     cn : [
17337                         {
17338                             cls: 'fc-event-inner',
17339                             cn : [
17340                                 {
17341                                   tag:'span',
17342                                   cls: 'fc-event-title',
17343                                   html : 'More'
17344                                 }
17345
17346
17347                             ]
17348                         },
17349                         {
17350                             cls: 'ui-resizable-handle ui-resizable-e',
17351                             html : '&nbsp;&nbsp;&nbsp'
17352                         }
17353
17354                     ]
17355                 };
17356
17357                 var ctr = _this.el.select('.fc-event-container',true).first();
17358                 var cg = ctr.createChild(cfg);
17359
17360                 var sbox = c.select('.fc-day-content',true).first().getBox();
17361                 var ebox = c.select('.fc-day-content',true).first().getBox();
17362                 //Roo.log(cg);
17363                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17364                 cg.setWidth(ebox.right - sbox.x -2);
17365
17366                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17367                 
17368             }
17369             
17370         });
17371         
17372         
17373         
17374     },
17375     
17376     onEventEnter: function (e, el,event,d) {
17377         this.fireEvent('evententer', this, el, event);
17378     },
17379     
17380     onEventLeave: function (e, el,event,d) {
17381         this.fireEvent('eventleave', this, el, event);
17382     },
17383     
17384     onEventClick: function (e, el,event,d) {
17385         this.fireEvent('eventclick', this, el, event);
17386     },
17387     
17388     onMonthChange: function () {
17389         this.store.load();
17390     },
17391     
17392     onMoreEventClick: function(e, el, more)
17393     {
17394         var _this = this;
17395         
17396         this.calpopover.placement = 'right';
17397         this.calpopover.setTitle('More');
17398         
17399         this.calpopover.setContent('');
17400         
17401         var ctr = this.calpopover.el.select('.popover-content', true).first();
17402         
17403         Roo.each(more, function(m){
17404             var cfg = {
17405                 cls : 'fc-event-hori fc-event-draggable',
17406                 html : m.title
17407             };
17408             var cg = ctr.createChild(cfg);
17409             
17410             cg.on('click', _this.onEventClick, _this, m);
17411         });
17412         
17413         this.calpopover.show(el);
17414         
17415         
17416     },
17417     
17418     onLoad: function () 
17419     {   
17420         this.calevents = [];
17421         var cal = this;
17422         
17423         if(this.store.getCount() > 0){
17424             this.store.data.each(function(d){
17425                cal.addItem({
17426                     id : d.data.id,
17427                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17428                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17429                     time : d.data.start_time,
17430                     title : d.data.title,
17431                     description : d.data.description,
17432                     venue : d.data.venue
17433                 });
17434             });
17435         }
17436         
17437         this.renderEvents();
17438         
17439         if(this.calevents.length && this.loadMask){
17440             this.maskEl.hide();
17441         }
17442     },
17443     
17444     onBeforeLoad: function()
17445     {
17446         this.clearEvents();
17447         if(this.loadMask){
17448             this.maskEl.show();
17449         }
17450     }
17451 });
17452
17453  
17454  /*
17455  * - LGPL
17456  *
17457  * element
17458  * 
17459  */
17460
17461 /**
17462  * @class Roo.bootstrap.Popover
17463  * @extends Roo.bootstrap.Component
17464  * Bootstrap Popover class
17465  * @cfg {String} html contents of the popover   (or false to use children..)
17466  * @cfg {String} title of popover (or false to hide)
17467  * @cfg {String} placement how it is placed
17468  * @cfg {String} trigger click || hover (or false to trigger manually)
17469  * @cfg {String} over what (parent or false to trigger manually.)
17470  * @cfg {Number} delay - delay before showing
17471  
17472  * @constructor
17473  * Create a new Popover
17474  * @param {Object} config The config object
17475  */
17476
17477 Roo.bootstrap.Popover = function(config){
17478     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17479     
17480     this.addEvents({
17481         // raw events
17482          /**
17483          * @event show
17484          * After the popover show
17485          * 
17486          * @param {Roo.bootstrap.Popover} this
17487          */
17488         "show" : true,
17489         /**
17490          * @event hide
17491          * After the popover hide
17492          * 
17493          * @param {Roo.bootstrap.Popover} this
17494          */
17495         "hide" : true
17496     });
17497 };
17498
17499 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17500     
17501     title: 'Fill in a title',
17502     html: false,
17503     
17504     placement : 'right',
17505     trigger : 'hover', // hover
17506     
17507     delay : 0,
17508     
17509     over: 'parent',
17510     
17511     can_build_overlaid : false,
17512     
17513     getChildContainer : function()
17514     {
17515         return this.el.select('.popover-content',true).first();
17516     },
17517     
17518     getAutoCreate : function(){
17519          
17520         var cfg = {
17521            cls : 'popover roo-dynamic',
17522            style: 'display:block',
17523            cn : [
17524                 {
17525                     cls : 'arrow'
17526                 },
17527                 {
17528                     cls : 'popover-inner',
17529                     cn : [
17530                         {
17531                             tag: 'h3',
17532                             cls: 'popover-title',
17533                             html : this.title
17534                         },
17535                         {
17536                             cls : 'popover-content',
17537                             html : this.html
17538                         }
17539                     ]
17540                     
17541                 }
17542            ]
17543         };
17544         
17545         return cfg;
17546     },
17547     setTitle: function(str)
17548     {
17549         this.title = str;
17550         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17551     },
17552     setContent: function(str)
17553     {
17554         this.html = str;
17555         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17556     },
17557     // as it get's added to the bottom of the page.
17558     onRender : function(ct, position)
17559     {
17560         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17561         if(!this.el){
17562             var cfg = Roo.apply({},  this.getAutoCreate());
17563             cfg.id = Roo.id();
17564             
17565             if (this.cls) {
17566                 cfg.cls += ' ' + this.cls;
17567             }
17568             if (this.style) {
17569                 cfg.style = this.style;
17570             }
17571             //Roo.log("adding to ");
17572             this.el = Roo.get(document.body).createChild(cfg, position);
17573 //            Roo.log(this.el);
17574         }
17575         this.initEvents();
17576     },
17577     
17578     initEvents : function()
17579     {
17580         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17581         this.el.enableDisplayMode('block');
17582         this.el.hide();
17583         if (this.over === false) {
17584             return; 
17585         }
17586         if (this.triggers === false) {
17587             return;
17588         }
17589         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17590         var triggers = this.trigger ? this.trigger.split(' ') : [];
17591         Roo.each(triggers, function(trigger) {
17592         
17593             if (trigger == 'click') {
17594                 on_el.on('click', this.toggle, this);
17595             } else if (trigger != 'manual') {
17596                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17597                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17598       
17599                 on_el.on(eventIn  ,this.enter, this);
17600                 on_el.on(eventOut, this.leave, this);
17601             }
17602         }, this);
17603         
17604     },
17605     
17606     
17607     // private
17608     timeout : null,
17609     hoverState : null,
17610     
17611     toggle : function () {
17612         this.hoverState == 'in' ? this.leave() : this.enter();
17613     },
17614     
17615     enter : function () {
17616         
17617         clearTimeout(this.timeout);
17618     
17619         this.hoverState = 'in';
17620     
17621         if (!this.delay || !this.delay.show) {
17622             this.show();
17623             return;
17624         }
17625         var _t = this;
17626         this.timeout = setTimeout(function () {
17627             if (_t.hoverState == 'in') {
17628                 _t.show();
17629             }
17630         }, this.delay.show)
17631     },
17632     
17633     leave : function() {
17634         clearTimeout(this.timeout);
17635     
17636         this.hoverState = 'out';
17637     
17638         if (!this.delay || !this.delay.hide) {
17639             this.hide();
17640             return;
17641         }
17642         var _t = this;
17643         this.timeout = setTimeout(function () {
17644             if (_t.hoverState == 'out') {
17645                 _t.hide();
17646             }
17647         }, this.delay.hide)
17648     },
17649     
17650     show : function (on_el)
17651     {
17652         if (!on_el) {
17653             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17654         }
17655         
17656         // set content.
17657         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17658         if (this.html !== false) {
17659             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17660         }
17661         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17662         if (!this.title.length) {
17663             this.el.select('.popover-title',true).hide();
17664         }
17665         
17666         var placement = typeof this.placement == 'function' ?
17667             this.placement.call(this, this.el, on_el) :
17668             this.placement;
17669             
17670         var autoToken = /\s?auto?\s?/i;
17671         var autoPlace = autoToken.test(placement);
17672         if (autoPlace) {
17673             placement = placement.replace(autoToken, '') || 'top';
17674         }
17675         
17676         //this.el.detach()
17677         //this.el.setXY([0,0]);
17678         this.el.show();
17679         this.el.dom.style.display='block';
17680         this.el.addClass(placement);
17681         
17682         //this.el.appendTo(on_el);
17683         
17684         var p = this.getPosition();
17685         var box = this.el.getBox();
17686         
17687         if (autoPlace) {
17688             // fixme..
17689         }
17690         var align = Roo.bootstrap.Popover.alignment[placement];
17691         
17692 //        Roo.log(align);
17693         this.el.alignTo(on_el, align[0],align[1]);
17694         //var arrow = this.el.select('.arrow',true).first();
17695         //arrow.set(align[2], 
17696         
17697         this.el.addClass('in');
17698         
17699         
17700         if (this.el.hasClass('fade')) {
17701             // fade it?
17702         }
17703         
17704         this.hoverState = 'in';
17705         
17706         this.fireEvent('show', this);
17707         
17708     },
17709     hide : function()
17710     {
17711         this.el.setXY([0,0]);
17712         this.el.removeClass('in');
17713         this.el.hide();
17714         this.hoverState = null;
17715         
17716         this.fireEvent('hide', this);
17717     }
17718     
17719 });
17720
17721 Roo.bootstrap.Popover.alignment = {
17722     'left' : ['r-l', [-10,0], 'right'],
17723     'right' : ['l-r', [10,0], 'left'],
17724     'bottom' : ['t-b', [0,10], 'top'],
17725     'top' : [ 'b-t', [0,-10], 'bottom']
17726 };
17727
17728  /*
17729  * - LGPL
17730  *
17731  * Progress
17732  * 
17733  */
17734
17735 /**
17736  * @class Roo.bootstrap.Progress
17737  * @extends Roo.bootstrap.Component
17738  * Bootstrap Progress class
17739  * @cfg {Boolean} striped striped of the progress bar
17740  * @cfg {Boolean} active animated of the progress bar
17741  * 
17742  * 
17743  * @constructor
17744  * Create a new Progress
17745  * @param {Object} config The config object
17746  */
17747
17748 Roo.bootstrap.Progress = function(config){
17749     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17750 };
17751
17752 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17753     
17754     striped : false,
17755     active: false,
17756     
17757     getAutoCreate : function(){
17758         var cfg = {
17759             tag: 'div',
17760             cls: 'progress'
17761         };
17762         
17763         
17764         if(this.striped){
17765             cfg.cls += ' progress-striped';
17766         }
17767       
17768         if(this.active){
17769             cfg.cls += ' active';
17770         }
17771         
17772         
17773         return cfg;
17774     }
17775    
17776 });
17777
17778  
17779
17780  /*
17781  * - LGPL
17782  *
17783  * ProgressBar
17784  * 
17785  */
17786
17787 /**
17788  * @class Roo.bootstrap.ProgressBar
17789  * @extends Roo.bootstrap.Component
17790  * Bootstrap ProgressBar class
17791  * @cfg {Number} aria_valuenow aria-value now
17792  * @cfg {Number} aria_valuemin aria-value min
17793  * @cfg {Number} aria_valuemax aria-value max
17794  * @cfg {String} label label for the progress bar
17795  * @cfg {String} panel (success | info | warning | danger )
17796  * @cfg {String} role role of the progress bar
17797  * @cfg {String} sr_only text
17798  * 
17799  * 
17800  * @constructor
17801  * Create a new ProgressBar
17802  * @param {Object} config The config object
17803  */
17804
17805 Roo.bootstrap.ProgressBar = function(config){
17806     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17807 };
17808
17809 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17810     
17811     aria_valuenow : 0,
17812     aria_valuemin : 0,
17813     aria_valuemax : 100,
17814     label : false,
17815     panel : false,
17816     role : false,
17817     sr_only: false,
17818     
17819     getAutoCreate : function()
17820     {
17821         
17822         var cfg = {
17823             tag: 'div',
17824             cls: 'progress-bar',
17825             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17826         };
17827         
17828         if(this.sr_only){
17829             cfg.cn = {
17830                 tag: 'span',
17831                 cls: 'sr-only',
17832                 html: this.sr_only
17833             }
17834         }
17835         
17836         if(this.role){
17837             cfg.role = this.role;
17838         }
17839         
17840         if(this.aria_valuenow){
17841             cfg['aria-valuenow'] = this.aria_valuenow;
17842         }
17843         
17844         if(this.aria_valuemin){
17845             cfg['aria-valuemin'] = this.aria_valuemin;
17846         }
17847         
17848         if(this.aria_valuemax){
17849             cfg['aria-valuemax'] = this.aria_valuemax;
17850         }
17851         
17852         if(this.label && !this.sr_only){
17853             cfg.html = this.label;
17854         }
17855         
17856         if(this.panel){
17857             cfg.cls += ' progress-bar-' + this.panel;
17858         }
17859         
17860         return cfg;
17861     },
17862     
17863     update : function(aria_valuenow)
17864     {
17865         this.aria_valuenow = aria_valuenow;
17866         
17867         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17868     }
17869    
17870 });
17871
17872  
17873
17874  /*
17875  * - LGPL
17876  *
17877  * column
17878  * 
17879  */
17880
17881 /**
17882  * @class Roo.bootstrap.TabGroup
17883  * @extends Roo.bootstrap.Column
17884  * Bootstrap Column class
17885  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17886  * @cfg {Boolean} carousel true to make the group behave like a carousel
17887  * @cfg {Boolean} bullets show bullets for the panels
17888  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17889  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17890  * @cfg {Boolean} showarrow (true|false) show arrow default true
17891  * 
17892  * @constructor
17893  * Create a new TabGroup
17894  * @param {Object} config The config object
17895  */
17896
17897 Roo.bootstrap.TabGroup = function(config){
17898     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17899     if (!this.navId) {
17900         this.navId = Roo.id();
17901     }
17902     this.tabs = [];
17903     Roo.bootstrap.TabGroup.register(this);
17904     
17905 };
17906
17907 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17908     
17909     carousel : false,
17910     transition : false,
17911     bullets : 0,
17912     timer : 0,
17913     autoslide : false,
17914     slideFn : false,
17915     slideOnTouch : false,
17916     showarrow : true,
17917     
17918     getAutoCreate : function()
17919     {
17920         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17921         
17922         cfg.cls += ' tab-content';
17923         
17924         if (this.carousel) {
17925             cfg.cls += ' carousel slide';
17926             
17927             cfg.cn = [{
17928                cls : 'carousel-inner',
17929                cn : []
17930             }];
17931         
17932             if(this.bullets  && !Roo.isTouch){
17933                 
17934                 var bullets = {
17935                     cls : 'carousel-bullets',
17936                     cn : []
17937                 };
17938                
17939                 if(this.bullets_cls){
17940                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17941                 }
17942                 
17943                 bullets.cn.push({
17944                     cls : 'clear'
17945                 });
17946                 
17947                 cfg.cn[0].cn.push(bullets);
17948             }
17949             
17950             if(this.showarrow){
17951                 cfg.cn[0].cn.push({
17952                     tag : 'div',
17953                     class : 'carousel-arrow',
17954                     cn : [
17955                         {
17956                             tag : 'div',
17957                             class : 'carousel-prev',
17958                             cn : [
17959                                 {
17960                                     tag : 'i',
17961                                     class : 'fa fa-chevron-left'
17962                                 }
17963                             ]
17964                         },
17965                         {
17966                             tag : 'div',
17967                             class : 'carousel-next',
17968                             cn : [
17969                                 {
17970                                     tag : 'i',
17971                                     class : 'fa fa-chevron-right'
17972                                 }
17973                             ]
17974                         }
17975                     ]
17976                 });
17977             }
17978             
17979         }
17980         
17981         return cfg;
17982     },
17983     
17984     initEvents:  function()
17985     {
17986 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17987 //            this.el.on("touchstart", this.onTouchStart, this);
17988 //        }
17989         
17990         if(this.autoslide){
17991             var _this = this;
17992             
17993             this.slideFn = window.setInterval(function() {
17994                 _this.showPanelNext();
17995             }, this.timer);
17996         }
17997         
17998         if(this.showarrow){
17999             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18000             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18001         }
18002         
18003         
18004     },
18005     
18006 //    onTouchStart : function(e, el, o)
18007 //    {
18008 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18009 //            return;
18010 //        }
18011 //        
18012 //        this.showPanelNext();
18013 //    },
18014     
18015     
18016     getChildContainer : function()
18017     {
18018         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18019     },
18020     
18021     /**
18022     * register a Navigation item
18023     * @param {Roo.bootstrap.NavItem} the navitem to add
18024     */
18025     register : function(item)
18026     {
18027         this.tabs.push( item);
18028         item.navId = this.navId; // not really needed..
18029         this.addBullet();
18030     
18031     },
18032     
18033     getActivePanel : function()
18034     {
18035         var r = false;
18036         Roo.each(this.tabs, function(t) {
18037             if (t.active) {
18038                 r = t;
18039                 return false;
18040             }
18041             return null;
18042         });
18043         return r;
18044         
18045     },
18046     getPanelByName : function(n)
18047     {
18048         var r = false;
18049         Roo.each(this.tabs, function(t) {
18050             if (t.tabId == n) {
18051                 r = t;
18052                 return false;
18053             }
18054             return null;
18055         });
18056         return r;
18057     },
18058     indexOfPanel : function(p)
18059     {
18060         var r = false;
18061         Roo.each(this.tabs, function(t,i) {
18062             if (t.tabId == p.tabId) {
18063                 r = i;
18064                 return false;
18065             }
18066             return null;
18067         });
18068         return r;
18069     },
18070     /**
18071      * show a specific panel
18072      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18073      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18074      */
18075     showPanel : function (pan)
18076     {
18077         if(this.transition || typeof(pan) == 'undefined'){
18078             Roo.log("waiting for the transitionend");
18079             return;
18080         }
18081         
18082         if (typeof(pan) == 'number') {
18083             pan = this.tabs[pan];
18084         }
18085         
18086         if (typeof(pan) == 'string') {
18087             pan = this.getPanelByName(pan);
18088         }
18089         
18090         var cur = this.getActivePanel();
18091         
18092         if(!pan || !cur){
18093             Roo.log('pan or acitve pan is undefined');
18094             return false;
18095         }
18096         
18097         if (pan.tabId == this.getActivePanel().tabId) {
18098             return true;
18099         }
18100         
18101         if (false === cur.fireEvent('beforedeactivate')) {
18102             return false;
18103         }
18104         
18105         if(this.bullets > 0 && !Roo.isTouch){
18106             this.setActiveBullet(this.indexOfPanel(pan));
18107         }
18108         
18109         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18110             
18111             this.transition = true;
18112             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18113             var lr = dir == 'next' ? 'left' : 'right';
18114             pan.el.addClass(dir); // or prev
18115             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18116             cur.el.addClass(lr); // or right
18117             pan.el.addClass(lr);
18118             
18119             var _this = this;
18120             cur.el.on('transitionend', function() {
18121                 Roo.log("trans end?");
18122                 
18123                 pan.el.removeClass([lr,dir]);
18124                 pan.setActive(true);
18125                 
18126                 cur.el.removeClass([lr]);
18127                 cur.setActive(false);
18128                 
18129                 _this.transition = false;
18130                 
18131             }, this, { single:  true } );
18132             
18133             return true;
18134         }
18135         
18136         cur.setActive(false);
18137         pan.setActive(true);
18138         
18139         return true;
18140         
18141     },
18142     showPanelNext : function()
18143     {
18144         var i = this.indexOfPanel(this.getActivePanel());
18145         
18146         if (i >= this.tabs.length - 1 && !this.autoslide) {
18147             return;
18148         }
18149         
18150         if (i >= this.tabs.length - 1 && this.autoslide) {
18151             i = -1;
18152         }
18153         
18154         this.showPanel(this.tabs[i+1]);
18155     },
18156     
18157     showPanelPrev : function()
18158     {
18159         var i = this.indexOfPanel(this.getActivePanel());
18160         
18161         if (i  < 1 && !this.autoslide) {
18162             return;
18163         }
18164         
18165         if (i < 1 && this.autoslide) {
18166             i = this.tabs.length;
18167         }
18168         
18169         this.showPanel(this.tabs[i-1]);
18170     },
18171     
18172     
18173     addBullet: function()
18174     {
18175         if(!this.bullets || Roo.isTouch){
18176             return;
18177         }
18178         var ctr = this.el.select('.carousel-bullets',true).first();
18179         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18180         var bullet = ctr.createChild({
18181             cls : 'bullet bullet-' + i
18182         },ctr.dom.lastChild);
18183         
18184         
18185         var _this = this;
18186         
18187         bullet.on('click', (function(e, el, o, ii, t){
18188
18189             e.preventDefault();
18190
18191             this.showPanel(ii);
18192
18193             if(this.autoslide && this.slideFn){
18194                 clearInterval(this.slideFn);
18195                 this.slideFn = window.setInterval(function() {
18196                     _this.showPanelNext();
18197                 }, this.timer);
18198             }
18199
18200         }).createDelegate(this, [i, bullet], true));
18201                 
18202         
18203     },
18204      
18205     setActiveBullet : function(i)
18206     {
18207         if(Roo.isTouch){
18208             return;
18209         }
18210         
18211         Roo.each(this.el.select('.bullet', true).elements, function(el){
18212             el.removeClass('selected');
18213         });
18214
18215         var bullet = this.el.select('.bullet-' + i, true).first();
18216         
18217         if(!bullet){
18218             return;
18219         }
18220         
18221         bullet.addClass('selected');
18222     }
18223     
18224     
18225   
18226 });
18227
18228  
18229
18230  
18231  
18232 Roo.apply(Roo.bootstrap.TabGroup, {
18233     
18234     groups: {},
18235      /**
18236     * register a Navigation Group
18237     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18238     */
18239     register : function(navgrp)
18240     {
18241         this.groups[navgrp.navId] = navgrp;
18242         
18243     },
18244     /**
18245     * fetch a Navigation Group based on the navigation ID
18246     * if one does not exist , it will get created.
18247     * @param {string} the navgroup to add
18248     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18249     */
18250     get: function(navId) {
18251         if (typeof(this.groups[navId]) == 'undefined') {
18252             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18253         }
18254         return this.groups[navId] ;
18255     }
18256     
18257     
18258     
18259 });
18260
18261  /*
18262  * - LGPL
18263  *
18264  * TabPanel
18265  * 
18266  */
18267
18268 /**
18269  * @class Roo.bootstrap.TabPanel
18270  * @extends Roo.bootstrap.Component
18271  * Bootstrap TabPanel class
18272  * @cfg {Boolean} active panel active
18273  * @cfg {String} html panel content
18274  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18275  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18276  * @cfg {String} href click to link..
18277  * 
18278  * 
18279  * @constructor
18280  * Create a new TabPanel
18281  * @param {Object} config The config object
18282  */
18283
18284 Roo.bootstrap.TabPanel = function(config){
18285     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18286     this.addEvents({
18287         /**
18288              * @event changed
18289              * Fires when the active status changes
18290              * @param {Roo.bootstrap.TabPanel} this
18291              * @param {Boolean} state the new state
18292             
18293          */
18294         'changed': true,
18295         /**
18296              * @event beforedeactivate
18297              * Fires before a tab is de-activated - can be used to do validation on a form.
18298              * @param {Roo.bootstrap.TabPanel} this
18299              * @return {Boolean} false if there is an error
18300             
18301          */
18302         'beforedeactivate': true
18303      });
18304     
18305     this.tabId = this.tabId || Roo.id();
18306   
18307 };
18308
18309 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18310     
18311     active: false,
18312     html: false,
18313     tabId: false,
18314     navId : false,
18315     href : '',
18316     
18317     getAutoCreate : function(){
18318         var cfg = {
18319             tag: 'div',
18320             // item is needed for carousel - not sure if it has any effect otherwise
18321             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18322             html: this.html || ''
18323         };
18324         
18325         if(this.active){
18326             cfg.cls += ' active';
18327         }
18328         
18329         if(this.tabId){
18330             cfg.tabId = this.tabId;
18331         }
18332         
18333         
18334         return cfg;
18335     },
18336     
18337     initEvents:  function()
18338     {
18339         var p = this.parent();
18340         
18341         this.navId = this.navId || p.navId;
18342         
18343         if (typeof(this.navId) != 'undefined') {
18344             // not really needed.. but just in case.. parent should be a NavGroup.
18345             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18346             
18347             tg.register(this);
18348             
18349             var i = tg.tabs.length - 1;
18350             
18351             if(this.active && tg.bullets > 0 && i < tg.bullets){
18352                 tg.setActiveBullet(i);
18353             }
18354         }
18355         
18356         this.el.on('click', this.onClick, this);
18357         
18358         if(Roo.isTouch){
18359             this.el.on("touchstart", this.onTouchStart, this);
18360             this.el.on("touchmove", this.onTouchMove, this);
18361             this.el.on("touchend", this.onTouchEnd, this);
18362         }
18363         
18364     },
18365     
18366     onRender : function(ct, position)
18367     {
18368         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18369     },
18370     
18371     setActive : function(state)
18372     {
18373         Roo.log("panel - set active " + this.tabId + "=" + state);
18374         
18375         this.active = state;
18376         if (!state) {
18377             this.el.removeClass('active');
18378             
18379         } else  if (!this.el.hasClass('active')) {
18380             this.el.addClass('active');
18381         }
18382         
18383         this.fireEvent('changed', this, state);
18384     },
18385     
18386     onClick : function(e)
18387     {
18388         e.preventDefault();
18389         
18390         if(!this.href.length){
18391             return;
18392         }
18393         
18394         window.location.href = this.href;
18395     },
18396     
18397     startX : 0,
18398     startY : 0,
18399     endX : 0,
18400     endY : 0,
18401     swiping : false,
18402     
18403     onTouchStart : function(e)
18404     {
18405         this.swiping = false;
18406         
18407         this.startX = e.browserEvent.touches[0].clientX;
18408         this.startY = e.browserEvent.touches[0].clientY;
18409     },
18410     
18411     onTouchMove : function(e)
18412     {
18413         this.swiping = true;
18414         
18415         this.endX = e.browserEvent.touches[0].clientX;
18416         this.endY = e.browserEvent.touches[0].clientY;
18417     },
18418     
18419     onTouchEnd : function(e)
18420     {
18421         if(!this.swiping){
18422             this.onClick(e);
18423             return;
18424         }
18425         
18426         var tabGroup = this.parent();
18427         
18428         if(this.endX > this.startX){ // swiping right
18429             tabGroup.showPanelPrev();
18430             return;
18431         }
18432         
18433         if(this.startX > this.endX){ // swiping left
18434             tabGroup.showPanelNext();
18435             return;
18436         }
18437     }
18438     
18439     
18440 });
18441  
18442
18443  
18444
18445  /*
18446  * - LGPL
18447  *
18448  * DateField
18449  * 
18450  */
18451
18452 /**
18453  * @class Roo.bootstrap.DateField
18454  * @extends Roo.bootstrap.Input
18455  * Bootstrap DateField class
18456  * @cfg {Number} weekStart default 0
18457  * @cfg {String} viewMode default empty, (months|years)
18458  * @cfg {String} minViewMode default empty, (months|years)
18459  * @cfg {Number} startDate default -Infinity
18460  * @cfg {Number} endDate default Infinity
18461  * @cfg {Boolean} todayHighlight default false
18462  * @cfg {Boolean} todayBtn default false
18463  * @cfg {Boolean} calendarWeeks default false
18464  * @cfg {Object} daysOfWeekDisabled default empty
18465  * @cfg {Boolean} singleMode default false (true | false)
18466  * 
18467  * @cfg {Boolean} keyboardNavigation default true
18468  * @cfg {String} language default en
18469  * 
18470  * @constructor
18471  * Create a new DateField
18472  * @param {Object} config The config object
18473  */
18474
18475 Roo.bootstrap.DateField = function(config){
18476     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18477      this.addEvents({
18478             /**
18479              * @event show
18480              * Fires when this field show.
18481              * @param {Roo.bootstrap.DateField} this
18482              * @param {Mixed} date The date value
18483              */
18484             show : true,
18485             /**
18486              * @event show
18487              * Fires when this field hide.
18488              * @param {Roo.bootstrap.DateField} this
18489              * @param {Mixed} date The date value
18490              */
18491             hide : true,
18492             /**
18493              * @event select
18494              * Fires when select a date.
18495              * @param {Roo.bootstrap.DateField} this
18496              * @param {Mixed} date The date value
18497              */
18498             select : true,
18499             /**
18500              * @event beforeselect
18501              * Fires when before select a date.
18502              * @param {Roo.bootstrap.DateField} this
18503              * @param {Mixed} date The date value
18504              */
18505             beforeselect : true
18506         });
18507 };
18508
18509 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18510     
18511     /**
18512      * @cfg {String} format
18513      * The default date format string which can be overriden for localization support.  The format must be
18514      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18515      */
18516     format : "m/d/y",
18517     /**
18518      * @cfg {String} altFormats
18519      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18520      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18521      */
18522     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18523     
18524     weekStart : 0,
18525     
18526     viewMode : '',
18527     
18528     minViewMode : '',
18529     
18530     todayHighlight : false,
18531     
18532     todayBtn: false,
18533     
18534     language: 'en',
18535     
18536     keyboardNavigation: true,
18537     
18538     calendarWeeks: false,
18539     
18540     startDate: -Infinity,
18541     
18542     endDate: Infinity,
18543     
18544     daysOfWeekDisabled: [],
18545     
18546     _events: [],
18547     
18548     singleMode : false,
18549     
18550     UTCDate: function()
18551     {
18552         return new Date(Date.UTC.apply(Date, arguments));
18553     },
18554     
18555     UTCToday: function()
18556     {
18557         var today = new Date();
18558         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18559     },
18560     
18561     getDate: function() {
18562             var d = this.getUTCDate();
18563             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18564     },
18565     
18566     getUTCDate: function() {
18567             return this.date;
18568     },
18569     
18570     setDate: function(d) {
18571             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18572     },
18573     
18574     setUTCDate: function(d) {
18575             this.date = d;
18576             this.setValue(this.formatDate(this.date));
18577     },
18578         
18579     onRender: function(ct, position)
18580     {
18581         
18582         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18583         
18584         this.language = this.language || 'en';
18585         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18586         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18587         
18588         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18589         this.format = this.format || 'm/d/y';
18590         this.isInline = false;
18591         this.isInput = true;
18592         this.component = this.el.select('.add-on', true).first() || false;
18593         this.component = (this.component && this.component.length === 0) ? false : this.component;
18594         this.hasInput = this.component && this.inputEl().length;
18595         
18596         if (typeof(this.minViewMode === 'string')) {
18597             switch (this.minViewMode) {
18598                 case 'months':
18599                     this.minViewMode = 1;
18600                     break;
18601                 case 'years':
18602                     this.minViewMode = 2;
18603                     break;
18604                 default:
18605                     this.minViewMode = 0;
18606                     break;
18607             }
18608         }
18609         
18610         if (typeof(this.viewMode === 'string')) {
18611             switch (this.viewMode) {
18612                 case 'months':
18613                     this.viewMode = 1;
18614                     break;
18615                 case 'years':
18616                     this.viewMode = 2;
18617                     break;
18618                 default:
18619                     this.viewMode = 0;
18620                     break;
18621             }
18622         }
18623                 
18624         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18625         
18626 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18627         
18628         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18629         
18630         this.picker().on('mousedown', this.onMousedown, this);
18631         this.picker().on('click', this.onClick, this);
18632         
18633         this.picker().addClass('datepicker-dropdown');
18634         
18635         this.startViewMode = this.viewMode;
18636         
18637         if(this.singleMode){
18638             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18639                 v.setVisibilityMode(Roo.Element.DISPLAY);
18640                 v.hide();
18641             });
18642             
18643             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18644                 v.setStyle('width', '189px');
18645             });
18646         }
18647         
18648         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18649             if(!this.calendarWeeks){
18650                 v.remove();
18651                 return;
18652             }
18653             
18654             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18655             v.attr('colspan', function(i, val){
18656                 return parseInt(val) + 1;
18657             });
18658         });
18659                         
18660         
18661         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18662         
18663         this.setStartDate(this.startDate);
18664         this.setEndDate(this.endDate);
18665         
18666         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18667         
18668         this.fillDow();
18669         this.fillMonths();
18670         this.update();
18671         this.showMode();
18672         
18673         if(this.isInline) {
18674             this.showPopup();
18675         }
18676     },
18677     
18678     picker : function()
18679     {
18680         return this.pickerEl;
18681 //        return this.el.select('.datepicker', true).first();
18682     },
18683     
18684     fillDow: function()
18685     {
18686         var dowCnt = this.weekStart;
18687         
18688         var dow = {
18689             tag: 'tr',
18690             cn: [
18691                 
18692             ]
18693         };
18694         
18695         if(this.calendarWeeks){
18696             dow.cn.push({
18697                 tag: 'th',
18698                 cls: 'cw',
18699                 html: '&nbsp;'
18700             })
18701         }
18702         
18703         while (dowCnt < this.weekStart + 7) {
18704             dow.cn.push({
18705                 tag: 'th',
18706                 cls: 'dow',
18707                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18708             });
18709         }
18710         
18711         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18712     },
18713     
18714     fillMonths: function()
18715     {    
18716         var i = 0;
18717         var months = this.picker().select('>.datepicker-months td', true).first();
18718         
18719         months.dom.innerHTML = '';
18720         
18721         while (i < 12) {
18722             var month = {
18723                 tag: 'span',
18724                 cls: 'month',
18725                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18726             };
18727             
18728             months.createChild(month);
18729         }
18730         
18731     },
18732     
18733     update: function()
18734     {
18735         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;
18736         
18737         if (this.date < this.startDate) {
18738             this.viewDate = new Date(this.startDate);
18739         } else if (this.date > this.endDate) {
18740             this.viewDate = new Date(this.endDate);
18741         } else {
18742             this.viewDate = new Date(this.date);
18743         }
18744         
18745         this.fill();
18746     },
18747     
18748     fill: function() 
18749     {
18750         var d = new Date(this.viewDate),
18751                 year = d.getUTCFullYear(),
18752                 month = d.getUTCMonth(),
18753                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18754                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18755                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18756                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18757                 currentDate = this.date && this.date.valueOf(),
18758                 today = this.UTCToday();
18759         
18760         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18761         
18762 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18763         
18764 //        this.picker.select('>tfoot th.today').
18765 //                                              .text(dates[this.language].today)
18766 //                                              .toggle(this.todayBtn !== false);
18767     
18768         this.updateNavArrows();
18769         this.fillMonths();
18770                                                 
18771         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18772         
18773         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18774          
18775         prevMonth.setUTCDate(day);
18776         
18777         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18778         
18779         var nextMonth = new Date(prevMonth);
18780         
18781         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18782         
18783         nextMonth = nextMonth.valueOf();
18784         
18785         var fillMonths = false;
18786         
18787         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18788         
18789         while(prevMonth.valueOf() <= nextMonth) {
18790             var clsName = '';
18791             
18792             if (prevMonth.getUTCDay() === this.weekStart) {
18793                 if(fillMonths){
18794                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18795                 }
18796                     
18797                 fillMonths = {
18798                     tag: 'tr',
18799                     cn: []
18800                 };
18801                 
18802                 if(this.calendarWeeks){
18803                     // ISO 8601: First week contains first thursday.
18804                     // ISO also states week starts on Monday, but we can be more abstract here.
18805                     var
18806                     // Start of current week: based on weekstart/current date
18807                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18808                     // Thursday of this week
18809                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18810                     // First Thursday of year, year from thursday
18811                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18812                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18813                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18814                     
18815                     fillMonths.cn.push({
18816                         tag: 'td',
18817                         cls: 'cw',
18818                         html: calWeek
18819                     });
18820                 }
18821             }
18822             
18823             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18824                 clsName += ' old';
18825             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18826                 clsName += ' new';
18827             }
18828             if (this.todayHighlight &&
18829                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18830                 prevMonth.getUTCMonth() == today.getMonth() &&
18831                 prevMonth.getUTCDate() == today.getDate()) {
18832                 clsName += ' today';
18833             }
18834             
18835             if (currentDate && prevMonth.valueOf() === currentDate) {
18836                 clsName += ' active';
18837             }
18838             
18839             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18840                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18841                     clsName += ' disabled';
18842             }
18843             
18844             fillMonths.cn.push({
18845                 tag: 'td',
18846                 cls: 'day ' + clsName,
18847                 html: prevMonth.getDate()
18848             });
18849             
18850             prevMonth.setDate(prevMonth.getDate()+1);
18851         }
18852           
18853         var currentYear = this.date && this.date.getUTCFullYear();
18854         var currentMonth = this.date && this.date.getUTCMonth();
18855         
18856         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18857         
18858         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18859             v.removeClass('active');
18860             
18861             if(currentYear === year && k === currentMonth){
18862                 v.addClass('active');
18863             }
18864             
18865             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18866                 v.addClass('disabled');
18867             }
18868             
18869         });
18870         
18871         
18872         year = parseInt(year/10, 10) * 10;
18873         
18874         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18875         
18876         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18877         
18878         year -= 1;
18879         for (var i = -1; i < 11; i++) {
18880             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18881                 tag: 'span',
18882                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18883                 html: year
18884             });
18885             
18886             year += 1;
18887         }
18888     },
18889     
18890     showMode: function(dir) 
18891     {
18892         if (dir) {
18893             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18894         }
18895         
18896         Roo.each(this.picker().select('>div',true).elements, function(v){
18897             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18898             v.hide();
18899         });
18900         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18901     },
18902     
18903     place: function()
18904     {
18905         if(this.isInline) {
18906             return;
18907         }
18908         
18909         this.picker().removeClass(['bottom', 'top']);
18910         
18911         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18912             /*
18913              * place to the top of element!
18914              *
18915              */
18916             
18917             this.picker().addClass('top');
18918             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18919             
18920             return;
18921         }
18922         
18923         this.picker().addClass('bottom');
18924         
18925         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18926     },
18927     
18928     parseDate : function(value)
18929     {
18930         if(!value || value instanceof Date){
18931             return value;
18932         }
18933         var v = Date.parseDate(value, this.format);
18934         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18935             v = Date.parseDate(value, 'Y-m-d');
18936         }
18937         if(!v && this.altFormats){
18938             if(!this.altFormatsArray){
18939                 this.altFormatsArray = this.altFormats.split("|");
18940             }
18941             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18942                 v = Date.parseDate(value, this.altFormatsArray[i]);
18943             }
18944         }
18945         return v;
18946     },
18947     
18948     formatDate : function(date, fmt)
18949     {   
18950         return (!date || !(date instanceof Date)) ?
18951         date : date.dateFormat(fmt || this.format);
18952     },
18953     
18954     onFocus : function()
18955     {
18956         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18957         this.showPopup();
18958     },
18959     
18960     onBlur : function()
18961     {
18962         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18963         
18964         var d = this.inputEl().getValue();
18965         
18966         this.setValue(d);
18967                 
18968         this.hidePopup();
18969     },
18970     
18971     showPopup : function()
18972     {
18973         this.picker().show();
18974         this.update();
18975         this.place();
18976         
18977         this.fireEvent('showpopup', this, this.date);
18978     },
18979     
18980     hidePopup : function()
18981     {
18982         if(this.isInline) {
18983             return;
18984         }
18985         this.picker().hide();
18986         this.viewMode = this.startViewMode;
18987         this.showMode();
18988         
18989         this.fireEvent('hidepopup', this, this.date);
18990         
18991     },
18992     
18993     onMousedown: function(e)
18994     {
18995         e.stopPropagation();
18996         e.preventDefault();
18997     },
18998     
18999     keyup: function(e)
19000     {
19001         Roo.bootstrap.DateField.superclass.keyup.call(this);
19002         this.update();
19003     },
19004
19005     setValue: function(v)
19006     {
19007         if(this.fireEvent('beforeselect', this, v) !== false){
19008             var d = new Date(this.parseDate(v) ).clearTime();
19009         
19010             if(isNaN(d.getTime())){
19011                 this.date = this.viewDate = '';
19012                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19013                 return;
19014             }
19015
19016             v = this.formatDate(d);
19017
19018             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19019
19020             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19021
19022             this.update();
19023
19024             this.fireEvent('select', this, this.date);
19025         }
19026     },
19027     
19028     getValue: function()
19029     {
19030         return this.formatDate(this.date);
19031     },
19032     
19033     fireKey: function(e)
19034     {
19035         if (!this.picker().isVisible()){
19036             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19037                 this.showPopup();
19038             }
19039             return;
19040         }
19041         
19042         var dateChanged = false,
19043         dir, day, month,
19044         newDate, newViewDate;
19045         
19046         switch(e.keyCode){
19047             case 27: // escape
19048                 this.hidePopup();
19049                 e.preventDefault();
19050                 break;
19051             case 37: // left
19052             case 39: // right
19053                 if (!this.keyboardNavigation) {
19054                     break;
19055                 }
19056                 dir = e.keyCode == 37 ? -1 : 1;
19057                 
19058                 if (e.ctrlKey){
19059                     newDate = this.moveYear(this.date, dir);
19060                     newViewDate = this.moveYear(this.viewDate, dir);
19061                 } else if (e.shiftKey){
19062                     newDate = this.moveMonth(this.date, dir);
19063                     newViewDate = this.moveMonth(this.viewDate, dir);
19064                 } else {
19065                     newDate = new Date(this.date);
19066                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19067                     newViewDate = new Date(this.viewDate);
19068                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19069                 }
19070                 if (this.dateWithinRange(newDate)){
19071                     this.date = newDate;
19072                     this.viewDate = newViewDate;
19073                     this.setValue(this.formatDate(this.date));
19074 //                    this.update();
19075                     e.preventDefault();
19076                     dateChanged = true;
19077                 }
19078                 break;
19079             case 38: // up
19080             case 40: // down
19081                 if (!this.keyboardNavigation) {
19082                     break;
19083                 }
19084                 dir = e.keyCode == 38 ? -1 : 1;
19085                 if (e.ctrlKey){
19086                     newDate = this.moveYear(this.date, dir);
19087                     newViewDate = this.moveYear(this.viewDate, dir);
19088                 } else if (e.shiftKey){
19089                     newDate = this.moveMonth(this.date, dir);
19090                     newViewDate = this.moveMonth(this.viewDate, dir);
19091                 } else {
19092                     newDate = new Date(this.date);
19093                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19094                     newViewDate = new Date(this.viewDate);
19095                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19096                 }
19097                 if (this.dateWithinRange(newDate)){
19098                     this.date = newDate;
19099                     this.viewDate = newViewDate;
19100                     this.setValue(this.formatDate(this.date));
19101 //                    this.update();
19102                     e.preventDefault();
19103                     dateChanged = true;
19104                 }
19105                 break;
19106             case 13: // enter
19107                 this.setValue(this.formatDate(this.date));
19108                 this.hidePopup();
19109                 e.preventDefault();
19110                 break;
19111             case 9: // tab
19112                 this.setValue(this.formatDate(this.date));
19113                 this.hidePopup();
19114                 break;
19115             case 16: // shift
19116             case 17: // ctrl
19117             case 18: // alt
19118                 break;
19119             default :
19120                 this.hide();
19121                 
19122         }
19123     },
19124     
19125     
19126     onClick: function(e) 
19127     {
19128         e.stopPropagation();
19129         e.preventDefault();
19130         
19131         var target = e.getTarget();
19132         
19133         if(target.nodeName.toLowerCase() === 'i'){
19134             target = Roo.get(target).dom.parentNode;
19135         }
19136         
19137         var nodeName = target.nodeName;
19138         var className = target.className;
19139         var html = target.innerHTML;
19140         //Roo.log(nodeName);
19141         
19142         switch(nodeName.toLowerCase()) {
19143             case 'th':
19144                 switch(className) {
19145                     case 'switch':
19146                         this.showMode(1);
19147                         break;
19148                     case 'prev':
19149                     case 'next':
19150                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19151                         switch(this.viewMode){
19152                                 case 0:
19153                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19154                                         break;
19155                                 case 1:
19156                                 case 2:
19157                                         this.viewDate = this.moveYear(this.viewDate, dir);
19158                                         break;
19159                         }
19160                         this.fill();
19161                         break;
19162                     case 'today':
19163                         var date = new Date();
19164                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19165 //                        this.fill()
19166                         this.setValue(this.formatDate(this.date));
19167                         
19168                         this.hidePopup();
19169                         break;
19170                 }
19171                 break;
19172             case 'span':
19173                 if (className.indexOf('disabled') < 0) {
19174                     this.viewDate.setUTCDate(1);
19175                     if (className.indexOf('month') > -1) {
19176                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19177                     } else {
19178                         var year = parseInt(html, 10) || 0;
19179                         this.viewDate.setUTCFullYear(year);
19180                         
19181                     }
19182                     
19183                     if(this.singleMode){
19184                         this.setValue(this.formatDate(this.viewDate));
19185                         this.hidePopup();
19186                         return;
19187                     }
19188                     
19189                     this.showMode(-1);
19190                     this.fill();
19191                 }
19192                 break;
19193                 
19194             case 'td':
19195                 //Roo.log(className);
19196                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19197                     var day = parseInt(html, 10) || 1;
19198                     var year = this.viewDate.getUTCFullYear(),
19199                         month = this.viewDate.getUTCMonth();
19200
19201                     if (className.indexOf('old') > -1) {
19202                         if(month === 0 ){
19203                             month = 11;
19204                             year -= 1;
19205                         }else{
19206                             month -= 1;
19207                         }
19208                     } else if (className.indexOf('new') > -1) {
19209                         if (month == 11) {
19210                             month = 0;
19211                             year += 1;
19212                         } else {
19213                             month += 1;
19214                         }
19215                     }
19216                     //Roo.log([year,month,day]);
19217                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19218                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19219 //                    this.fill();
19220                     //Roo.log(this.formatDate(this.date));
19221                     this.setValue(this.formatDate(this.date));
19222                     this.hidePopup();
19223                 }
19224                 break;
19225         }
19226     },
19227     
19228     setStartDate: function(startDate)
19229     {
19230         this.startDate = startDate || -Infinity;
19231         if (this.startDate !== -Infinity) {
19232             this.startDate = this.parseDate(this.startDate);
19233         }
19234         this.update();
19235         this.updateNavArrows();
19236     },
19237
19238     setEndDate: function(endDate)
19239     {
19240         this.endDate = endDate || Infinity;
19241         if (this.endDate !== Infinity) {
19242             this.endDate = this.parseDate(this.endDate);
19243         }
19244         this.update();
19245         this.updateNavArrows();
19246     },
19247     
19248     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19249     {
19250         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19251         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19252             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19253         }
19254         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19255             return parseInt(d, 10);
19256         });
19257         this.update();
19258         this.updateNavArrows();
19259     },
19260     
19261     updateNavArrows: function() 
19262     {
19263         if(this.singleMode){
19264             return;
19265         }
19266         
19267         var d = new Date(this.viewDate),
19268         year = d.getUTCFullYear(),
19269         month = d.getUTCMonth();
19270         
19271         Roo.each(this.picker().select('.prev', true).elements, function(v){
19272             v.show();
19273             switch (this.viewMode) {
19274                 case 0:
19275
19276                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19277                         v.hide();
19278                     }
19279                     break;
19280                 case 1:
19281                 case 2:
19282                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19283                         v.hide();
19284                     }
19285                     break;
19286             }
19287         });
19288         
19289         Roo.each(this.picker().select('.next', true).elements, function(v){
19290             v.show();
19291             switch (this.viewMode) {
19292                 case 0:
19293
19294                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19295                         v.hide();
19296                     }
19297                     break;
19298                 case 1:
19299                 case 2:
19300                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19301                         v.hide();
19302                     }
19303                     break;
19304             }
19305         })
19306     },
19307     
19308     moveMonth: function(date, dir)
19309     {
19310         if (!dir) {
19311             return date;
19312         }
19313         var new_date = new Date(date.valueOf()),
19314         day = new_date.getUTCDate(),
19315         month = new_date.getUTCMonth(),
19316         mag = Math.abs(dir),
19317         new_month, test;
19318         dir = dir > 0 ? 1 : -1;
19319         if (mag == 1){
19320             test = dir == -1
19321             // If going back one month, make sure month is not current month
19322             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19323             ? function(){
19324                 return new_date.getUTCMonth() == month;
19325             }
19326             // If going forward one month, make sure month is as expected
19327             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19328             : function(){
19329                 return new_date.getUTCMonth() != new_month;
19330             };
19331             new_month = month + dir;
19332             new_date.setUTCMonth(new_month);
19333             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19334             if (new_month < 0 || new_month > 11) {
19335                 new_month = (new_month + 12) % 12;
19336             }
19337         } else {
19338             // For magnitudes >1, move one month at a time...
19339             for (var i=0; i<mag; i++) {
19340                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19341                 new_date = this.moveMonth(new_date, dir);
19342             }
19343             // ...then reset the day, keeping it in the new month
19344             new_month = new_date.getUTCMonth();
19345             new_date.setUTCDate(day);
19346             test = function(){
19347                 return new_month != new_date.getUTCMonth();
19348             };
19349         }
19350         // Common date-resetting loop -- if date is beyond end of month, make it
19351         // end of month
19352         while (test()){
19353             new_date.setUTCDate(--day);
19354             new_date.setUTCMonth(new_month);
19355         }
19356         return new_date;
19357     },
19358
19359     moveYear: function(date, dir)
19360     {
19361         return this.moveMonth(date, dir*12);
19362     },
19363
19364     dateWithinRange: function(date)
19365     {
19366         return date >= this.startDate && date <= this.endDate;
19367     },
19368
19369     
19370     remove: function() 
19371     {
19372         this.picker().remove();
19373     },
19374     
19375     validateValue : function(value)
19376     {
19377         if(this.getVisibilityEl().hasClass('hidden')){
19378             return true;
19379         }
19380         
19381         if(value.length < 1)  {
19382             if(this.allowBlank){
19383                 return true;
19384             }
19385             return false;
19386         }
19387         
19388         if(value.length < this.minLength){
19389             return false;
19390         }
19391         if(value.length > this.maxLength){
19392             return false;
19393         }
19394         if(this.vtype){
19395             var vt = Roo.form.VTypes;
19396             if(!vt[this.vtype](value, this)){
19397                 return false;
19398             }
19399         }
19400         if(typeof this.validator == "function"){
19401             var msg = this.validator(value);
19402             if(msg !== true){
19403                 return false;
19404             }
19405         }
19406         
19407         if(this.regex && !this.regex.test(value)){
19408             return false;
19409         }
19410         
19411         if(typeof(this.parseDate(value)) == 'undefined'){
19412             return false;
19413         }
19414         
19415         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19416             return false;
19417         }      
19418         
19419         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19420             return false;
19421         } 
19422         
19423         
19424         return true;
19425     },
19426     
19427     reset : function()
19428     {
19429         this.date = this.viewDate = '';
19430         
19431         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19432     }
19433    
19434 });
19435
19436 Roo.apply(Roo.bootstrap.DateField,  {
19437     
19438     head : {
19439         tag: 'thead',
19440         cn: [
19441         {
19442             tag: 'tr',
19443             cn: [
19444             {
19445                 tag: 'th',
19446                 cls: 'prev',
19447                 html: '<i class="fa fa-arrow-left"/>'
19448             },
19449             {
19450                 tag: 'th',
19451                 cls: 'switch',
19452                 colspan: '5'
19453             },
19454             {
19455                 tag: 'th',
19456                 cls: 'next',
19457                 html: '<i class="fa fa-arrow-right"/>'
19458             }
19459
19460             ]
19461         }
19462         ]
19463     },
19464     
19465     content : {
19466         tag: 'tbody',
19467         cn: [
19468         {
19469             tag: 'tr',
19470             cn: [
19471             {
19472                 tag: 'td',
19473                 colspan: '7'
19474             }
19475             ]
19476         }
19477         ]
19478     },
19479     
19480     footer : {
19481         tag: 'tfoot',
19482         cn: [
19483         {
19484             tag: 'tr',
19485             cn: [
19486             {
19487                 tag: 'th',
19488                 colspan: '7',
19489                 cls: 'today'
19490             }
19491                     
19492             ]
19493         }
19494         ]
19495     },
19496     
19497     dates:{
19498         en: {
19499             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19500             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19501             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19502             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19503             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19504             today: "Today"
19505         }
19506     },
19507     
19508     modes: [
19509     {
19510         clsName: 'days',
19511         navFnc: 'Month',
19512         navStep: 1
19513     },
19514     {
19515         clsName: 'months',
19516         navFnc: 'FullYear',
19517         navStep: 1
19518     },
19519     {
19520         clsName: 'years',
19521         navFnc: 'FullYear',
19522         navStep: 10
19523     }]
19524 });
19525
19526 Roo.apply(Roo.bootstrap.DateField,  {
19527   
19528     template : {
19529         tag: 'div',
19530         cls: 'datepicker dropdown-menu roo-dynamic',
19531         cn: [
19532         {
19533             tag: 'div',
19534             cls: 'datepicker-days',
19535             cn: [
19536             {
19537                 tag: 'table',
19538                 cls: 'table-condensed',
19539                 cn:[
19540                 Roo.bootstrap.DateField.head,
19541                 {
19542                     tag: 'tbody'
19543                 },
19544                 Roo.bootstrap.DateField.footer
19545                 ]
19546             }
19547             ]
19548         },
19549         {
19550             tag: 'div',
19551             cls: 'datepicker-months',
19552             cn: [
19553             {
19554                 tag: 'table',
19555                 cls: 'table-condensed',
19556                 cn:[
19557                 Roo.bootstrap.DateField.head,
19558                 Roo.bootstrap.DateField.content,
19559                 Roo.bootstrap.DateField.footer
19560                 ]
19561             }
19562             ]
19563         },
19564         {
19565             tag: 'div',
19566             cls: 'datepicker-years',
19567             cn: [
19568             {
19569                 tag: 'table',
19570                 cls: 'table-condensed',
19571                 cn:[
19572                 Roo.bootstrap.DateField.head,
19573                 Roo.bootstrap.DateField.content,
19574                 Roo.bootstrap.DateField.footer
19575                 ]
19576             }
19577             ]
19578         }
19579         ]
19580     }
19581 });
19582
19583  
19584
19585  /*
19586  * - LGPL
19587  *
19588  * TimeField
19589  * 
19590  */
19591
19592 /**
19593  * @class Roo.bootstrap.TimeField
19594  * @extends Roo.bootstrap.Input
19595  * Bootstrap DateField class
19596  * 
19597  * 
19598  * @constructor
19599  * Create a new TimeField
19600  * @param {Object} config The config object
19601  */
19602
19603 Roo.bootstrap.TimeField = function(config){
19604     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19605     this.addEvents({
19606             /**
19607              * @event show
19608              * Fires when this field show.
19609              * @param {Roo.bootstrap.DateField} thisthis
19610              * @param {Mixed} date The date value
19611              */
19612             show : true,
19613             /**
19614              * @event show
19615              * Fires when this field hide.
19616              * @param {Roo.bootstrap.DateField} this
19617              * @param {Mixed} date The date value
19618              */
19619             hide : true,
19620             /**
19621              * @event select
19622              * Fires when select a date.
19623              * @param {Roo.bootstrap.DateField} this
19624              * @param {Mixed} date The date value
19625              */
19626             select : true
19627         });
19628 };
19629
19630 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19631     
19632     /**
19633      * @cfg {String} format
19634      * The default time format string which can be overriden for localization support.  The format must be
19635      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19636      */
19637     format : "H:i",
19638        
19639     onRender: function(ct, position)
19640     {
19641         
19642         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19643                 
19644         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19645         
19646         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19647         
19648         this.pop = this.picker().select('>.datepicker-time',true).first();
19649         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19650         
19651         this.picker().on('mousedown', this.onMousedown, this);
19652         this.picker().on('click', this.onClick, this);
19653         
19654         this.picker().addClass('datepicker-dropdown');
19655     
19656         this.fillTime();
19657         this.update();
19658             
19659         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19660         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19661         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19662         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19663         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19664         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19665
19666     },
19667     
19668     fireKey: function(e){
19669         if (!this.picker().isVisible()){
19670             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19671                 this.show();
19672             }
19673             return;
19674         }
19675
19676         e.preventDefault();
19677         
19678         switch(e.keyCode){
19679             case 27: // escape
19680                 this.hide();
19681                 break;
19682             case 37: // left
19683             case 39: // right
19684                 this.onTogglePeriod();
19685                 break;
19686             case 38: // up
19687                 this.onIncrementMinutes();
19688                 break;
19689             case 40: // down
19690                 this.onDecrementMinutes();
19691                 break;
19692             case 13: // enter
19693             case 9: // tab
19694                 this.setTime();
19695                 break;
19696         }
19697     },
19698     
19699     onClick: function(e) {
19700         e.stopPropagation();
19701         e.preventDefault();
19702     },
19703     
19704     picker : function()
19705     {
19706         return this.el.select('.datepicker', true).first();
19707     },
19708     
19709     fillTime: function()
19710     {    
19711         var time = this.pop.select('tbody', true).first();
19712         
19713         time.dom.innerHTML = '';
19714         
19715         time.createChild({
19716             tag: 'tr',
19717             cn: [
19718                 {
19719                     tag: 'td',
19720                     cn: [
19721                         {
19722                             tag: 'a',
19723                             href: '#',
19724                             cls: 'btn',
19725                             cn: [
19726                                 {
19727                                     tag: 'span',
19728                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19729                                 }
19730                             ]
19731                         } 
19732                     ]
19733                 },
19734                 {
19735                     tag: 'td',
19736                     cls: 'separator'
19737                 },
19738                 {
19739                     tag: 'td',
19740                     cn: [
19741                         {
19742                             tag: 'a',
19743                             href: '#',
19744                             cls: 'btn',
19745                             cn: [
19746                                 {
19747                                     tag: 'span',
19748                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19749                                 }
19750                             ]
19751                         }
19752                     ]
19753                 },
19754                 {
19755                     tag: 'td',
19756                     cls: 'separator'
19757                 }
19758             ]
19759         });
19760         
19761         time.createChild({
19762             tag: 'tr',
19763             cn: [
19764                 {
19765                     tag: 'td',
19766                     cn: [
19767                         {
19768                             tag: 'span',
19769                             cls: 'timepicker-hour',
19770                             html: '00'
19771                         }  
19772                     ]
19773                 },
19774                 {
19775                     tag: 'td',
19776                     cls: 'separator',
19777                     html: ':'
19778                 },
19779                 {
19780                     tag: 'td',
19781                     cn: [
19782                         {
19783                             tag: 'span',
19784                             cls: 'timepicker-minute',
19785                             html: '00'
19786                         }  
19787                     ]
19788                 },
19789                 {
19790                     tag: 'td',
19791                     cls: 'separator'
19792                 },
19793                 {
19794                     tag: 'td',
19795                     cn: [
19796                         {
19797                             tag: 'button',
19798                             type: 'button',
19799                             cls: 'btn btn-primary period',
19800                             html: 'AM'
19801                             
19802                         }
19803                     ]
19804                 }
19805             ]
19806         });
19807         
19808         time.createChild({
19809             tag: 'tr',
19810             cn: [
19811                 {
19812                     tag: 'td',
19813                     cn: [
19814                         {
19815                             tag: 'a',
19816                             href: '#',
19817                             cls: 'btn',
19818                             cn: [
19819                                 {
19820                                     tag: 'span',
19821                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19822                                 }
19823                             ]
19824                         }
19825                     ]
19826                 },
19827                 {
19828                     tag: 'td',
19829                     cls: 'separator'
19830                 },
19831                 {
19832                     tag: 'td',
19833                     cn: [
19834                         {
19835                             tag: 'a',
19836                             href: '#',
19837                             cls: 'btn',
19838                             cn: [
19839                                 {
19840                                     tag: 'span',
19841                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19842                                 }
19843                             ]
19844                         }
19845                     ]
19846                 },
19847                 {
19848                     tag: 'td',
19849                     cls: 'separator'
19850                 }
19851             ]
19852         });
19853         
19854     },
19855     
19856     update: function()
19857     {
19858         
19859         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19860         
19861         this.fill();
19862     },
19863     
19864     fill: function() 
19865     {
19866         var hours = this.time.getHours();
19867         var minutes = this.time.getMinutes();
19868         var period = 'AM';
19869         
19870         if(hours > 11){
19871             period = 'PM';
19872         }
19873         
19874         if(hours == 0){
19875             hours = 12;
19876         }
19877         
19878         
19879         if(hours > 12){
19880             hours = hours - 12;
19881         }
19882         
19883         if(hours < 10){
19884             hours = '0' + hours;
19885         }
19886         
19887         if(minutes < 10){
19888             minutes = '0' + minutes;
19889         }
19890         
19891         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19892         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19893         this.pop.select('button', true).first().dom.innerHTML = period;
19894         
19895     },
19896     
19897     place: function()
19898     {   
19899         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19900         
19901         var cls = ['bottom'];
19902         
19903         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19904             cls.pop();
19905             cls.push('top');
19906         }
19907         
19908         cls.push('right');
19909         
19910         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19911             cls.pop();
19912             cls.push('left');
19913         }
19914         
19915         this.picker().addClass(cls.join('-'));
19916         
19917         var _this = this;
19918         
19919         Roo.each(cls, function(c){
19920             if(c == 'bottom'){
19921                 _this.picker().setTop(_this.inputEl().getHeight());
19922                 return;
19923             }
19924             if(c == 'top'){
19925                 _this.picker().setTop(0 - _this.picker().getHeight());
19926                 return;
19927             }
19928             
19929             if(c == 'left'){
19930                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19931                 return;
19932             }
19933             if(c == 'right'){
19934                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19935                 return;
19936             }
19937         });
19938         
19939     },
19940   
19941     onFocus : function()
19942     {
19943         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19944         this.show();
19945     },
19946     
19947     onBlur : function()
19948     {
19949         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19950         this.hide();
19951     },
19952     
19953     show : function()
19954     {
19955         this.picker().show();
19956         this.pop.show();
19957         this.update();
19958         this.place();
19959         
19960         this.fireEvent('show', this, this.date);
19961     },
19962     
19963     hide : function()
19964     {
19965         this.picker().hide();
19966         this.pop.hide();
19967         
19968         this.fireEvent('hide', this, this.date);
19969     },
19970     
19971     setTime : function()
19972     {
19973         this.hide();
19974         this.setValue(this.time.format(this.format));
19975         
19976         this.fireEvent('select', this, this.date);
19977         
19978         
19979     },
19980     
19981     onMousedown: function(e){
19982         e.stopPropagation();
19983         e.preventDefault();
19984     },
19985     
19986     onIncrementHours: function()
19987     {
19988         Roo.log('onIncrementHours');
19989         this.time = this.time.add(Date.HOUR, 1);
19990         this.update();
19991         
19992     },
19993     
19994     onDecrementHours: function()
19995     {
19996         Roo.log('onDecrementHours');
19997         this.time = this.time.add(Date.HOUR, -1);
19998         this.update();
19999     },
20000     
20001     onIncrementMinutes: function()
20002     {
20003         Roo.log('onIncrementMinutes');
20004         this.time = this.time.add(Date.MINUTE, 1);
20005         this.update();
20006     },
20007     
20008     onDecrementMinutes: function()
20009     {
20010         Roo.log('onDecrementMinutes');
20011         this.time = this.time.add(Date.MINUTE, -1);
20012         this.update();
20013     },
20014     
20015     onTogglePeriod: function()
20016     {
20017         Roo.log('onTogglePeriod');
20018         this.time = this.time.add(Date.HOUR, 12);
20019         this.update();
20020     }
20021     
20022    
20023 });
20024
20025 Roo.apply(Roo.bootstrap.TimeField,  {
20026     
20027     content : {
20028         tag: 'tbody',
20029         cn: [
20030             {
20031                 tag: 'tr',
20032                 cn: [
20033                 {
20034                     tag: 'td',
20035                     colspan: '7'
20036                 }
20037                 ]
20038             }
20039         ]
20040     },
20041     
20042     footer : {
20043         tag: 'tfoot',
20044         cn: [
20045             {
20046                 tag: 'tr',
20047                 cn: [
20048                 {
20049                     tag: 'th',
20050                     colspan: '7',
20051                     cls: '',
20052                     cn: [
20053                         {
20054                             tag: 'button',
20055                             cls: 'btn btn-info ok',
20056                             html: 'OK'
20057                         }
20058                     ]
20059                 }
20060
20061                 ]
20062             }
20063         ]
20064     }
20065 });
20066
20067 Roo.apply(Roo.bootstrap.TimeField,  {
20068   
20069     template : {
20070         tag: 'div',
20071         cls: 'datepicker dropdown-menu',
20072         cn: [
20073             {
20074                 tag: 'div',
20075                 cls: 'datepicker-time',
20076                 cn: [
20077                 {
20078                     tag: 'table',
20079                     cls: 'table-condensed',
20080                     cn:[
20081                     Roo.bootstrap.TimeField.content,
20082                     Roo.bootstrap.TimeField.footer
20083                     ]
20084                 }
20085                 ]
20086             }
20087         ]
20088     }
20089 });
20090
20091  
20092
20093  /*
20094  * - LGPL
20095  *
20096  * MonthField
20097  * 
20098  */
20099
20100 /**
20101  * @class Roo.bootstrap.MonthField
20102  * @extends Roo.bootstrap.Input
20103  * Bootstrap MonthField class
20104  * 
20105  * @cfg {String} language default en
20106  * 
20107  * @constructor
20108  * Create a new MonthField
20109  * @param {Object} config The config object
20110  */
20111
20112 Roo.bootstrap.MonthField = function(config){
20113     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20114     
20115     this.addEvents({
20116         /**
20117          * @event show
20118          * Fires when this field show.
20119          * @param {Roo.bootstrap.MonthField} this
20120          * @param {Mixed} date The date value
20121          */
20122         show : true,
20123         /**
20124          * @event show
20125          * Fires when this field hide.
20126          * @param {Roo.bootstrap.MonthField} this
20127          * @param {Mixed} date The date value
20128          */
20129         hide : true,
20130         /**
20131          * @event select
20132          * Fires when select a date.
20133          * @param {Roo.bootstrap.MonthField} this
20134          * @param {String} oldvalue The old value
20135          * @param {String} newvalue The new value
20136          */
20137         select : true
20138     });
20139 };
20140
20141 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20142     
20143     onRender: function(ct, position)
20144     {
20145         
20146         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20147         
20148         this.language = this.language || 'en';
20149         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20150         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20151         
20152         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20153         this.isInline = false;
20154         this.isInput = true;
20155         this.component = this.el.select('.add-on', true).first() || false;
20156         this.component = (this.component && this.component.length === 0) ? false : this.component;
20157         this.hasInput = this.component && this.inputEL().length;
20158         
20159         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20160         
20161         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20162         
20163         this.picker().on('mousedown', this.onMousedown, this);
20164         this.picker().on('click', this.onClick, this);
20165         
20166         this.picker().addClass('datepicker-dropdown');
20167         
20168         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20169             v.setStyle('width', '189px');
20170         });
20171         
20172         this.fillMonths();
20173         
20174         this.update();
20175         
20176         if(this.isInline) {
20177             this.show();
20178         }
20179         
20180     },
20181     
20182     setValue: function(v, suppressEvent)
20183     {   
20184         var o = this.getValue();
20185         
20186         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20187         
20188         this.update();
20189
20190         if(suppressEvent !== true){
20191             this.fireEvent('select', this, o, v);
20192         }
20193         
20194     },
20195     
20196     getValue: function()
20197     {
20198         return this.value;
20199     },
20200     
20201     onClick: function(e) 
20202     {
20203         e.stopPropagation();
20204         e.preventDefault();
20205         
20206         var target = e.getTarget();
20207         
20208         if(target.nodeName.toLowerCase() === 'i'){
20209             target = Roo.get(target).dom.parentNode;
20210         }
20211         
20212         var nodeName = target.nodeName;
20213         var className = target.className;
20214         var html = target.innerHTML;
20215         
20216         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20217             return;
20218         }
20219         
20220         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20221         
20222         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20223         
20224         this.hide();
20225                         
20226     },
20227     
20228     picker : function()
20229     {
20230         return this.pickerEl;
20231     },
20232     
20233     fillMonths: function()
20234     {    
20235         var i = 0;
20236         var months = this.picker().select('>.datepicker-months td', true).first();
20237         
20238         months.dom.innerHTML = '';
20239         
20240         while (i < 12) {
20241             var month = {
20242                 tag: 'span',
20243                 cls: 'month',
20244                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20245             };
20246             
20247             months.createChild(month);
20248         }
20249         
20250     },
20251     
20252     update: function()
20253     {
20254         var _this = this;
20255         
20256         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20257             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20258         }
20259         
20260         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20261             e.removeClass('active');
20262             
20263             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20264                 e.addClass('active');
20265             }
20266         })
20267     },
20268     
20269     place: function()
20270     {
20271         if(this.isInline) {
20272             return;
20273         }
20274         
20275         this.picker().removeClass(['bottom', 'top']);
20276         
20277         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20278             /*
20279              * place to the top of element!
20280              *
20281              */
20282             
20283             this.picker().addClass('top');
20284             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20285             
20286             return;
20287         }
20288         
20289         this.picker().addClass('bottom');
20290         
20291         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20292     },
20293     
20294     onFocus : function()
20295     {
20296         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20297         this.show();
20298     },
20299     
20300     onBlur : function()
20301     {
20302         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20303         
20304         var d = this.inputEl().getValue();
20305         
20306         this.setValue(d);
20307                 
20308         this.hide();
20309     },
20310     
20311     show : function()
20312     {
20313         this.picker().show();
20314         this.picker().select('>.datepicker-months', true).first().show();
20315         this.update();
20316         this.place();
20317         
20318         this.fireEvent('show', this, this.date);
20319     },
20320     
20321     hide : function()
20322     {
20323         if(this.isInline) {
20324             return;
20325         }
20326         this.picker().hide();
20327         this.fireEvent('hide', this, this.date);
20328         
20329     },
20330     
20331     onMousedown: function(e)
20332     {
20333         e.stopPropagation();
20334         e.preventDefault();
20335     },
20336     
20337     keyup: function(e)
20338     {
20339         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20340         this.update();
20341     },
20342
20343     fireKey: function(e)
20344     {
20345         if (!this.picker().isVisible()){
20346             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20347                 this.show();
20348             }
20349             return;
20350         }
20351         
20352         var dir;
20353         
20354         switch(e.keyCode){
20355             case 27: // escape
20356                 this.hide();
20357                 e.preventDefault();
20358                 break;
20359             case 37: // left
20360             case 39: // right
20361                 dir = e.keyCode == 37 ? -1 : 1;
20362                 
20363                 this.vIndex = this.vIndex + dir;
20364                 
20365                 if(this.vIndex < 0){
20366                     this.vIndex = 0;
20367                 }
20368                 
20369                 if(this.vIndex > 11){
20370                     this.vIndex = 11;
20371                 }
20372                 
20373                 if(isNaN(this.vIndex)){
20374                     this.vIndex = 0;
20375                 }
20376                 
20377                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20378                 
20379                 break;
20380             case 38: // up
20381             case 40: // down
20382                 
20383                 dir = e.keyCode == 38 ? -1 : 1;
20384                 
20385                 this.vIndex = this.vIndex + dir * 4;
20386                 
20387                 if(this.vIndex < 0){
20388                     this.vIndex = 0;
20389                 }
20390                 
20391                 if(this.vIndex > 11){
20392                     this.vIndex = 11;
20393                 }
20394                 
20395                 if(isNaN(this.vIndex)){
20396                     this.vIndex = 0;
20397                 }
20398                 
20399                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20400                 break;
20401                 
20402             case 13: // enter
20403                 
20404                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20405                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20406                 }
20407                 
20408                 this.hide();
20409                 e.preventDefault();
20410                 break;
20411             case 9: // tab
20412                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20413                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20414                 }
20415                 this.hide();
20416                 break;
20417             case 16: // shift
20418             case 17: // ctrl
20419             case 18: // alt
20420                 break;
20421             default :
20422                 this.hide();
20423                 
20424         }
20425     },
20426     
20427     remove: function() 
20428     {
20429         this.picker().remove();
20430     }
20431    
20432 });
20433
20434 Roo.apply(Roo.bootstrap.MonthField,  {
20435     
20436     content : {
20437         tag: 'tbody',
20438         cn: [
20439         {
20440             tag: 'tr',
20441             cn: [
20442             {
20443                 tag: 'td',
20444                 colspan: '7'
20445             }
20446             ]
20447         }
20448         ]
20449     },
20450     
20451     dates:{
20452         en: {
20453             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20454             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20455         }
20456     }
20457 });
20458
20459 Roo.apply(Roo.bootstrap.MonthField,  {
20460   
20461     template : {
20462         tag: 'div',
20463         cls: 'datepicker dropdown-menu roo-dynamic',
20464         cn: [
20465             {
20466                 tag: 'div',
20467                 cls: 'datepicker-months',
20468                 cn: [
20469                 {
20470                     tag: 'table',
20471                     cls: 'table-condensed',
20472                     cn:[
20473                         Roo.bootstrap.DateField.content
20474                     ]
20475                 }
20476                 ]
20477             }
20478         ]
20479     }
20480 });
20481
20482  
20483
20484  
20485  /*
20486  * - LGPL
20487  *
20488  * CheckBox
20489  * 
20490  */
20491
20492 /**
20493  * @class Roo.bootstrap.CheckBox
20494  * @extends Roo.bootstrap.Input
20495  * Bootstrap CheckBox class
20496  * 
20497  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20498  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20499  * @cfg {String} boxLabel The text that appears beside the checkbox
20500  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20501  * @cfg {Boolean} checked initnal the element
20502  * @cfg {Boolean} inline inline the element (default false)
20503  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20504  * @cfg {String} tooltip label tooltip
20505  * 
20506  * @constructor
20507  * Create a new CheckBox
20508  * @param {Object} config The config object
20509  */
20510
20511 Roo.bootstrap.CheckBox = function(config){
20512     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20513    
20514     this.addEvents({
20515         /**
20516         * @event check
20517         * Fires when the element is checked or unchecked.
20518         * @param {Roo.bootstrap.CheckBox} this This input
20519         * @param {Boolean} checked The new checked value
20520         */
20521        check : true,
20522        /**
20523         * @event click
20524         * Fires when the element is click.
20525         * @param {Roo.bootstrap.CheckBox} this This input
20526         */
20527        click : true
20528     });
20529     
20530 };
20531
20532 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20533   
20534     inputType: 'checkbox',
20535     inputValue: 1,
20536     valueOff: 0,
20537     boxLabel: false,
20538     checked: false,
20539     weight : false,
20540     inline: false,
20541     tooltip : '',
20542     
20543     getAutoCreate : function()
20544     {
20545         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20546         
20547         var id = Roo.id();
20548         
20549         var cfg = {};
20550         
20551         cfg.cls = 'form-group ' + this.inputType; //input-group
20552         
20553         if(this.inline){
20554             cfg.cls += ' ' + this.inputType + '-inline';
20555         }
20556         
20557         var input =  {
20558             tag: 'input',
20559             id : id,
20560             type : this.inputType,
20561             value : this.inputValue,
20562             cls : 'roo-' + this.inputType, //'form-box',
20563             placeholder : this.placeholder || ''
20564             
20565         };
20566         
20567         if(this.inputType != 'radio'){
20568             var hidden =  {
20569                 tag: 'input',
20570                 type : 'hidden',
20571                 cls : 'roo-hidden-value',
20572                 value : this.checked ? this.inputValue : this.valueOff
20573             };
20574         }
20575         
20576             
20577         if (this.weight) { // Validity check?
20578             cfg.cls += " " + this.inputType + "-" + this.weight;
20579         }
20580         
20581         if (this.disabled) {
20582             input.disabled=true;
20583         }
20584         
20585         if(this.checked){
20586             input.checked = this.checked;
20587         }
20588         
20589         if (this.name) {
20590             
20591             input.name = this.name;
20592             
20593             if(this.inputType != 'radio'){
20594                 hidden.name = this.name;
20595                 input.name = '_hidden_' + this.name;
20596             }
20597         }
20598         
20599         if (this.size) {
20600             input.cls += ' input-' + this.size;
20601         }
20602         
20603         var settings=this;
20604         
20605         ['xs','sm','md','lg'].map(function(size){
20606             if (settings[size]) {
20607                 cfg.cls += ' col-' + size + '-' + settings[size];
20608             }
20609         });
20610         
20611         var inputblock = input;
20612          
20613         if (this.before || this.after) {
20614             
20615             inputblock = {
20616                 cls : 'input-group',
20617                 cn :  [] 
20618             };
20619             
20620             if (this.before) {
20621                 inputblock.cn.push({
20622                     tag :'span',
20623                     cls : 'input-group-addon',
20624                     html : this.before
20625                 });
20626             }
20627             
20628             inputblock.cn.push(input);
20629             
20630             if(this.inputType != 'radio'){
20631                 inputblock.cn.push(hidden);
20632             }
20633             
20634             if (this.after) {
20635                 inputblock.cn.push({
20636                     tag :'span',
20637                     cls : 'input-group-addon',
20638                     html : this.after
20639                 });
20640             }
20641             
20642         }
20643         
20644         if (align ==='left' && this.fieldLabel.length) {
20645 //                Roo.log("left and has label");
20646             cfg.cn = [
20647                 {
20648                     tag: 'label',
20649                     'for' :  id,
20650                     cls : 'control-label',
20651                     html : this.fieldLabel
20652                 },
20653                 {
20654                     cls : "", 
20655                     cn: [
20656                         inputblock
20657                     ]
20658                 }
20659             ];
20660             
20661             if(this.labelWidth > 12){
20662                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20663             }
20664             
20665             if(this.labelWidth < 13 && this.labelmd == 0){
20666                 this.labelmd = this.labelWidth;
20667             }
20668             
20669             if(this.labellg > 0){
20670                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20671                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20672             }
20673             
20674             if(this.labelmd > 0){
20675                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20676                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20677             }
20678             
20679             if(this.labelsm > 0){
20680                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20681                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20682             }
20683             
20684             if(this.labelxs > 0){
20685                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20686                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20687             }
20688             
20689         } else if ( this.fieldLabel.length) {
20690 //                Roo.log(" label");
20691                 cfg.cn = [
20692                    
20693                     {
20694                         tag: this.boxLabel ? 'span' : 'label',
20695                         'for': id,
20696                         cls: 'control-label box-input-label',
20697                         //cls : 'input-group-addon',
20698                         html : this.fieldLabel
20699                     },
20700                     
20701                     inputblock
20702                     
20703                 ];
20704
20705         } else {
20706             
20707 //                Roo.log(" no label && no align");
20708                 cfg.cn = [  inputblock ] ;
20709                 
20710                 
20711         }
20712         
20713         if(this.boxLabel){
20714              var boxLabelCfg = {
20715                 tag: 'label',
20716                 //'for': id, // box label is handled by onclick - so no for...
20717                 cls: 'box-label',
20718                 html: this.boxLabel
20719             };
20720             
20721             if(this.tooltip){
20722                 boxLabelCfg.tooltip = this.tooltip;
20723             }
20724              
20725             cfg.cn.push(boxLabelCfg);
20726         }
20727         
20728         if(this.inputType != 'radio'){
20729             cfg.cn.push(hidden);
20730         }
20731         
20732         return cfg;
20733         
20734     },
20735     
20736     /**
20737      * return the real input element.
20738      */
20739     inputEl: function ()
20740     {
20741         return this.el.select('input.roo-' + this.inputType,true).first();
20742     },
20743     hiddenEl: function ()
20744     {
20745         return this.el.select('input.roo-hidden-value',true).first();
20746     },
20747     
20748     labelEl: function()
20749     {
20750         return this.el.select('label.control-label',true).first();
20751     },
20752     /* depricated... */
20753     
20754     label: function()
20755     {
20756         return this.labelEl();
20757     },
20758     
20759     boxLabelEl: function()
20760     {
20761         return this.el.select('label.box-label',true).first();
20762     },
20763     
20764     initEvents : function()
20765     {
20766 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20767         
20768         this.inputEl().on('click', this.onClick,  this);
20769         
20770         if (this.boxLabel) { 
20771             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20772         }
20773         
20774         this.startValue = this.getValue();
20775         
20776         if(this.groupId){
20777             Roo.bootstrap.CheckBox.register(this);
20778         }
20779     },
20780     
20781     onClick : function(e)
20782     {   
20783         if(this.fireEvent('click', this, e) !== false){
20784             this.setChecked(!this.checked);
20785         }
20786         
20787     },
20788     
20789     setChecked : function(state,suppressEvent)
20790     {
20791         this.startValue = this.getValue();
20792
20793         if(this.inputType == 'radio'){
20794             
20795             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20796                 e.dom.checked = false;
20797             });
20798             
20799             this.inputEl().dom.checked = true;
20800             
20801             this.inputEl().dom.value = this.inputValue;
20802             
20803             if(suppressEvent !== true){
20804                 this.fireEvent('check', this, true);
20805             }
20806             
20807             this.validate();
20808             
20809             return;
20810         }
20811         
20812         this.checked = state;
20813         
20814         this.inputEl().dom.checked = state;
20815         
20816         
20817         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20818         
20819         if(suppressEvent !== true){
20820             this.fireEvent('check', this, state);
20821         }
20822         
20823         this.validate();
20824     },
20825     
20826     getValue : function()
20827     {
20828         if(this.inputType == 'radio'){
20829             return this.getGroupValue();
20830         }
20831         
20832         return this.hiddenEl().dom.value;
20833         
20834     },
20835     
20836     getGroupValue : function()
20837     {
20838         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20839             return '';
20840         }
20841         
20842         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20843     },
20844     
20845     setValue : function(v,suppressEvent)
20846     {
20847         if(this.inputType == 'radio'){
20848             this.setGroupValue(v, suppressEvent);
20849             return;
20850         }
20851         
20852         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20853         
20854         this.validate();
20855     },
20856     
20857     setGroupValue : function(v, suppressEvent)
20858     {
20859         this.startValue = this.getValue();
20860         
20861         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20862             e.dom.checked = false;
20863             
20864             if(e.dom.value == v){
20865                 e.dom.checked = true;
20866             }
20867         });
20868         
20869         if(suppressEvent !== true){
20870             this.fireEvent('check', this, true);
20871         }
20872
20873         this.validate();
20874         
20875         return;
20876     },
20877     
20878     validate : function()
20879     {
20880         if(this.getVisibilityEl().hasClass('hidden')){
20881             return true;
20882         }
20883         
20884         if(
20885                 this.disabled || 
20886                 (this.inputType == 'radio' && this.validateRadio()) ||
20887                 (this.inputType == 'checkbox' && this.validateCheckbox())
20888         ){
20889             this.markValid();
20890             return true;
20891         }
20892         
20893         this.markInvalid();
20894         return false;
20895     },
20896     
20897     validateRadio : function()
20898     {
20899         if(this.getVisibilityEl().hasClass('hidden')){
20900             return true;
20901         }
20902         
20903         if(this.allowBlank){
20904             return true;
20905         }
20906         
20907         var valid = false;
20908         
20909         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20910             if(!e.dom.checked){
20911                 return;
20912             }
20913             
20914             valid = true;
20915             
20916             return false;
20917         });
20918         
20919         return valid;
20920     },
20921     
20922     validateCheckbox : function()
20923     {
20924         if(!this.groupId){
20925             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20926             //return (this.getValue() == this.inputValue) ? true : false;
20927         }
20928         
20929         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20930         
20931         if(!group){
20932             return false;
20933         }
20934         
20935         var r = false;
20936         
20937         for(var i in group){
20938             if(group[i].el.isVisible(true)){
20939                 r = false;
20940                 break;
20941             }
20942             
20943             r = true;
20944         }
20945         
20946         for(var i in group){
20947             if(r){
20948                 break;
20949             }
20950             
20951             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20952         }
20953         
20954         return r;
20955     },
20956     
20957     /**
20958      * Mark this field as valid
20959      */
20960     markValid : function()
20961     {
20962         var _this = this;
20963         
20964         this.fireEvent('valid', this);
20965         
20966         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20967         
20968         if(this.groupId){
20969             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20970         }
20971         
20972         if(label){
20973             label.markValid();
20974         }
20975
20976         if(this.inputType == 'radio'){
20977             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20978                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20979                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20980             });
20981             
20982             return;
20983         }
20984
20985         if(!this.groupId){
20986             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20987             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20988             return;
20989         }
20990         
20991         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20992         
20993         if(!group){
20994             return;
20995         }
20996         
20997         for(var i in group){
20998             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20999             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21000         }
21001     },
21002     
21003      /**
21004      * Mark this field as invalid
21005      * @param {String} msg The validation message
21006      */
21007     markInvalid : function(msg)
21008     {
21009         if(this.allowBlank){
21010             return;
21011         }
21012         
21013         var _this = this;
21014         
21015         this.fireEvent('invalid', this, msg);
21016         
21017         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21018         
21019         if(this.groupId){
21020             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21021         }
21022         
21023         if(label){
21024             label.markInvalid();
21025         }
21026             
21027         if(this.inputType == 'radio'){
21028             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21029                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21030                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21031             });
21032             
21033             return;
21034         }
21035         
21036         if(!this.groupId){
21037             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21038             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21039             return;
21040         }
21041         
21042         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21043         
21044         if(!group){
21045             return;
21046         }
21047         
21048         for(var i in group){
21049             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21050             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21051         }
21052         
21053     },
21054     
21055     clearInvalid : function()
21056     {
21057         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21058         
21059         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21060         
21061         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21062         
21063         if (label && label.iconEl) {
21064             label.iconEl.removeClass(label.validClass);
21065             label.iconEl.removeClass(label.invalidClass);
21066         }
21067     },
21068     
21069     disable : function()
21070     {
21071         if(this.inputType != 'radio'){
21072             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21073             return;
21074         }
21075         
21076         var _this = this;
21077         
21078         if(this.rendered){
21079             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21080                 _this.getActionEl().addClass(this.disabledClass);
21081                 e.dom.disabled = true;
21082             });
21083         }
21084         
21085         this.disabled = true;
21086         this.fireEvent("disable", this);
21087         return this;
21088     },
21089
21090     enable : function()
21091     {
21092         if(this.inputType != 'radio'){
21093             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21094             return;
21095         }
21096         
21097         var _this = this;
21098         
21099         if(this.rendered){
21100             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21101                 _this.getActionEl().removeClass(this.disabledClass);
21102                 e.dom.disabled = false;
21103             });
21104         }
21105         
21106         this.disabled = false;
21107         this.fireEvent("enable", this);
21108         return this;
21109     },
21110     
21111     setBoxLabel : function(v)
21112     {
21113         this.boxLabel = v;
21114         
21115         if(this.rendered){
21116             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21117         }
21118     }
21119
21120 });
21121
21122 Roo.apply(Roo.bootstrap.CheckBox, {
21123     
21124     groups: {},
21125     
21126      /**
21127     * register a CheckBox Group
21128     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21129     */
21130     register : function(checkbox)
21131     {
21132         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21133             this.groups[checkbox.groupId] = {};
21134         }
21135         
21136         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21137             return;
21138         }
21139         
21140         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21141         
21142     },
21143     /**
21144     * fetch a CheckBox Group based on the group ID
21145     * @param {string} the group ID
21146     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21147     */
21148     get: function(groupId) {
21149         if (typeof(this.groups[groupId]) == 'undefined') {
21150             return false;
21151         }
21152         
21153         return this.groups[groupId] ;
21154     }
21155     
21156     
21157 });
21158 /*
21159  * - LGPL
21160  *
21161  * RadioItem
21162  * 
21163  */
21164
21165 /**
21166  * @class Roo.bootstrap.Radio
21167  * @extends Roo.bootstrap.Component
21168  * Bootstrap Radio class
21169  * @cfg {String} boxLabel - the label associated
21170  * @cfg {String} value - the value of radio
21171  * 
21172  * @constructor
21173  * Create a new Radio
21174  * @param {Object} config The config object
21175  */
21176 Roo.bootstrap.Radio = function(config){
21177     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21178     
21179 };
21180
21181 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21182     
21183     boxLabel : '',
21184     
21185     value : '',
21186     
21187     getAutoCreate : function()
21188     {
21189         var cfg = {
21190             tag : 'div',
21191             cls : 'form-group radio',
21192             cn : [
21193                 {
21194                     tag : 'label',
21195                     cls : 'box-label',
21196                     html : this.boxLabel
21197                 }
21198             ]
21199         };
21200         
21201         return cfg;
21202     },
21203     
21204     initEvents : function() 
21205     {
21206         this.parent().register(this);
21207         
21208         this.el.on('click', this.onClick, this);
21209         
21210     },
21211     
21212     onClick : function(e)
21213     {
21214         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21215             this.setChecked(true);
21216         }
21217     },
21218     
21219     setChecked : function(state, suppressEvent)
21220     {
21221         this.parent().setValue(this.value, suppressEvent);
21222         
21223     },
21224     
21225     setBoxLabel : function(v)
21226     {
21227         this.boxLabel = v;
21228         
21229         if(this.rendered){
21230             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21231         }
21232     }
21233     
21234 });
21235  
21236
21237  /*
21238  * - LGPL
21239  *
21240  * Input
21241  * 
21242  */
21243
21244 /**
21245  * @class Roo.bootstrap.SecurePass
21246  * @extends Roo.bootstrap.Input
21247  * Bootstrap SecurePass class
21248  *
21249  * 
21250  * @constructor
21251  * Create a new SecurePass
21252  * @param {Object} config The config object
21253  */
21254  
21255 Roo.bootstrap.SecurePass = function (config) {
21256     // these go here, so the translation tool can replace them..
21257     this.errors = {
21258         PwdEmpty: "Please type a password, and then retype it to confirm.",
21259         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21260         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21261         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21262         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21263         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21264         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21265         TooWeak: "Your password is Too Weak."
21266     },
21267     this.meterLabel = "Password strength:";
21268     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21269     this.meterClass = [
21270         "roo-password-meter-tooweak", 
21271         "roo-password-meter-weak", 
21272         "roo-password-meter-medium", 
21273         "roo-password-meter-strong", 
21274         "roo-password-meter-grey"
21275     ];
21276     
21277     this.errors = {};
21278     
21279     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21280 }
21281
21282 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21283     /**
21284      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21285      * {
21286      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21287      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21288      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21289      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21290      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21291      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21292      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21293      * })
21294      */
21295     // private
21296     
21297     meterWidth: 300,
21298     errorMsg :'',    
21299     errors: false,
21300     imageRoot: '/',
21301     /**
21302      * @cfg {String/Object} Label for the strength meter (defaults to
21303      * 'Password strength:')
21304      */
21305     // private
21306     meterLabel: '',
21307     /**
21308      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21309      * ['Weak', 'Medium', 'Strong'])
21310      */
21311     // private    
21312     pwdStrengths: false,    
21313     // private
21314     strength: 0,
21315     // private
21316     _lastPwd: null,
21317     // private
21318     kCapitalLetter: 0,
21319     kSmallLetter: 1,
21320     kDigit: 2,
21321     kPunctuation: 3,
21322     
21323     insecure: false,
21324     // private
21325     initEvents: function ()
21326     {
21327         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21328
21329         if (this.el.is('input[type=password]') && Roo.isSafari) {
21330             this.el.on('keydown', this.SafariOnKeyDown, this);
21331         }
21332
21333         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21334     },
21335     // private
21336     onRender: function (ct, position)
21337     {
21338         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21339         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21340         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21341
21342         this.trigger.createChild({
21343                    cn: [
21344                     {
21345                     //id: 'PwdMeter',
21346                     tag: 'div',
21347                     cls: 'roo-password-meter-grey col-xs-12',
21348                     style: {
21349                         //width: 0,
21350                         //width: this.meterWidth + 'px'                                                
21351                         }
21352                     },
21353                     {                            
21354                          cls: 'roo-password-meter-text'                          
21355                     }
21356                 ]            
21357         });
21358
21359          
21360         if (this.hideTrigger) {
21361             this.trigger.setDisplayed(false);
21362         }
21363         this.setSize(this.width || '', this.height || '');
21364     },
21365     // private
21366     onDestroy: function ()
21367     {
21368         if (this.trigger) {
21369             this.trigger.removeAllListeners();
21370             this.trigger.remove();
21371         }
21372         if (this.wrap) {
21373             this.wrap.remove();
21374         }
21375         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21376     },
21377     // private
21378     checkStrength: function ()
21379     {
21380         var pwd = this.inputEl().getValue();
21381         if (pwd == this._lastPwd) {
21382             return;
21383         }
21384
21385         var strength;
21386         if (this.ClientSideStrongPassword(pwd)) {
21387             strength = 3;
21388         } else if (this.ClientSideMediumPassword(pwd)) {
21389             strength = 2;
21390         } else if (this.ClientSideWeakPassword(pwd)) {
21391             strength = 1;
21392         } else {
21393             strength = 0;
21394         }
21395         
21396         Roo.log('strength1: ' + strength);
21397         
21398         //var pm = this.trigger.child('div/div/div').dom;
21399         var pm = this.trigger.child('div/div');
21400         pm.removeClass(this.meterClass);
21401         pm.addClass(this.meterClass[strength]);
21402                 
21403         
21404         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21405                 
21406         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21407         
21408         this._lastPwd = pwd;
21409     },
21410     reset: function ()
21411     {
21412         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21413         
21414         this._lastPwd = '';
21415         
21416         var pm = this.trigger.child('div/div');
21417         pm.removeClass(this.meterClass);
21418         pm.addClass('roo-password-meter-grey');        
21419         
21420         
21421         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21422         
21423         pt.innerHTML = '';
21424         this.inputEl().dom.type='password';
21425     },
21426     // private
21427     validateValue: function (value)
21428     {
21429         
21430         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21431             return false;
21432         }
21433         if (value.length == 0) {
21434             if (this.allowBlank) {
21435                 this.clearInvalid();
21436                 return true;
21437             }
21438
21439             this.markInvalid(this.errors.PwdEmpty);
21440             this.errorMsg = this.errors.PwdEmpty;
21441             return false;
21442         }
21443         
21444         if(this.insecure){
21445             return true;
21446         }
21447         
21448         if ('[\x21-\x7e]*'.match(value)) {
21449             this.markInvalid(this.errors.PwdBadChar);
21450             this.errorMsg = this.errors.PwdBadChar;
21451             return false;
21452         }
21453         if (value.length < 6) {
21454             this.markInvalid(this.errors.PwdShort);
21455             this.errorMsg = this.errors.PwdShort;
21456             return false;
21457         }
21458         if (value.length > 16) {
21459             this.markInvalid(this.errors.PwdLong);
21460             this.errorMsg = this.errors.PwdLong;
21461             return false;
21462         }
21463         var strength;
21464         if (this.ClientSideStrongPassword(value)) {
21465             strength = 3;
21466         } else if (this.ClientSideMediumPassword(value)) {
21467             strength = 2;
21468         } else if (this.ClientSideWeakPassword(value)) {
21469             strength = 1;
21470         } else {
21471             strength = 0;
21472         }
21473
21474         
21475         if (strength < 2) {
21476             //this.markInvalid(this.errors.TooWeak);
21477             this.errorMsg = this.errors.TooWeak;
21478             //return false;
21479         }
21480         
21481         
21482         console.log('strength2: ' + strength);
21483         
21484         //var pm = this.trigger.child('div/div/div').dom;
21485         
21486         var pm = this.trigger.child('div/div');
21487         pm.removeClass(this.meterClass);
21488         pm.addClass(this.meterClass[strength]);
21489                 
21490         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21491                 
21492         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21493         
21494         this.errorMsg = ''; 
21495         return true;
21496     },
21497     // private
21498     CharacterSetChecks: function (type)
21499     {
21500         this.type = type;
21501         this.fResult = false;
21502     },
21503     // private
21504     isctype: function (character, type)
21505     {
21506         switch (type) {  
21507             case this.kCapitalLetter:
21508                 if (character >= 'A' && character <= 'Z') {
21509                     return true;
21510                 }
21511                 break;
21512             
21513             case this.kSmallLetter:
21514                 if (character >= 'a' && character <= 'z') {
21515                     return true;
21516                 }
21517                 break;
21518             
21519             case this.kDigit:
21520                 if (character >= '0' && character <= '9') {
21521                     return true;
21522                 }
21523                 break;
21524             
21525             case this.kPunctuation:
21526                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21527                     return true;
21528                 }
21529                 break;
21530             
21531             default:
21532                 return false;
21533         }
21534
21535     },
21536     // private
21537     IsLongEnough: function (pwd, size)
21538     {
21539         return !(pwd == null || isNaN(size) || pwd.length < size);
21540     },
21541     // private
21542     SpansEnoughCharacterSets: function (word, nb)
21543     {
21544         if (!this.IsLongEnough(word, nb))
21545         {
21546             return false;
21547         }
21548
21549         var characterSetChecks = new Array(
21550             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21551             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21552         );
21553         
21554         for (var index = 0; index < word.length; ++index) {
21555             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21556                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21557                     characterSetChecks[nCharSet].fResult = true;
21558                     break;
21559                 }
21560             }
21561         }
21562
21563         var nCharSets = 0;
21564         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21565             if (characterSetChecks[nCharSet].fResult) {
21566                 ++nCharSets;
21567             }
21568         }
21569
21570         if (nCharSets < nb) {
21571             return false;
21572         }
21573         return true;
21574     },
21575     // private
21576     ClientSideStrongPassword: function (pwd)
21577     {
21578         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21579     },
21580     // private
21581     ClientSideMediumPassword: function (pwd)
21582     {
21583         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21584     },
21585     // private
21586     ClientSideWeakPassword: function (pwd)
21587     {
21588         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21589     }
21590           
21591 })//<script type="text/javascript">
21592
21593 /*
21594  * Based  Ext JS Library 1.1.1
21595  * Copyright(c) 2006-2007, Ext JS, LLC.
21596  * LGPL
21597  *
21598  */
21599  
21600 /**
21601  * @class Roo.HtmlEditorCore
21602  * @extends Roo.Component
21603  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21604  *
21605  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21606  */
21607
21608 Roo.HtmlEditorCore = function(config){
21609     
21610     
21611     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21612     
21613     
21614     this.addEvents({
21615         /**
21616          * @event initialize
21617          * Fires when the editor is fully initialized (including the iframe)
21618          * @param {Roo.HtmlEditorCore} this
21619          */
21620         initialize: true,
21621         /**
21622          * @event activate
21623          * Fires when the editor is first receives the focus. Any insertion must wait
21624          * until after this event.
21625          * @param {Roo.HtmlEditorCore} this
21626          */
21627         activate: true,
21628          /**
21629          * @event beforesync
21630          * Fires before the textarea is updated with content from the editor iframe. Return false
21631          * to cancel the sync.
21632          * @param {Roo.HtmlEditorCore} this
21633          * @param {String} html
21634          */
21635         beforesync: true,
21636          /**
21637          * @event beforepush
21638          * Fires before the iframe editor is updated with content from the textarea. Return false
21639          * to cancel the push.
21640          * @param {Roo.HtmlEditorCore} this
21641          * @param {String} html
21642          */
21643         beforepush: true,
21644          /**
21645          * @event sync
21646          * Fires when the textarea is updated with content from the editor iframe.
21647          * @param {Roo.HtmlEditorCore} this
21648          * @param {String} html
21649          */
21650         sync: true,
21651          /**
21652          * @event push
21653          * Fires when the iframe editor is updated with content from the textarea.
21654          * @param {Roo.HtmlEditorCore} this
21655          * @param {String} html
21656          */
21657         push: true,
21658         
21659         /**
21660          * @event editorevent
21661          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21662          * @param {Roo.HtmlEditorCore} this
21663          */
21664         editorevent: true
21665         
21666     });
21667     
21668     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21669     
21670     // defaults : white / black...
21671     this.applyBlacklists();
21672     
21673     
21674     
21675 };
21676
21677
21678 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21679
21680
21681      /**
21682      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21683      */
21684     
21685     owner : false,
21686     
21687      /**
21688      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21689      *                        Roo.resizable.
21690      */
21691     resizable : false,
21692      /**
21693      * @cfg {Number} height (in pixels)
21694      */   
21695     height: 300,
21696    /**
21697      * @cfg {Number} width (in pixels)
21698      */   
21699     width: 500,
21700     
21701     /**
21702      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21703      * 
21704      */
21705     stylesheets: false,
21706     
21707     // id of frame..
21708     frameId: false,
21709     
21710     // private properties
21711     validationEvent : false,
21712     deferHeight: true,
21713     initialized : false,
21714     activated : false,
21715     sourceEditMode : false,
21716     onFocus : Roo.emptyFn,
21717     iframePad:3,
21718     hideMode:'offsets',
21719     
21720     clearUp: true,
21721     
21722     // blacklist + whitelisted elements..
21723     black: false,
21724     white: false,
21725      
21726     bodyCls : '',
21727
21728     /**
21729      * Protected method that will not generally be called directly. It
21730      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21731      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21732      */
21733     getDocMarkup : function(){
21734         // body styles..
21735         var st = '';
21736         
21737         // inherit styels from page...?? 
21738         if (this.stylesheets === false) {
21739             
21740             Roo.get(document.head).select('style').each(function(node) {
21741                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21742             });
21743             
21744             Roo.get(document.head).select('link').each(function(node) { 
21745                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21746             });
21747             
21748         } else if (!this.stylesheets.length) {
21749                 // simple..
21750                 st = '<style type="text/css">' +
21751                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21752                    '</style>';
21753         } else { 
21754             st = '<style type="text/css">' +
21755                     this.stylesheets +
21756                 '</style>';
21757         }
21758         
21759         st +=  '<style type="text/css">' +
21760             'IMG { cursor: pointer } ' +
21761         '</style>';
21762
21763         var cls = 'roo-htmleditor-body';
21764         
21765         if(this.bodyCls.length){
21766             cls += ' ' + this.bodyCls;
21767         }
21768         
21769         return '<html><head>' + st  +
21770             //<style type="text/css">' +
21771             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21772             //'</style>' +
21773             ' </head><body class="' +  cls + '"></body></html>';
21774     },
21775
21776     // private
21777     onRender : function(ct, position)
21778     {
21779         var _t = this;
21780         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21781         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21782         
21783         
21784         this.el.dom.style.border = '0 none';
21785         this.el.dom.setAttribute('tabIndex', -1);
21786         this.el.addClass('x-hidden hide');
21787         
21788         
21789         
21790         if(Roo.isIE){ // fix IE 1px bogus margin
21791             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21792         }
21793        
21794         
21795         this.frameId = Roo.id();
21796         
21797          
21798         
21799         var iframe = this.owner.wrap.createChild({
21800             tag: 'iframe',
21801             cls: 'form-control', // bootstrap..
21802             id: this.frameId,
21803             name: this.frameId,
21804             frameBorder : 'no',
21805             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21806         }, this.el
21807         );
21808         
21809         
21810         this.iframe = iframe.dom;
21811
21812          this.assignDocWin();
21813         
21814         this.doc.designMode = 'on';
21815        
21816         this.doc.open();
21817         this.doc.write(this.getDocMarkup());
21818         this.doc.close();
21819
21820         
21821         var task = { // must defer to wait for browser to be ready
21822             run : function(){
21823                 //console.log("run task?" + this.doc.readyState);
21824                 this.assignDocWin();
21825                 if(this.doc.body || this.doc.readyState == 'complete'){
21826                     try {
21827                         this.doc.designMode="on";
21828                     } catch (e) {
21829                         return;
21830                     }
21831                     Roo.TaskMgr.stop(task);
21832                     this.initEditor.defer(10, this);
21833                 }
21834             },
21835             interval : 10,
21836             duration: 10000,
21837             scope: this
21838         };
21839         Roo.TaskMgr.start(task);
21840
21841     },
21842
21843     // private
21844     onResize : function(w, h)
21845     {
21846          Roo.log('resize: ' +w + ',' + h );
21847         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21848         if(!this.iframe){
21849             return;
21850         }
21851         if(typeof w == 'number'){
21852             
21853             this.iframe.style.width = w + 'px';
21854         }
21855         if(typeof h == 'number'){
21856             
21857             this.iframe.style.height = h + 'px';
21858             if(this.doc){
21859                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21860             }
21861         }
21862         
21863     },
21864
21865     /**
21866      * Toggles the editor between standard and source edit mode.
21867      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21868      */
21869     toggleSourceEdit : function(sourceEditMode){
21870         
21871         this.sourceEditMode = sourceEditMode === true;
21872         
21873         if(this.sourceEditMode){
21874  
21875             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21876             
21877         }else{
21878             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21879             //this.iframe.className = '';
21880             this.deferFocus();
21881         }
21882         //this.setSize(this.owner.wrap.getSize());
21883         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21884     },
21885
21886     
21887   
21888
21889     /**
21890      * Protected method that will not generally be called directly. If you need/want
21891      * custom HTML cleanup, this is the method you should override.
21892      * @param {String} html The HTML to be cleaned
21893      * return {String} The cleaned HTML
21894      */
21895     cleanHtml : function(html){
21896         html = String(html);
21897         if(html.length > 5){
21898             if(Roo.isSafari){ // strip safari nonsense
21899                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21900             }
21901         }
21902         if(html == '&nbsp;'){
21903             html = '';
21904         }
21905         return html;
21906     },
21907
21908     /**
21909      * HTML Editor -> Textarea
21910      * Protected method that will not generally be called directly. Syncs the contents
21911      * of the editor iframe with the textarea.
21912      */
21913     syncValue : function(){
21914         if(this.initialized){
21915             var bd = (this.doc.body || this.doc.documentElement);
21916             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21917             var html = bd.innerHTML;
21918             if(Roo.isSafari){
21919                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21920                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21921                 if(m && m[1]){
21922                     html = '<div style="'+m[0]+'">' + html + '</div>';
21923                 }
21924             }
21925             html = this.cleanHtml(html);
21926             // fix up the special chars.. normaly like back quotes in word...
21927             // however we do not want to do this with chinese..
21928             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21929                 var cc = b.charCodeAt();
21930                 if (
21931                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21932                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21933                     (cc >= 0xf900 && cc < 0xfb00 )
21934                 ) {
21935                         return b;
21936                 }
21937                 return "&#"+cc+";" 
21938             });
21939             if(this.owner.fireEvent('beforesync', this, html) !== false){
21940                 this.el.dom.value = html;
21941                 this.owner.fireEvent('sync', this, html);
21942             }
21943         }
21944     },
21945
21946     /**
21947      * Protected method that will not generally be called directly. Pushes the value of the textarea
21948      * into the iframe editor.
21949      */
21950     pushValue : function(){
21951         if(this.initialized){
21952             var v = this.el.dom.value.trim();
21953             
21954 //            if(v.length < 1){
21955 //                v = '&#160;';
21956 //            }
21957             
21958             if(this.owner.fireEvent('beforepush', this, v) !== false){
21959                 var d = (this.doc.body || this.doc.documentElement);
21960                 d.innerHTML = v;
21961                 this.cleanUpPaste();
21962                 this.el.dom.value = d.innerHTML;
21963                 this.owner.fireEvent('push', this, v);
21964             }
21965         }
21966     },
21967
21968     // private
21969     deferFocus : function(){
21970         this.focus.defer(10, this);
21971     },
21972
21973     // doc'ed in Field
21974     focus : function(){
21975         if(this.win && !this.sourceEditMode){
21976             this.win.focus();
21977         }else{
21978             this.el.focus();
21979         }
21980     },
21981     
21982     assignDocWin: function()
21983     {
21984         var iframe = this.iframe;
21985         
21986          if(Roo.isIE){
21987             this.doc = iframe.contentWindow.document;
21988             this.win = iframe.contentWindow;
21989         } else {
21990 //            if (!Roo.get(this.frameId)) {
21991 //                return;
21992 //            }
21993 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21994 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21995             
21996             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21997                 return;
21998             }
21999             
22000             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22001             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22002         }
22003     },
22004     
22005     // private
22006     initEditor : function(){
22007         //console.log("INIT EDITOR");
22008         this.assignDocWin();
22009         
22010         
22011         
22012         this.doc.designMode="on";
22013         this.doc.open();
22014         this.doc.write(this.getDocMarkup());
22015         this.doc.close();
22016         
22017         var dbody = (this.doc.body || this.doc.documentElement);
22018         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22019         // this copies styles from the containing element into thsi one..
22020         // not sure why we need all of this..
22021         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22022         
22023         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22024         //ss['background-attachment'] = 'fixed'; // w3c
22025         dbody.bgProperties = 'fixed'; // ie
22026         //Roo.DomHelper.applyStyles(dbody, ss);
22027         Roo.EventManager.on(this.doc, {
22028             //'mousedown': this.onEditorEvent,
22029             'mouseup': this.onEditorEvent,
22030             'dblclick': this.onEditorEvent,
22031             'click': this.onEditorEvent,
22032             'keyup': this.onEditorEvent,
22033             buffer:100,
22034             scope: this
22035         });
22036         if(Roo.isGecko){
22037             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22038         }
22039         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22040             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22041         }
22042         this.initialized = true;
22043
22044         this.owner.fireEvent('initialize', this);
22045         this.pushValue();
22046     },
22047
22048     // private
22049     onDestroy : function(){
22050         
22051         
22052         
22053         if(this.rendered){
22054             
22055             //for (var i =0; i < this.toolbars.length;i++) {
22056             //    // fixme - ask toolbars for heights?
22057             //    this.toolbars[i].onDestroy();
22058            // }
22059             
22060             //this.wrap.dom.innerHTML = '';
22061             //this.wrap.remove();
22062         }
22063     },
22064
22065     // private
22066     onFirstFocus : function(){
22067         
22068         this.assignDocWin();
22069         
22070         
22071         this.activated = true;
22072          
22073     
22074         if(Roo.isGecko){ // prevent silly gecko errors
22075             this.win.focus();
22076             var s = this.win.getSelection();
22077             if(!s.focusNode || s.focusNode.nodeType != 3){
22078                 var r = s.getRangeAt(0);
22079                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22080                 r.collapse(true);
22081                 this.deferFocus();
22082             }
22083             try{
22084                 this.execCmd('useCSS', true);
22085                 this.execCmd('styleWithCSS', false);
22086             }catch(e){}
22087         }
22088         this.owner.fireEvent('activate', this);
22089     },
22090
22091     // private
22092     adjustFont: function(btn){
22093         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22094         //if(Roo.isSafari){ // safari
22095         //    adjust *= 2;
22096        // }
22097         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22098         if(Roo.isSafari){ // safari
22099             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22100             v =  (v < 10) ? 10 : v;
22101             v =  (v > 48) ? 48 : v;
22102             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22103             
22104         }
22105         
22106         
22107         v = Math.max(1, v+adjust);
22108         
22109         this.execCmd('FontSize', v  );
22110     },
22111
22112     onEditorEvent : function(e)
22113     {
22114         this.owner.fireEvent('editorevent', this, e);
22115       //  this.updateToolbar();
22116         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22117     },
22118
22119     insertTag : function(tg)
22120     {
22121         // could be a bit smarter... -> wrap the current selected tRoo..
22122         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22123             
22124             range = this.createRange(this.getSelection());
22125             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22126             wrappingNode.appendChild(range.extractContents());
22127             range.insertNode(wrappingNode);
22128
22129             return;
22130             
22131             
22132             
22133         }
22134         this.execCmd("formatblock",   tg);
22135         
22136     },
22137     
22138     insertText : function(txt)
22139     {
22140         
22141         
22142         var range = this.createRange();
22143         range.deleteContents();
22144                //alert(Sender.getAttribute('label'));
22145                
22146         range.insertNode(this.doc.createTextNode(txt));
22147     } ,
22148     
22149      
22150
22151     /**
22152      * Executes a Midas editor command on the editor document and performs necessary focus and
22153      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22154      * @param {String} cmd The Midas command
22155      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22156      */
22157     relayCmd : function(cmd, value){
22158         this.win.focus();
22159         this.execCmd(cmd, value);
22160         this.owner.fireEvent('editorevent', this);
22161         //this.updateToolbar();
22162         this.owner.deferFocus();
22163     },
22164
22165     /**
22166      * Executes a Midas editor command directly on the editor document.
22167      * For visual commands, you should use {@link #relayCmd} instead.
22168      * <b>This should only be called after the editor is initialized.</b>
22169      * @param {String} cmd The Midas command
22170      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22171      */
22172     execCmd : function(cmd, value){
22173         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22174         this.syncValue();
22175     },
22176  
22177  
22178    
22179     /**
22180      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22181      * to insert tRoo.
22182      * @param {String} text | dom node.. 
22183      */
22184     insertAtCursor : function(text)
22185     {
22186         
22187         if(!this.activated){
22188             return;
22189         }
22190         /*
22191         if(Roo.isIE){
22192             this.win.focus();
22193             var r = this.doc.selection.createRange();
22194             if(r){
22195                 r.collapse(true);
22196                 r.pasteHTML(text);
22197                 this.syncValue();
22198                 this.deferFocus();
22199             
22200             }
22201             return;
22202         }
22203         */
22204         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22205             this.win.focus();
22206             
22207             
22208             // from jquery ui (MIT licenced)
22209             var range, node;
22210             var win = this.win;
22211             
22212             if (win.getSelection && win.getSelection().getRangeAt) {
22213                 range = win.getSelection().getRangeAt(0);
22214                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22215                 range.insertNode(node);
22216             } else if (win.document.selection && win.document.selection.createRange) {
22217                 // no firefox support
22218                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22219                 win.document.selection.createRange().pasteHTML(txt);
22220             } else {
22221                 // no firefox support
22222                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22223                 this.execCmd('InsertHTML', txt);
22224             } 
22225             
22226             this.syncValue();
22227             
22228             this.deferFocus();
22229         }
22230     },
22231  // private
22232     mozKeyPress : function(e){
22233         if(e.ctrlKey){
22234             var c = e.getCharCode(), cmd;
22235           
22236             if(c > 0){
22237                 c = String.fromCharCode(c).toLowerCase();
22238                 switch(c){
22239                     case 'b':
22240                         cmd = 'bold';
22241                         break;
22242                     case 'i':
22243                         cmd = 'italic';
22244                         break;
22245                     
22246                     case 'u':
22247                         cmd = 'underline';
22248                         break;
22249                     
22250                     case 'v':
22251                         this.cleanUpPaste.defer(100, this);
22252                         return;
22253                         
22254                 }
22255                 if(cmd){
22256                     this.win.focus();
22257                     this.execCmd(cmd);
22258                     this.deferFocus();
22259                     e.preventDefault();
22260                 }
22261                 
22262             }
22263         }
22264     },
22265
22266     // private
22267     fixKeys : function(){ // load time branching for fastest keydown performance
22268         if(Roo.isIE){
22269             return function(e){
22270                 var k = e.getKey(), r;
22271                 if(k == e.TAB){
22272                     e.stopEvent();
22273                     r = this.doc.selection.createRange();
22274                     if(r){
22275                         r.collapse(true);
22276                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22277                         this.deferFocus();
22278                     }
22279                     return;
22280                 }
22281                 
22282                 if(k == e.ENTER){
22283                     r = this.doc.selection.createRange();
22284                     if(r){
22285                         var target = r.parentElement();
22286                         if(!target || target.tagName.toLowerCase() != 'li'){
22287                             e.stopEvent();
22288                             r.pasteHTML('<br />');
22289                             r.collapse(false);
22290                             r.select();
22291                         }
22292                     }
22293                 }
22294                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22295                     this.cleanUpPaste.defer(100, this);
22296                     return;
22297                 }
22298                 
22299                 
22300             };
22301         }else if(Roo.isOpera){
22302             return function(e){
22303                 var k = e.getKey();
22304                 if(k == e.TAB){
22305                     e.stopEvent();
22306                     this.win.focus();
22307                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22308                     this.deferFocus();
22309                 }
22310                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22311                     this.cleanUpPaste.defer(100, this);
22312                     return;
22313                 }
22314                 
22315             };
22316         }else if(Roo.isSafari){
22317             return function(e){
22318                 var k = e.getKey();
22319                 
22320                 if(k == e.TAB){
22321                     e.stopEvent();
22322                     this.execCmd('InsertText','\t');
22323                     this.deferFocus();
22324                     return;
22325                 }
22326                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22327                     this.cleanUpPaste.defer(100, this);
22328                     return;
22329                 }
22330                 
22331              };
22332         }
22333     }(),
22334     
22335     getAllAncestors: function()
22336     {
22337         var p = this.getSelectedNode();
22338         var a = [];
22339         if (!p) {
22340             a.push(p); // push blank onto stack..
22341             p = this.getParentElement();
22342         }
22343         
22344         
22345         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22346             a.push(p);
22347             p = p.parentNode;
22348         }
22349         a.push(this.doc.body);
22350         return a;
22351     },
22352     lastSel : false,
22353     lastSelNode : false,
22354     
22355     
22356     getSelection : function() 
22357     {
22358         this.assignDocWin();
22359         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22360     },
22361     
22362     getSelectedNode: function() 
22363     {
22364         // this may only work on Gecko!!!
22365         
22366         // should we cache this!!!!
22367         
22368         
22369         
22370          
22371         var range = this.createRange(this.getSelection()).cloneRange();
22372         
22373         if (Roo.isIE) {
22374             var parent = range.parentElement();
22375             while (true) {
22376                 var testRange = range.duplicate();
22377                 testRange.moveToElementText(parent);
22378                 if (testRange.inRange(range)) {
22379                     break;
22380                 }
22381                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22382                     break;
22383                 }
22384                 parent = parent.parentElement;
22385             }
22386             return parent;
22387         }
22388         
22389         // is ancestor a text element.
22390         var ac =  range.commonAncestorContainer;
22391         if (ac.nodeType == 3) {
22392             ac = ac.parentNode;
22393         }
22394         
22395         var ar = ac.childNodes;
22396          
22397         var nodes = [];
22398         var other_nodes = [];
22399         var has_other_nodes = false;
22400         for (var i=0;i<ar.length;i++) {
22401             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22402                 continue;
22403             }
22404             // fullly contained node.
22405             
22406             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22407                 nodes.push(ar[i]);
22408                 continue;
22409             }
22410             
22411             // probably selected..
22412             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22413                 other_nodes.push(ar[i]);
22414                 continue;
22415             }
22416             // outer..
22417             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22418                 continue;
22419             }
22420             
22421             
22422             has_other_nodes = true;
22423         }
22424         if (!nodes.length && other_nodes.length) {
22425             nodes= other_nodes;
22426         }
22427         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22428             return false;
22429         }
22430         
22431         return nodes[0];
22432     },
22433     createRange: function(sel)
22434     {
22435         // this has strange effects when using with 
22436         // top toolbar - not sure if it's a great idea.
22437         //this.editor.contentWindow.focus();
22438         if (typeof sel != "undefined") {
22439             try {
22440                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22441             } catch(e) {
22442                 return this.doc.createRange();
22443             }
22444         } else {
22445             return this.doc.createRange();
22446         }
22447     },
22448     getParentElement: function()
22449     {
22450         
22451         this.assignDocWin();
22452         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22453         
22454         var range = this.createRange(sel);
22455          
22456         try {
22457             var p = range.commonAncestorContainer;
22458             while (p.nodeType == 3) { // text node
22459                 p = p.parentNode;
22460             }
22461             return p;
22462         } catch (e) {
22463             return null;
22464         }
22465     
22466     },
22467     /***
22468      *
22469      * Range intersection.. the hard stuff...
22470      *  '-1' = before
22471      *  '0' = hits..
22472      *  '1' = after.
22473      *         [ -- selected range --- ]
22474      *   [fail]                        [fail]
22475      *
22476      *    basically..
22477      *      if end is before start or  hits it. fail.
22478      *      if start is after end or hits it fail.
22479      *
22480      *   if either hits (but other is outside. - then it's not 
22481      *   
22482      *    
22483      **/
22484     
22485     
22486     // @see http://www.thismuchiknow.co.uk/?p=64.
22487     rangeIntersectsNode : function(range, node)
22488     {
22489         var nodeRange = node.ownerDocument.createRange();
22490         try {
22491             nodeRange.selectNode(node);
22492         } catch (e) {
22493             nodeRange.selectNodeContents(node);
22494         }
22495     
22496         var rangeStartRange = range.cloneRange();
22497         rangeStartRange.collapse(true);
22498     
22499         var rangeEndRange = range.cloneRange();
22500         rangeEndRange.collapse(false);
22501     
22502         var nodeStartRange = nodeRange.cloneRange();
22503         nodeStartRange.collapse(true);
22504     
22505         var nodeEndRange = nodeRange.cloneRange();
22506         nodeEndRange.collapse(false);
22507     
22508         return rangeStartRange.compareBoundaryPoints(
22509                  Range.START_TO_START, nodeEndRange) == -1 &&
22510                rangeEndRange.compareBoundaryPoints(
22511                  Range.START_TO_START, nodeStartRange) == 1;
22512         
22513          
22514     },
22515     rangeCompareNode : function(range, node)
22516     {
22517         var nodeRange = node.ownerDocument.createRange();
22518         try {
22519             nodeRange.selectNode(node);
22520         } catch (e) {
22521             nodeRange.selectNodeContents(node);
22522         }
22523         
22524         
22525         range.collapse(true);
22526     
22527         nodeRange.collapse(true);
22528      
22529         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22530         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22531          
22532         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22533         
22534         var nodeIsBefore   =  ss == 1;
22535         var nodeIsAfter    = ee == -1;
22536         
22537         if (nodeIsBefore && nodeIsAfter) {
22538             return 0; // outer
22539         }
22540         if (!nodeIsBefore && nodeIsAfter) {
22541             return 1; //right trailed.
22542         }
22543         
22544         if (nodeIsBefore && !nodeIsAfter) {
22545             return 2;  // left trailed.
22546         }
22547         // fully contined.
22548         return 3;
22549     },
22550
22551     // private? - in a new class?
22552     cleanUpPaste :  function()
22553     {
22554         // cleans up the whole document..
22555         Roo.log('cleanuppaste');
22556         
22557         this.cleanUpChildren(this.doc.body);
22558         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22559         if (clean != this.doc.body.innerHTML) {
22560             this.doc.body.innerHTML = clean;
22561         }
22562         
22563     },
22564     
22565     cleanWordChars : function(input) {// change the chars to hex code
22566         var he = Roo.HtmlEditorCore;
22567         
22568         var output = input;
22569         Roo.each(he.swapCodes, function(sw) { 
22570             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22571             
22572             output = output.replace(swapper, sw[1]);
22573         });
22574         
22575         return output;
22576     },
22577     
22578     
22579     cleanUpChildren : function (n)
22580     {
22581         if (!n.childNodes.length) {
22582             return;
22583         }
22584         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22585            this.cleanUpChild(n.childNodes[i]);
22586         }
22587     },
22588     
22589     
22590         
22591     
22592     cleanUpChild : function (node)
22593     {
22594         var ed = this;
22595         //console.log(node);
22596         if (node.nodeName == "#text") {
22597             // clean up silly Windows -- stuff?
22598             return; 
22599         }
22600         if (node.nodeName == "#comment") {
22601             node.parentNode.removeChild(node);
22602             // clean up silly Windows -- stuff?
22603             return; 
22604         }
22605         var lcname = node.tagName.toLowerCase();
22606         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22607         // whitelist of tags..
22608         
22609         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22610             // remove node.
22611             node.parentNode.removeChild(node);
22612             return;
22613             
22614         }
22615         
22616         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22617         
22618         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22619         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22620         
22621         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22622         //    remove_keep_children = true;
22623         //}
22624         
22625         if (remove_keep_children) {
22626             this.cleanUpChildren(node);
22627             // inserts everything just before this node...
22628             while (node.childNodes.length) {
22629                 var cn = node.childNodes[0];
22630                 node.removeChild(cn);
22631                 node.parentNode.insertBefore(cn, node);
22632             }
22633             node.parentNode.removeChild(node);
22634             return;
22635         }
22636         
22637         if (!node.attributes || !node.attributes.length) {
22638             this.cleanUpChildren(node);
22639             return;
22640         }
22641         
22642         function cleanAttr(n,v)
22643         {
22644             
22645             if (v.match(/^\./) || v.match(/^\//)) {
22646                 return;
22647             }
22648             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22649                 return;
22650             }
22651             if (v.match(/^#/)) {
22652                 return;
22653             }
22654 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22655             node.removeAttribute(n);
22656             
22657         }
22658         
22659         var cwhite = this.cwhite;
22660         var cblack = this.cblack;
22661             
22662         function cleanStyle(n,v)
22663         {
22664             if (v.match(/expression/)) { //XSS?? should we even bother..
22665                 node.removeAttribute(n);
22666                 return;
22667             }
22668             
22669             var parts = v.split(/;/);
22670             var clean = [];
22671             
22672             Roo.each(parts, function(p) {
22673                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22674                 if (!p.length) {
22675                     return true;
22676                 }
22677                 var l = p.split(':').shift().replace(/\s+/g,'');
22678                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22679                 
22680                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22681 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22682                     //node.removeAttribute(n);
22683                     return true;
22684                 }
22685                 //Roo.log()
22686                 // only allow 'c whitelisted system attributes'
22687                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22688 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22689                     //node.removeAttribute(n);
22690                     return true;
22691                 }
22692                 
22693                 
22694                  
22695                 
22696                 clean.push(p);
22697                 return true;
22698             });
22699             if (clean.length) { 
22700                 node.setAttribute(n, clean.join(';'));
22701             } else {
22702                 node.removeAttribute(n);
22703             }
22704             
22705         }
22706         
22707         
22708         for (var i = node.attributes.length-1; i > -1 ; i--) {
22709             var a = node.attributes[i];
22710             //console.log(a);
22711             
22712             if (a.name.toLowerCase().substr(0,2)=='on')  {
22713                 node.removeAttribute(a.name);
22714                 continue;
22715             }
22716             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22717                 node.removeAttribute(a.name);
22718                 continue;
22719             }
22720             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22721                 cleanAttr(a.name,a.value); // fixme..
22722                 continue;
22723             }
22724             if (a.name == 'style') {
22725                 cleanStyle(a.name,a.value);
22726                 continue;
22727             }
22728             /// clean up MS crap..
22729             // tecnically this should be a list of valid class'es..
22730             
22731             
22732             if (a.name == 'class') {
22733                 if (a.value.match(/^Mso/)) {
22734                     node.className = '';
22735                 }
22736                 
22737                 if (a.value.match(/^body$/)) {
22738                     node.className = '';
22739                 }
22740                 continue;
22741             }
22742             
22743             // style cleanup!?
22744             // class cleanup?
22745             
22746         }
22747         
22748         
22749         this.cleanUpChildren(node);
22750         
22751         
22752     },
22753     
22754     /**
22755      * Clean up MS wordisms...
22756      */
22757     cleanWord : function(node)
22758     {
22759         
22760         
22761         if (!node) {
22762             this.cleanWord(this.doc.body);
22763             return;
22764         }
22765         if (node.nodeName == "#text") {
22766             // clean up silly Windows -- stuff?
22767             return; 
22768         }
22769         if (node.nodeName == "#comment") {
22770             node.parentNode.removeChild(node);
22771             // clean up silly Windows -- stuff?
22772             return; 
22773         }
22774         
22775         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22776             node.parentNode.removeChild(node);
22777             return;
22778         }
22779         
22780         // remove - but keep children..
22781         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22782             while (node.childNodes.length) {
22783                 var cn = node.childNodes[0];
22784                 node.removeChild(cn);
22785                 node.parentNode.insertBefore(cn, node);
22786             }
22787             node.parentNode.removeChild(node);
22788             this.iterateChildren(node, this.cleanWord);
22789             return;
22790         }
22791         // clean styles
22792         if (node.className.length) {
22793             
22794             var cn = node.className.split(/\W+/);
22795             var cna = [];
22796             Roo.each(cn, function(cls) {
22797                 if (cls.match(/Mso[a-zA-Z]+/)) {
22798                     return;
22799                 }
22800                 cna.push(cls);
22801             });
22802             node.className = cna.length ? cna.join(' ') : '';
22803             if (!cna.length) {
22804                 node.removeAttribute("class");
22805             }
22806         }
22807         
22808         if (node.hasAttribute("lang")) {
22809             node.removeAttribute("lang");
22810         }
22811         
22812         if (node.hasAttribute("style")) {
22813             
22814             var styles = node.getAttribute("style").split(";");
22815             var nstyle = [];
22816             Roo.each(styles, function(s) {
22817                 if (!s.match(/:/)) {
22818                     return;
22819                 }
22820                 var kv = s.split(":");
22821                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22822                     return;
22823                 }
22824                 // what ever is left... we allow.
22825                 nstyle.push(s);
22826             });
22827             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22828             if (!nstyle.length) {
22829                 node.removeAttribute('style');
22830             }
22831         }
22832         this.iterateChildren(node, this.cleanWord);
22833         
22834         
22835         
22836     },
22837     /**
22838      * iterateChildren of a Node, calling fn each time, using this as the scole..
22839      * @param {DomNode} node node to iterate children of.
22840      * @param {Function} fn method of this class to call on each item.
22841      */
22842     iterateChildren : function(node, fn)
22843     {
22844         if (!node.childNodes.length) {
22845                 return;
22846         }
22847         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22848            fn.call(this, node.childNodes[i])
22849         }
22850     },
22851     
22852     
22853     /**
22854      * cleanTableWidths.
22855      *
22856      * Quite often pasting from word etc.. results in tables with column and widths.
22857      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22858      *
22859      */
22860     cleanTableWidths : function(node)
22861     {
22862          
22863          
22864         if (!node) {
22865             this.cleanTableWidths(this.doc.body);
22866             return;
22867         }
22868         
22869         // ignore list...
22870         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22871             return; 
22872         }
22873         Roo.log(node.tagName);
22874         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22875             this.iterateChildren(node, this.cleanTableWidths);
22876             return;
22877         }
22878         if (node.hasAttribute('width')) {
22879             node.removeAttribute('width');
22880         }
22881         
22882          
22883         if (node.hasAttribute("style")) {
22884             // pretty basic...
22885             
22886             var styles = node.getAttribute("style").split(";");
22887             var nstyle = [];
22888             Roo.each(styles, function(s) {
22889                 if (!s.match(/:/)) {
22890                     return;
22891                 }
22892                 var kv = s.split(":");
22893                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22894                     return;
22895                 }
22896                 // what ever is left... we allow.
22897                 nstyle.push(s);
22898             });
22899             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22900             if (!nstyle.length) {
22901                 node.removeAttribute('style');
22902             }
22903         }
22904         
22905         this.iterateChildren(node, this.cleanTableWidths);
22906         
22907         
22908     },
22909     
22910     
22911     
22912     
22913     domToHTML : function(currentElement, depth, nopadtext) {
22914         
22915         depth = depth || 0;
22916         nopadtext = nopadtext || false;
22917     
22918         if (!currentElement) {
22919             return this.domToHTML(this.doc.body);
22920         }
22921         
22922         //Roo.log(currentElement);
22923         var j;
22924         var allText = false;
22925         var nodeName = currentElement.nodeName;
22926         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22927         
22928         if  (nodeName == '#text') {
22929             
22930             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22931         }
22932         
22933         
22934         var ret = '';
22935         if (nodeName != 'BODY') {
22936              
22937             var i = 0;
22938             // Prints the node tagName, such as <A>, <IMG>, etc
22939             if (tagName) {
22940                 var attr = [];
22941                 for(i = 0; i < currentElement.attributes.length;i++) {
22942                     // quoting?
22943                     var aname = currentElement.attributes.item(i).name;
22944                     if (!currentElement.attributes.item(i).value.length) {
22945                         continue;
22946                     }
22947                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22948                 }
22949                 
22950                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22951             } 
22952             else {
22953                 
22954                 // eack
22955             }
22956         } else {
22957             tagName = false;
22958         }
22959         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22960             return ret;
22961         }
22962         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22963             nopadtext = true;
22964         }
22965         
22966         
22967         // Traverse the tree
22968         i = 0;
22969         var currentElementChild = currentElement.childNodes.item(i);
22970         var allText = true;
22971         var innerHTML  = '';
22972         lastnode = '';
22973         while (currentElementChild) {
22974             // Formatting code (indent the tree so it looks nice on the screen)
22975             var nopad = nopadtext;
22976             if (lastnode == 'SPAN') {
22977                 nopad  = true;
22978             }
22979             // text
22980             if  (currentElementChild.nodeName == '#text') {
22981                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22982                 toadd = nopadtext ? toadd : toadd.trim();
22983                 if (!nopad && toadd.length > 80) {
22984                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22985                 }
22986                 innerHTML  += toadd;
22987                 
22988                 i++;
22989                 currentElementChild = currentElement.childNodes.item(i);
22990                 lastNode = '';
22991                 continue;
22992             }
22993             allText = false;
22994             
22995             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22996                 
22997             // Recursively traverse the tree structure of the child node
22998             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22999             lastnode = currentElementChild.nodeName;
23000             i++;
23001             currentElementChild=currentElement.childNodes.item(i);
23002         }
23003         
23004         ret += innerHTML;
23005         
23006         if (!allText) {
23007                 // The remaining code is mostly for formatting the tree
23008             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23009         }
23010         
23011         
23012         if (tagName) {
23013             ret+= "</"+tagName+">";
23014         }
23015         return ret;
23016         
23017     },
23018         
23019     applyBlacklists : function()
23020     {
23021         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23022         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23023         
23024         this.white = [];
23025         this.black = [];
23026         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23027             if (b.indexOf(tag) > -1) {
23028                 return;
23029             }
23030             this.white.push(tag);
23031             
23032         }, this);
23033         
23034         Roo.each(w, function(tag) {
23035             if (b.indexOf(tag) > -1) {
23036                 return;
23037             }
23038             if (this.white.indexOf(tag) > -1) {
23039                 return;
23040             }
23041             this.white.push(tag);
23042             
23043         }, this);
23044         
23045         
23046         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23047             if (w.indexOf(tag) > -1) {
23048                 return;
23049             }
23050             this.black.push(tag);
23051             
23052         }, this);
23053         
23054         Roo.each(b, function(tag) {
23055             if (w.indexOf(tag) > -1) {
23056                 return;
23057             }
23058             if (this.black.indexOf(tag) > -1) {
23059                 return;
23060             }
23061             this.black.push(tag);
23062             
23063         }, this);
23064         
23065         
23066         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23067         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23068         
23069         this.cwhite = [];
23070         this.cblack = [];
23071         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23072             if (b.indexOf(tag) > -1) {
23073                 return;
23074             }
23075             this.cwhite.push(tag);
23076             
23077         }, this);
23078         
23079         Roo.each(w, function(tag) {
23080             if (b.indexOf(tag) > -1) {
23081                 return;
23082             }
23083             if (this.cwhite.indexOf(tag) > -1) {
23084                 return;
23085             }
23086             this.cwhite.push(tag);
23087             
23088         }, this);
23089         
23090         
23091         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23092             if (w.indexOf(tag) > -1) {
23093                 return;
23094             }
23095             this.cblack.push(tag);
23096             
23097         }, this);
23098         
23099         Roo.each(b, function(tag) {
23100             if (w.indexOf(tag) > -1) {
23101                 return;
23102             }
23103             if (this.cblack.indexOf(tag) > -1) {
23104                 return;
23105             }
23106             this.cblack.push(tag);
23107             
23108         }, this);
23109     },
23110     
23111     setStylesheets : function(stylesheets)
23112     {
23113         if(typeof(stylesheets) == 'string'){
23114             Roo.get(this.iframe.contentDocument.head).createChild({
23115                 tag : 'link',
23116                 rel : 'stylesheet',
23117                 type : 'text/css',
23118                 href : stylesheets
23119             });
23120             
23121             return;
23122         }
23123         var _this = this;
23124      
23125         Roo.each(stylesheets, function(s) {
23126             if(!s.length){
23127                 return;
23128             }
23129             
23130             Roo.get(_this.iframe.contentDocument.head).createChild({
23131                 tag : 'link',
23132                 rel : 'stylesheet',
23133                 type : 'text/css',
23134                 href : s
23135             });
23136         });
23137
23138         
23139     },
23140     
23141     removeStylesheets : function()
23142     {
23143         var _this = this;
23144         
23145         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23146             s.remove();
23147         });
23148     },
23149     
23150     setStyle : function(style)
23151     {
23152         Roo.get(this.iframe.contentDocument.head).createChild({
23153             tag : 'style',
23154             type : 'text/css',
23155             html : style
23156         });
23157
23158         return;
23159     }
23160     
23161     // hide stuff that is not compatible
23162     /**
23163      * @event blur
23164      * @hide
23165      */
23166     /**
23167      * @event change
23168      * @hide
23169      */
23170     /**
23171      * @event focus
23172      * @hide
23173      */
23174     /**
23175      * @event specialkey
23176      * @hide
23177      */
23178     /**
23179      * @cfg {String} fieldClass @hide
23180      */
23181     /**
23182      * @cfg {String} focusClass @hide
23183      */
23184     /**
23185      * @cfg {String} autoCreate @hide
23186      */
23187     /**
23188      * @cfg {String} inputType @hide
23189      */
23190     /**
23191      * @cfg {String} invalidClass @hide
23192      */
23193     /**
23194      * @cfg {String} invalidText @hide
23195      */
23196     /**
23197      * @cfg {String} msgFx @hide
23198      */
23199     /**
23200      * @cfg {String} validateOnBlur @hide
23201      */
23202 });
23203
23204 Roo.HtmlEditorCore.white = [
23205         'area', 'br', 'img', 'input', 'hr', 'wbr',
23206         
23207        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23208        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23209        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23210        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23211        'table',   'ul',         'xmp', 
23212        
23213        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23214       'thead',   'tr', 
23215      
23216       'dir', 'menu', 'ol', 'ul', 'dl',
23217        
23218       'embed',  'object'
23219 ];
23220
23221
23222 Roo.HtmlEditorCore.black = [
23223     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23224         'applet', // 
23225         'base',   'basefont', 'bgsound', 'blink',  'body', 
23226         'frame',  'frameset', 'head',    'html',   'ilayer', 
23227         'iframe', 'layer',  'link',     'meta',    'object',   
23228         'script', 'style' ,'title',  'xml' // clean later..
23229 ];
23230 Roo.HtmlEditorCore.clean = [
23231     'script', 'style', 'title', 'xml'
23232 ];
23233 Roo.HtmlEditorCore.remove = [
23234     'font'
23235 ];
23236 // attributes..
23237
23238 Roo.HtmlEditorCore.ablack = [
23239     'on'
23240 ];
23241     
23242 Roo.HtmlEditorCore.aclean = [ 
23243     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23244 ];
23245
23246 // protocols..
23247 Roo.HtmlEditorCore.pwhite= [
23248         'http',  'https',  'mailto'
23249 ];
23250
23251 // white listed style attributes.
23252 Roo.HtmlEditorCore.cwhite= [
23253       //  'text-align', /// default is to allow most things..
23254       
23255          
23256 //        'font-size'//??
23257 ];
23258
23259 // black listed style attributes.
23260 Roo.HtmlEditorCore.cblack= [
23261       //  'font-size' -- this can be set by the project 
23262 ];
23263
23264
23265 Roo.HtmlEditorCore.swapCodes   =[ 
23266     [    8211, "--" ], 
23267     [    8212, "--" ], 
23268     [    8216,  "'" ],  
23269     [    8217, "'" ],  
23270     [    8220, '"' ],  
23271     [    8221, '"' ],  
23272     [    8226, "*" ],  
23273     [    8230, "..." ]
23274 ]; 
23275
23276     /*
23277  * - LGPL
23278  *
23279  * HtmlEditor
23280  * 
23281  */
23282
23283 /**
23284  * @class Roo.bootstrap.HtmlEditor
23285  * @extends Roo.bootstrap.TextArea
23286  * Bootstrap HtmlEditor class
23287
23288  * @constructor
23289  * Create a new HtmlEditor
23290  * @param {Object} config The config object
23291  */
23292
23293 Roo.bootstrap.HtmlEditor = function(config){
23294     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23295     if (!this.toolbars) {
23296         this.toolbars = [];
23297     }
23298     
23299     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23300     this.addEvents({
23301             /**
23302              * @event initialize
23303              * Fires when the editor is fully initialized (including the iframe)
23304              * @param {HtmlEditor} this
23305              */
23306             initialize: true,
23307             /**
23308              * @event activate
23309              * Fires when the editor is first receives the focus. Any insertion must wait
23310              * until after this event.
23311              * @param {HtmlEditor} this
23312              */
23313             activate: true,
23314              /**
23315              * @event beforesync
23316              * Fires before the textarea is updated with content from the editor iframe. Return false
23317              * to cancel the sync.
23318              * @param {HtmlEditor} this
23319              * @param {String} html
23320              */
23321             beforesync: true,
23322              /**
23323              * @event beforepush
23324              * Fires before the iframe editor is updated with content from the textarea. Return false
23325              * to cancel the push.
23326              * @param {HtmlEditor} this
23327              * @param {String} html
23328              */
23329             beforepush: true,
23330              /**
23331              * @event sync
23332              * Fires when the textarea is updated with content from the editor iframe.
23333              * @param {HtmlEditor} this
23334              * @param {String} html
23335              */
23336             sync: true,
23337              /**
23338              * @event push
23339              * Fires when the iframe editor is updated with content from the textarea.
23340              * @param {HtmlEditor} this
23341              * @param {String} html
23342              */
23343             push: true,
23344              /**
23345              * @event editmodechange
23346              * Fires when the editor switches edit modes
23347              * @param {HtmlEditor} this
23348              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23349              */
23350             editmodechange: true,
23351             /**
23352              * @event editorevent
23353              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23354              * @param {HtmlEditor} this
23355              */
23356             editorevent: true,
23357             /**
23358              * @event firstfocus
23359              * Fires when on first focus - needed by toolbars..
23360              * @param {HtmlEditor} this
23361              */
23362             firstfocus: true,
23363             /**
23364              * @event autosave
23365              * Auto save the htmlEditor value as a file into Events
23366              * @param {HtmlEditor} this
23367              */
23368             autosave: true,
23369             /**
23370              * @event savedpreview
23371              * preview the saved version of htmlEditor
23372              * @param {HtmlEditor} this
23373              */
23374             savedpreview: true
23375         });
23376 };
23377
23378
23379 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23380     
23381     
23382       /**
23383      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23384      */
23385     toolbars : false,
23386     
23387      /**
23388     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23389     */
23390     btns : [],
23391    
23392      /**
23393      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23394      *                        Roo.resizable.
23395      */
23396     resizable : false,
23397      /**
23398      * @cfg {Number} height (in pixels)
23399      */   
23400     height: 300,
23401    /**
23402      * @cfg {Number} width (in pixels)
23403      */   
23404     width: false,
23405     
23406     /**
23407      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23408      * 
23409      */
23410     stylesheets: false,
23411     
23412     // id of frame..
23413     frameId: false,
23414     
23415     // private properties
23416     validationEvent : false,
23417     deferHeight: true,
23418     initialized : false,
23419     activated : false,
23420     
23421     onFocus : Roo.emptyFn,
23422     iframePad:3,
23423     hideMode:'offsets',
23424     
23425     tbContainer : false,
23426     
23427     bodyCls : '',
23428     
23429     toolbarContainer :function() {
23430         return this.wrap.select('.x-html-editor-tb',true).first();
23431     },
23432
23433     /**
23434      * Protected method that will not generally be called directly. It
23435      * is called when the editor creates its toolbar. Override this method if you need to
23436      * add custom toolbar buttons.
23437      * @param {HtmlEditor} editor
23438      */
23439     createToolbar : function(){
23440         Roo.log('renewing');
23441         Roo.log("create toolbars");
23442         
23443         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23444         this.toolbars[0].render(this.toolbarContainer());
23445         
23446         return;
23447         
23448 //        if (!editor.toolbars || !editor.toolbars.length) {
23449 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23450 //        }
23451 //        
23452 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23453 //            editor.toolbars[i] = Roo.factory(
23454 //                    typeof(editor.toolbars[i]) == 'string' ?
23455 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23456 //                Roo.bootstrap.HtmlEditor);
23457 //            editor.toolbars[i].init(editor);
23458 //        }
23459     },
23460
23461      
23462     // private
23463     onRender : function(ct, position)
23464     {
23465        // Roo.log("Call onRender: " + this.xtype);
23466         var _t = this;
23467         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23468       
23469         this.wrap = this.inputEl().wrap({
23470             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23471         });
23472         
23473         this.editorcore.onRender(ct, position);
23474          
23475         if (this.resizable) {
23476             this.resizeEl = new Roo.Resizable(this.wrap, {
23477                 pinned : true,
23478                 wrap: true,
23479                 dynamic : true,
23480                 minHeight : this.height,
23481                 height: this.height,
23482                 handles : this.resizable,
23483                 width: this.width,
23484                 listeners : {
23485                     resize : function(r, w, h) {
23486                         _t.onResize(w,h); // -something
23487                     }
23488                 }
23489             });
23490             
23491         }
23492         this.createToolbar(this);
23493        
23494         
23495         if(!this.width && this.resizable){
23496             this.setSize(this.wrap.getSize());
23497         }
23498         if (this.resizeEl) {
23499             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23500             // should trigger onReize..
23501         }
23502         
23503     },
23504
23505     // private
23506     onResize : function(w, h)
23507     {
23508         Roo.log('resize: ' +w + ',' + h );
23509         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23510         var ew = false;
23511         var eh = false;
23512         
23513         if(this.inputEl() ){
23514             if(typeof w == 'number'){
23515                 var aw = w - this.wrap.getFrameWidth('lr');
23516                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23517                 ew = aw;
23518             }
23519             if(typeof h == 'number'){
23520                  var tbh = -11;  // fixme it needs to tool bar size!
23521                 for (var i =0; i < this.toolbars.length;i++) {
23522                     // fixme - ask toolbars for heights?
23523                     tbh += this.toolbars[i].el.getHeight();
23524                     //if (this.toolbars[i].footer) {
23525                     //    tbh += this.toolbars[i].footer.el.getHeight();
23526                     //}
23527                 }
23528               
23529                 
23530                 
23531                 
23532                 
23533                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23534                 ah -= 5; // knock a few pixes off for look..
23535                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23536                 var eh = ah;
23537             }
23538         }
23539         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23540         this.editorcore.onResize(ew,eh);
23541         
23542     },
23543
23544     /**
23545      * Toggles the editor between standard and source edit mode.
23546      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23547      */
23548     toggleSourceEdit : function(sourceEditMode)
23549     {
23550         this.editorcore.toggleSourceEdit(sourceEditMode);
23551         
23552         if(this.editorcore.sourceEditMode){
23553             Roo.log('editor - showing textarea');
23554             
23555 //            Roo.log('in');
23556 //            Roo.log(this.syncValue());
23557             this.syncValue();
23558             this.inputEl().removeClass(['hide', 'x-hidden']);
23559             this.inputEl().dom.removeAttribute('tabIndex');
23560             this.inputEl().focus();
23561         }else{
23562             Roo.log('editor - hiding textarea');
23563 //            Roo.log('out')
23564 //            Roo.log(this.pushValue()); 
23565             this.pushValue();
23566             
23567             this.inputEl().addClass(['hide', 'x-hidden']);
23568             this.inputEl().dom.setAttribute('tabIndex', -1);
23569             //this.deferFocus();
23570         }
23571          
23572         if(this.resizable){
23573             this.setSize(this.wrap.getSize());
23574         }
23575         
23576         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23577     },
23578  
23579     // private (for BoxComponent)
23580     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23581
23582     // private (for BoxComponent)
23583     getResizeEl : function(){
23584         return this.wrap;
23585     },
23586
23587     // private (for BoxComponent)
23588     getPositionEl : function(){
23589         return this.wrap;
23590     },
23591
23592     // private
23593     initEvents : function(){
23594         this.originalValue = this.getValue();
23595     },
23596
23597 //    /**
23598 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23599 //     * @method
23600 //     */
23601 //    markInvalid : Roo.emptyFn,
23602 //    /**
23603 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23604 //     * @method
23605 //     */
23606 //    clearInvalid : Roo.emptyFn,
23607
23608     setValue : function(v){
23609         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23610         this.editorcore.pushValue();
23611     },
23612
23613      
23614     // private
23615     deferFocus : function(){
23616         this.focus.defer(10, this);
23617     },
23618
23619     // doc'ed in Field
23620     focus : function(){
23621         this.editorcore.focus();
23622         
23623     },
23624       
23625
23626     // private
23627     onDestroy : function(){
23628         
23629         
23630         
23631         if(this.rendered){
23632             
23633             for (var i =0; i < this.toolbars.length;i++) {
23634                 // fixme - ask toolbars for heights?
23635                 this.toolbars[i].onDestroy();
23636             }
23637             
23638             this.wrap.dom.innerHTML = '';
23639             this.wrap.remove();
23640         }
23641     },
23642
23643     // private
23644     onFirstFocus : function(){
23645         //Roo.log("onFirstFocus");
23646         this.editorcore.onFirstFocus();
23647          for (var i =0; i < this.toolbars.length;i++) {
23648             this.toolbars[i].onFirstFocus();
23649         }
23650         
23651     },
23652     
23653     // private
23654     syncValue : function()
23655     {   
23656         this.editorcore.syncValue();
23657     },
23658     
23659     pushValue : function()
23660     {   
23661         this.editorcore.pushValue();
23662     }
23663      
23664     
23665     // hide stuff that is not compatible
23666     /**
23667      * @event blur
23668      * @hide
23669      */
23670     /**
23671      * @event change
23672      * @hide
23673      */
23674     /**
23675      * @event focus
23676      * @hide
23677      */
23678     /**
23679      * @event specialkey
23680      * @hide
23681      */
23682     /**
23683      * @cfg {String} fieldClass @hide
23684      */
23685     /**
23686      * @cfg {String} focusClass @hide
23687      */
23688     /**
23689      * @cfg {String} autoCreate @hide
23690      */
23691     /**
23692      * @cfg {String} inputType @hide
23693      */
23694     /**
23695      * @cfg {String} invalidClass @hide
23696      */
23697     /**
23698      * @cfg {String} invalidText @hide
23699      */
23700     /**
23701      * @cfg {String} msgFx @hide
23702      */
23703     /**
23704      * @cfg {String} validateOnBlur @hide
23705      */
23706 });
23707  
23708     
23709    
23710    
23711    
23712       
23713 Roo.namespace('Roo.bootstrap.htmleditor');
23714 /**
23715  * @class Roo.bootstrap.HtmlEditorToolbar1
23716  * Basic Toolbar
23717  * 
23718  * Usage:
23719  *
23720  new Roo.bootstrap.HtmlEditor({
23721     ....
23722     toolbars : [
23723         new Roo.bootstrap.HtmlEditorToolbar1({
23724             disable : { fonts: 1 , format: 1, ..., ... , ...],
23725             btns : [ .... ]
23726         })
23727     }
23728      
23729  * 
23730  * @cfg {Object} disable List of elements to disable..
23731  * @cfg {Array} btns List of additional buttons.
23732  * 
23733  * 
23734  * NEEDS Extra CSS? 
23735  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23736  */
23737  
23738 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23739 {
23740     
23741     Roo.apply(this, config);
23742     
23743     // default disabled, based on 'good practice'..
23744     this.disable = this.disable || {};
23745     Roo.applyIf(this.disable, {
23746         fontSize : true,
23747         colors : true,
23748         specialElements : true
23749     });
23750     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23751     
23752     this.editor = config.editor;
23753     this.editorcore = config.editor.editorcore;
23754     
23755     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23756     
23757     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23758     // dont call parent... till later.
23759 }
23760 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23761      
23762     bar : true,
23763     
23764     editor : false,
23765     editorcore : false,
23766     
23767     
23768     formats : [
23769         "p" ,  
23770         "h1","h2","h3","h4","h5","h6", 
23771         "pre", "code", 
23772         "abbr", "acronym", "address", "cite", "samp", "var",
23773         'div','span'
23774     ],
23775     
23776     onRender : function(ct, position)
23777     {
23778        // Roo.log("Call onRender: " + this.xtype);
23779         
23780        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23781        Roo.log(this.el);
23782        this.el.dom.style.marginBottom = '0';
23783        var _this = this;
23784        var editorcore = this.editorcore;
23785        var editor= this.editor;
23786        
23787        var children = [];
23788        var btn = function(id,cmd , toggle, handler, html){
23789        
23790             var  event = toggle ? 'toggle' : 'click';
23791        
23792             var a = {
23793                 size : 'sm',
23794                 xtype: 'Button',
23795                 xns: Roo.bootstrap,
23796                 glyphicon : id,
23797                 cmd : id || cmd,
23798                 enableToggle:toggle !== false,
23799                 html : html || '',
23800                 pressed : toggle ? false : null,
23801                 listeners : {}
23802             };
23803             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23804                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23805             };
23806             children.push(a);
23807             return a;
23808        }
23809        
23810     //    var cb_box = function...
23811         
23812         var style = {
23813                 xtype: 'Button',
23814                 size : 'sm',
23815                 xns: Roo.bootstrap,
23816                 glyphicon : 'font',
23817                 //html : 'submit'
23818                 menu : {
23819                     xtype: 'Menu',
23820                     xns: Roo.bootstrap,
23821                     items:  []
23822                 }
23823         };
23824         Roo.each(this.formats, function(f) {
23825             style.menu.items.push({
23826                 xtype :'MenuItem',
23827                 xns: Roo.bootstrap,
23828                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23829                 tagname : f,
23830                 listeners : {
23831                     click : function()
23832                     {
23833                         editorcore.insertTag(this.tagname);
23834                         editor.focus();
23835                     }
23836                 }
23837                 
23838             });
23839         });
23840         children.push(style);   
23841         
23842         btn('bold',false,true);
23843         btn('italic',false,true);
23844         btn('align-left', 'justifyleft',true);
23845         btn('align-center', 'justifycenter',true);
23846         btn('align-right' , 'justifyright',true);
23847         btn('link', false, false, function(btn) {
23848             //Roo.log("create link?");
23849             var url = prompt(this.createLinkText, this.defaultLinkValue);
23850             if(url && url != 'http:/'+'/'){
23851                 this.editorcore.relayCmd('createlink', url);
23852             }
23853         }),
23854         btn('list','insertunorderedlist',true);
23855         btn('pencil', false,true, function(btn){
23856                 Roo.log(this);
23857                 this.toggleSourceEdit(btn.pressed);
23858         });
23859         
23860         if (this.editor.btns.length > 0) {
23861             for (var i = 0; i<this.editor.btns.length; i++) {
23862                 children.push(this.editor.btns[i]);
23863             }
23864         }
23865         
23866         /*
23867         var cog = {
23868                 xtype: 'Button',
23869                 size : 'sm',
23870                 xns: Roo.bootstrap,
23871                 glyphicon : 'cog',
23872                 //html : 'submit'
23873                 menu : {
23874                     xtype: 'Menu',
23875                     xns: Roo.bootstrap,
23876                     items:  []
23877                 }
23878         };
23879         
23880         cog.menu.items.push({
23881             xtype :'MenuItem',
23882             xns: Roo.bootstrap,
23883             html : Clean styles,
23884             tagname : f,
23885             listeners : {
23886                 click : function()
23887                 {
23888                     editorcore.insertTag(this.tagname);
23889                     editor.focus();
23890                 }
23891             }
23892             
23893         });
23894        */
23895         
23896          
23897        this.xtype = 'NavSimplebar';
23898         
23899         for(var i=0;i< children.length;i++) {
23900             
23901             this.buttons.add(this.addxtypeChild(children[i]));
23902             
23903         }
23904         
23905         editor.on('editorevent', this.updateToolbar, this);
23906     },
23907     onBtnClick : function(id)
23908     {
23909        this.editorcore.relayCmd(id);
23910        this.editorcore.focus();
23911     },
23912     
23913     /**
23914      * Protected method that will not generally be called directly. It triggers
23915      * a toolbar update by reading the markup state of the current selection in the editor.
23916      */
23917     updateToolbar: function(){
23918
23919         if(!this.editorcore.activated){
23920             this.editor.onFirstFocus(); // is this neeed?
23921             return;
23922         }
23923
23924         var btns = this.buttons; 
23925         var doc = this.editorcore.doc;
23926         btns.get('bold').setActive(doc.queryCommandState('bold'));
23927         btns.get('italic').setActive(doc.queryCommandState('italic'));
23928         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23929         
23930         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23931         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23932         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23933         
23934         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23935         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23936          /*
23937         
23938         var ans = this.editorcore.getAllAncestors();
23939         if (this.formatCombo) {
23940             
23941             
23942             var store = this.formatCombo.store;
23943             this.formatCombo.setValue("");
23944             for (var i =0; i < ans.length;i++) {
23945                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23946                     // select it..
23947                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23948                     break;
23949                 }
23950             }
23951         }
23952         
23953         
23954         
23955         // hides menus... - so this cant be on a menu...
23956         Roo.bootstrap.MenuMgr.hideAll();
23957         */
23958         Roo.bootstrap.MenuMgr.hideAll();
23959         //this.editorsyncValue();
23960     },
23961     onFirstFocus: function() {
23962         this.buttons.each(function(item){
23963            item.enable();
23964         });
23965     },
23966     toggleSourceEdit : function(sourceEditMode){
23967         
23968           
23969         if(sourceEditMode){
23970             Roo.log("disabling buttons");
23971            this.buttons.each( function(item){
23972                 if(item.cmd != 'pencil'){
23973                     item.disable();
23974                 }
23975             });
23976           
23977         }else{
23978             Roo.log("enabling buttons");
23979             if(this.editorcore.initialized){
23980                 this.buttons.each( function(item){
23981                     item.enable();
23982                 });
23983             }
23984             
23985         }
23986         Roo.log("calling toggole on editor");
23987         // tell the editor that it's been pressed..
23988         this.editor.toggleSourceEdit(sourceEditMode);
23989        
23990     }
23991 });
23992
23993
23994
23995
23996
23997 /**
23998  * @class Roo.bootstrap.Table.AbstractSelectionModel
23999  * @extends Roo.util.Observable
24000  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24001  * implemented by descendant classes.  This class should not be directly instantiated.
24002  * @constructor
24003  */
24004 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24005     this.locked = false;
24006     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24007 };
24008
24009
24010 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24011     /** @ignore Called by the grid automatically. Do not call directly. */
24012     init : function(grid){
24013         this.grid = grid;
24014         this.initEvents();
24015     },
24016
24017     /**
24018      * Locks the selections.
24019      */
24020     lock : function(){
24021         this.locked = true;
24022     },
24023
24024     /**
24025      * Unlocks the selections.
24026      */
24027     unlock : function(){
24028         this.locked = false;
24029     },
24030
24031     /**
24032      * Returns true if the selections are locked.
24033      * @return {Boolean}
24034      */
24035     isLocked : function(){
24036         return this.locked;
24037     }
24038 });
24039 /**
24040  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24041  * @class Roo.bootstrap.Table.RowSelectionModel
24042  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24043  * It supports multiple selections and keyboard selection/navigation. 
24044  * @constructor
24045  * @param {Object} config
24046  */
24047
24048 Roo.bootstrap.Table.RowSelectionModel = function(config){
24049     Roo.apply(this, config);
24050     this.selections = new Roo.util.MixedCollection(false, function(o){
24051         return o.id;
24052     });
24053
24054     this.last = false;
24055     this.lastActive = false;
24056
24057     this.addEvents({
24058         /**
24059              * @event selectionchange
24060              * Fires when the selection changes
24061              * @param {SelectionModel} this
24062              */
24063             "selectionchange" : true,
24064         /**
24065              * @event afterselectionchange
24066              * Fires after the selection changes (eg. by key press or clicking)
24067              * @param {SelectionModel} this
24068              */
24069             "afterselectionchange" : true,
24070         /**
24071              * @event beforerowselect
24072              * Fires when a row is selected being selected, return false to cancel.
24073              * @param {SelectionModel} this
24074              * @param {Number} rowIndex The selected index
24075              * @param {Boolean} keepExisting False if other selections will be cleared
24076              */
24077             "beforerowselect" : true,
24078         /**
24079              * @event rowselect
24080              * Fires when a row is selected.
24081              * @param {SelectionModel} this
24082              * @param {Number} rowIndex The selected index
24083              * @param {Roo.data.Record} r The record
24084              */
24085             "rowselect" : true,
24086         /**
24087              * @event rowdeselect
24088              * Fires when a row is deselected.
24089              * @param {SelectionModel} this
24090              * @param {Number} rowIndex The selected index
24091              */
24092         "rowdeselect" : true
24093     });
24094     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24095     this.locked = false;
24096  };
24097
24098 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24099     /**
24100      * @cfg {Boolean} singleSelect
24101      * True to allow selection of only one row at a time (defaults to false)
24102      */
24103     singleSelect : false,
24104
24105     // private
24106     initEvents : function()
24107     {
24108
24109         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24110         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24111         //}else{ // allow click to work like normal
24112          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24113         //}
24114         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24115         this.grid.on("rowclick", this.handleMouseDown, this);
24116         
24117         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24118             "up" : function(e){
24119                 if(!e.shiftKey){
24120                     this.selectPrevious(e.shiftKey);
24121                 }else if(this.last !== false && this.lastActive !== false){
24122                     var last = this.last;
24123                     this.selectRange(this.last,  this.lastActive-1);
24124                     this.grid.getView().focusRow(this.lastActive);
24125                     if(last !== false){
24126                         this.last = last;
24127                     }
24128                 }else{
24129                     this.selectFirstRow();
24130                 }
24131                 this.fireEvent("afterselectionchange", this);
24132             },
24133             "down" : function(e){
24134                 if(!e.shiftKey){
24135                     this.selectNext(e.shiftKey);
24136                 }else if(this.last !== false && this.lastActive !== false){
24137                     var last = this.last;
24138                     this.selectRange(this.last,  this.lastActive+1);
24139                     this.grid.getView().focusRow(this.lastActive);
24140                     if(last !== false){
24141                         this.last = last;
24142                     }
24143                 }else{
24144                     this.selectFirstRow();
24145                 }
24146                 this.fireEvent("afterselectionchange", this);
24147             },
24148             scope: this
24149         });
24150         this.grid.store.on('load', function(){
24151             this.selections.clear();
24152         },this);
24153         /*
24154         var view = this.grid.view;
24155         view.on("refresh", this.onRefresh, this);
24156         view.on("rowupdated", this.onRowUpdated, this);
24157         view.on("rowremoved", this.onRemove, this);
24158         */
24159     },
24160
24161     // private
24162     onRefresh : function()
24163     {
24164         var ds = this.grid.store, i, v = this.grid.view;
24165         var s = this.selections;
24166         s.each(function(r){
24167             if((i = ds.indexOfId(r.id)) != -1){
24168                 v.onRowSelect(i);
24169             }else{
24170                 s.remove(r);
24171             }
24172         });
24173     },
24174
24175     // private
24176     onRemove : function(v, index, r){
24177         this.selections.remove(r);
24178     },
24179
24180     // private
24181     onRowUpdated : function(v, index, r){
24182         if(this.isSelected(r)){
24183             v.onRowSelect(index);
24184         }
24185     },
24186
24187     /**
24188      * Select records.
24189      * @param {Array} records The records to select
24190      * @param {Boolean} keepExisting (optional) True to keep existing selections
24191      */
24192     selectRecords : function(records, keepExisting)
24193     {
24194         if(!keepExisting){
24195             this.clearSelections();
24196         }
24197             var ds = this.grid.store;
24198         for(var i = 0, len = records.length; i < len; i++){
24199             this.selectRow(ds.indexOf(records[i]), true);
24200         }
24201     },
24202
24203     /**
24204      * Gets the number of selected rows.
24205      * @return {Number}
24206      */
24207     getCount : function(){
24208         return this.selections.length;
24209     },
24210
24211     /**
24212      * Selects the first row in the grid.
24213      */
24214     selectFirstRow : function(){
24215         this.selectRow(0);
24216     },
24217
24218     /**
24219      * Select the last row.
24220      * @param {Boolean} keepExisting (optional) True to keep existing selections
24221      */
24222     selectLastRow : function(keepExisting){
24223         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24224         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24225     },
24226
24227     /**
24228      * Selects the row immediately following the last selected row.
24229      * @param {Boolean} keepExisting (optional) True to keep existing selections
24230      */
24231     selectNext : function(keepExisting)
24232     {
24233             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24234             this.selectRow(this.last+1, keepExisting);
24235             this.grid.getView().focusRow(this.last);
24236         }
24237     },
24238
24239     /**
24240      * Selects the row that precedes the last selected row.
24241      * @param {Boolean} keepExisting (optional) True to keep existing selections
24242      */
24243     selectPrevious : function(keepExisting){
24244         if(this.last){
24245             this.selectRow(this.last-1, keepExisting);
24246             this.grid.getView().focusRow(this.last);
24247         }
24248     },
24249
24250     /**
24251      * Returns the selected records
24252      * @return {Array} Array of selected records
24253      */
24254     getSelections : function(){
24255         return [].concat(this.selections.items);
24256     },
24257
24258     /**
24259      * Returns the first selected record.
24260      * @return {Record}
24261      */
24262     getSelected : function(){
24263         return this.selections.itemAt(0);
24264     },
24265
24266
24267     /**
24268      * Clears all selections.
24269      */
24270     clearSelections : function(fast)
24271     {
24272         if(this.locked) {
24273             return;
24274         }
24275         if(fast !== true){
24276                 var ds = this.grid.store;
24277             var s = this.selections;
24278             s.each(function(r){
24279                 this.deselectRow(ds.indexOfId(r.id));
24280             }, this);
24281             s.clear();
24282         }else{
24283             this.selections.clear();
24284         }
24285         this.last = false;
24286     },
24287
24288
24289     /**
24290      * Selects all rows.
24291      */
24292     selectAll : function(){
24293         if(this.locked) {
24294             return;
24295         }
24296         this.selections.clear();
24297         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24298             this.selectRow(i, true);
24299         }
24300     },
24301
24302     /**
24303      * Returns True if there is a selection.
24304      * @return {Boolean}
24305      */
24306     hasSelection : function(){
24307         return this.selections.length > 0;
24308     },
24309
24310     /**
24311      * Returns True if the specified row is selected.
24312      * @param {Number/Record} record The record or index of the record to check
24313      * @return {Boolean}
24314      */
24315     isSelected : function(index){
24316             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24317         return (r && this.selections.key(r.id) ? true : false);
24318     },
24319
24320     /**
24321      * Returns True if the specified record id is selected.
24322      * @param {String} id The id of record to check
24323      * @return {Boolean}
24324      */
24325     isIdSelected : function(id){
24326         return (this.selections.key(id) ? true : false);
24327     },
24328
24329
24330     // private
24331     handleMouseDBClick : function(e, t){
24332         
24333     },
24334     // private
24335     handleMouseDown : function(e, t)
24336     {
24337             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24338         if(this.isLocked() || rowIndex < 0 ){
24339             return;
24340         };
24341         if(e.shiftKey && this.last !== false){
24342             var last = this.last;
24343             this.selectRange(last, rowIndex, e.ctrlKey);
24344             this.last = last; // reset the last
24345             t.focus();
24346     
24347         }else{
24348             var isSelected = this.isSelected(rowIndex);
24349             //Roo.log("select row:" + rowIndex);
24350             if(isSelected){
24351                 this.deselectRow(rowIndex);
24352             } else {
24353                         this.selectRow(rowIndex, true);
24354             }
24355     
24356             /*
24357                 if(e.button !== 0 && isSelected){
24358                 alert('rowIndex 2: ' + rowIndex);
24359                     view.focusRow(rowIndex);
24360                 }else if(e.ctrlKey && isSelected){
24361                     this.deselectRow(rowIndex);
24362                 }else if(!isSelected){
24363                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24364                     view.focusRow(rowIndex);
24365                 }
24366             */
24367         }
24368         this.fireEvent("afterselectionchange", this);
24369     },
24370     // private
24371     handleDragableRowClick :  function(grid, rowIndex, e) 
24372     {
24373         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24374             this.selectRow(rowIndex, false);
24375             grid.view.focusRow(rowIndex);
24376              this.fireEvent("afterselectionchange", this);
24377         }
24378     },
24379     
24380     /**
24381      * Selects multiple rows.
24382      * @param {Array} rows Array of the indexes of the row to select
24383      * @param {Boolean} keepExisting (optional) True to keep existing selections
24384      */
24385     selectRows : function(rows, keepExisting){
24386         if(!keepExisting){
24387             this.clearSelections();
24388         }
24389         for(var i = 0, len = rows.length; i < len; i++){
24390             this.selectRow(rows[i], true);
24391         }
24392     },
24393
24394     /**
24395      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24396      * @param {Number} startRow The index of the first row in the range
24397      * @param {Number} endRow The index of the last row in the range
24398      * @param {Boolean} keepExisting (optional) True to retain existing selections
24399      */
24400     selectRange : function(startRow, endRow, keepExisting){
24401         if(this.locked) {
24402             return;
24403         }
24404         if(!keepExisting){
24405             this.clearSelections();
24406         }
24407         if(startRow <= endRow){
24408             for(var i = startRow; i <= endRow; i++){
24409                 this.selectRow(i, true);
24410             }
24411         }else{
24412             for(var i = startRow; i >= endRow; i--){
24413                 this.selectRow(i, true);
24414             }
24415         }
24416     },
24417
24418     /**
24419      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24420      * @param {Number} startRow The index of the first row in the range
24421      * @param {Number} endRow The index of the last row in the range
24422      */
24423     deselectRange : function(startRow, endRow, preventViewNotify){
24424         if(this.locked) {
24425             return;
24426         }
24427         for(var i = startRow; i <= endRow; i++){
24428             this.deselectRow(i, preventViewNotify);
24429         }
24430     },
24431
24432     /**
24433      * Selects a row.
24434      * @param {Number} row The index of the row to select
24435      * @param {Boolean} keepExisting (optional) True to keep existing selections
24436      */
24437     selectRow : function(index, keepExisting, preventViewNotify)
24438     {
24439             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24440             return;
24441         }
24442         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24443             if(!keepExisting || this.singleSelect){
24444                 this.clearSelections();
24445             }
24446             
24447             var r = this.grid.store.getAt(index);
24448             //console.log('selectRow - record id :' + r.id);
24449             
24450             this.selections.add(r);
24451             this.last = this.lastActive = index;
24452             if(!preventViewNotify){
24453                 var proxy = new Roo.Element(
24454                                 this.grid.getRowDom(index)
24455                 );
24456                 proxy.addClass('bg-info info');
24457             }
24458             this.fireEvent("rowselect", this, index, r);
24459             this.fireEvent("selectionchange", this);
24460         }
24461     },
24462
24463     /**
24464      * Deselects a row.
24465      * @param {Number} row The index of the row to deselect
24466      */
24467     deselectRow : function(index, preventViewNotify)
24468     {
24469         if(this.locked) {
24470             return;
24471         }
24472         if(this.last == index){
24473             this.last = false;
24474         }
24475         if(this.lastActive == index){
24476             this.lastActive = false;
24477         }
24478         
24479         var r = this.grid.store.getAt(index);
24480         if (!r) {
24481             return;
24482         }
24483         
24484         this.selections.remove(r);
24485         //.console.log('deselectRow - record id :' + r.id);
24486         if(!preventViewNotify){
24487         
24488             var proxy = new Roo.Element(
24489                 this.grid.getRowDom(index)
24490             );
24491             proxy.removeClass('bg-info info');
24492         }
24493         this.fireEvent("rowdeselect", this, index);
24494         this.fireEvent("selectionchange", this);
24495     },
24496
24497     // private
24498     restoreLast : function(){
24499         if(this._last){
24500             this.last = this._last;
24501         }
24502     },
24503
24504     // private
24505     acceptsNav : function(row, col, cm){
24506         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24507     },
24508
24509     // private
24510     onEditorKey : function(field, e){
24511         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24512         if(k == e.TAB){
24513             e.stopEvent();
24514             ed.completeEdit();
24515             if(e.shiftKey){
24516                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24517             }else{
24518                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24519             }
24520         }else if(k == e.ENTER && !e.ctrlKey){
24521             e.stopEvent();
24522             ed.completeEdit();
24523             if(e.shiftKey){
24524                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24525             }else{
24526                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24527             }
24528         }else if(k == e.ESC){
24529             ed.cancelEdit();
24530         }
24531         if(newCell){
24532             g.startEditing(newCell[0], newCell[1]);
24533         }
24534     }
24535 });
24536 /*
24537  * Based on:
24538  * Ext JS Library 1.1.1
24539  * Copyright(c) 2006-2007, Ext JS, LLC.
24540  *
24541  * Originally Released Under LGPL - original licence link has changed is not relivant.
24542  *
24543  * Fork - LGPL
24544  * <script type="text/javascript">
24545  */
24546  
24547 /**
24548  * @class Roo.bootstrap.PagingToolbar
24549  * @extends Roo.bootstrap.NavSimplebar
24550  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24551  * @constructor
24552  * Create a new PagingToolbar
24553  * @param {Object} config The config object
24554  * @param {Roo.data.Store} store
24555  */
24556 Roo.bootstrap.PagingToolbar = function(config)
24557 {
24558     // old args format still supported... - xtype is prefered..
24559         // created from xtype...
24560     
24561     this.ds = config.dataSource;
24562     
24563     if (config.store && !this.ds) {
24564         this.store= Roo.factory(config.store, Roo.data);
24565         this.ds = this.store;
24566         this.ds.xmodule = this.xmodule || false;
24567     }
24568     
24569     this.toolbarItems = [];
24570     if (config.items) {
24571         this.toolbarItems = config.items;
24572     }
24573     
24574     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24575     
24576     this.cursor = 0;
24577     
24578     if (this.ds) { 
24579         this.bind(this.ds);
24580     }
24581     
24582     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24583     
24584 };
24585
24586 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24587     /**
24588      * @cfg {Roo.data.Store} dataSource
24589      * The underlying data store providing the paged data
24590      */
24591     /**
24592      * @cfg {String/HTMLElement/Element} container
24593      * container The id or element that will contain the toolbar
24594      */
24595     /**
24596      * @cfg {Boolean} displayInfo
24597      * True to display the displayMsg (defaults to false)
24598      */
24599     /**
24600      * @cfg {Number} pageSize
24601      * The number of records to display per page (defaults to 20)
24602      */
24603     pageSize: 20,
24604     /**
24605      * @cfg {String} displayMsg
24606      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24607      */
24608     displayMsg : 'Displaying {0} - {1} of {2}',
24609     /**
24610      * @cfg {String} emptyMsg
24611      * The message to display when no records are found (defaults to "No data to display")
24612      */
24613     emptyMsg : 'No data to display',
24614     /**
24615      * Customizable piece of the default paging text (defaults to "Page")
24616      * @type String
24617      */
24618     beforePageText : "Page",
24619     /**
24620      * Customizable piece of the default paging text (defaults to "of %0")
24621      * @type String
24622      */
24623     afterPageText : "of {0}",
24624     /**
24625      * Customizable piece of the default paging text (defaults to "First Page")
24626      * @type String
24627      */
24628     firstText : "First Page",
24629     /**
24630      * Customizable piece of the default paging text (defaults to "Previous Page")
24631      * @type String
24632      */
24633     prevText : "Previous Page",
24634     /**
24635      * Customizable piece of the default paging text (defaults to "Next Page")
24636      * @type String
24637      */
24638     nextText : "Next Page",
24639     /**
24640      * Customizable piece of the default paging text (defaults to "Last Page")
24641      * @type String
24642      */
24643     lastText : "Last Page",
24644     /**
24645      * Customizable piece of the default paging text (defaults to "Refresh")
24646      * @type String
24647      */
24648     refreshText : "Refresh",
24649
24650     buttons : false,
24651     // private
24652     onRender : function(ct, position) 
24653     {
24654         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24655         this.navgroup.parentId = this.id;
24656         this.navgroup.onRender(this.el, null);
24657         // add the buttons to the navgroup
24658         
24659         if(this.displayInfo){
24660             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24661             this.displayEl = this.el.select('.x-paging-info', true).first();
24662 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24663 //            this.displayEl = navel.el.select('span',true).first();
24664         }
24665         
24666         var _this = this;
24667         
24668         if(this.buttons){
24669             Roo.each(_this.buttons, function(e){ // this might need to use render????
24670                Roo.factory(e).render(_this.el);
24671             });
24672         }
24673             
24674         Roo.each(_this.toolbarItems, function(e) {
24675             _this.navgroup.addItem(e);
24676         });
24677         
24678         
24679         this.first = this.navgroup.addItem({
24680             tooltip: this.firstText,
24681             cls: "prev",
24682             icon : 'fa fa-backward',
24683             disabled: true,
24684             preventDefault: true,
24685             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24686         });
24687         
24688         this.prev =  this.navgroup.addItem({
24689             tooltip: this.prevText,
24690             cls: "prev",
24691             icon : 'fa fa-step-backward',
24692             disabled: true,
24693             preventDefault: true,
24694             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24695         });
24696     //this.addSeparator();
24697         
24698         
24699         var field = this.navgroup.addItem( {
24700             tagtype : 'span',
24701             cls : 'x-paging-position',
24702             
24703             html : this.beforePageText  +
24704                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24705                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24706          } ); //?? escaped?
24707         
24708         this.field = field.el.select('input', true).first();
24709         this.field.on("keydown", this.onPagingKeydown, this);
24710         this.field.on("focus", function(){this.dom.select();});
24711     
24712     
24713         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24714         //this.field.setHeight(18);
24715         //this.addSeparator();
24716         this.next = this.navgroup.addItem({
24717             tooltip: this.nextText,
24718             cls: "next",
24719             html : ' <i class="fa fa-step-forward">',
24720             disabled: true,
24721             preventDefault: true,
24722             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24723         });
24724         this.last = this.navgroup.addItem({
24725             tooltip: this.lastText,
24726             icon : 'fa fa-forward',
24727             cls: "next",
24728             disabled: true,
24729             preventDefault: true,
24730             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24731         });
24732     //this.addSeparator();
24733         this.loading = this.navgroup.addItem({
24734             tooltip: this.refreshText,
24735             icon: 'fa fa-refresh',
24736             preventDefault: true,
24737             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24738         });
24739         
24740     },
24741
24742     // private
24743     updateInfo : function(){
24744         if(this.displayEl){
24745             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24746             var msg = count == 0 ?
24747                 this.emptyMsg :
24748                 String.format(
24749                     this.displayMsg,
24750                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24751                 );
24752             this.displayEl.update(msg);
24753         }
24754     },
24755
24756     // private
24757     onLoad : function(ds, r, o)
24758     {
24759         this.cursor = o.params.start ? o.params.start : 0;
24760         
24761         var d = this.getPageData(),
24762             ap = d.activePage,
24763             ps = d.pages;
24764         
24765         
24766         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24767         this.field.dom.value = ap;
24768         this.first.setDisabled(ap == 1);
24769         this.prev.setDisabled(ap == 1);
24770         this.next.setDisabled(ap == ps);
24771         this.last.setDisabled(ap == ps);
24772         this.loading.enable();
24773         this.updateInfo();
24774     },
24775
24776     // private
24777     getPageData : function(){
24778         var total = this.ds.getTotalCount();
24779         return {
24780             total : total,
24781             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24782             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24783         };
24784     },
24785
24786     // private
24787     onLoadError : function(){
24788         this.loading.enable();
24789     },
24790
24791     // private
24792     onPagingKeydown : function(e){
24793         var k = e.getKey();
24794         var d = this.getPageData();
24795         if(k == e.RETURN){
24796             var v = this.field.dom.value, pageNum;
24797             if(!v || isNaN(pageNum = parseInt(v, 10))){
24798                 this.field.dom.value = d.activePage;
24799                 return;
24800             }
24801             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24802             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24803             e.stopEvent();
24804         }
24805         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))
24806         {
24807           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24808           this.field.dom.value = pageNum;
24809           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24810           e.stopEvent();
24811         }
24812         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24813         {
24814           var v = this.field.dom.value, pageNum; 
24815           var increment = (e.shiftKey) ? 10 : 1;
24816           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24817                 increment *= -1;
24818           }
24819           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24820             this.field.dom.value = d.activePage;
24821             return;
24822           }
24823           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24824           {
24825             this.field.dom.value = parseInt(v, 10) + increment;
24826             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24827             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24828           }
24829           e.stopEvent();
24830         }
24831     },
24832
24833     // private
24834     beforeLoad : function(){
24835         if(this.loading){
24836             this.loading.disable();
24837         }
24838     },
24839
24840     // private
24841     onClick : function(which){
24842         
24843         var ds = this.ds;
24844         if (!ds) {
24845             return;
24846         }
24847         
24848         switch(which){
24849             case "first":
24850                 ds.load({params:{start: 0, limit: this.pageSize}});
24851             break;
24852             case "prev":
24853                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24854             break;
24855             case "next":
24856                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24857             break;
24858             case "last":
24859                 var total = ds.getTotalCount();
24860                 var extra = total % this.pageSize;
24861                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24862                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24863             break;
24864             case "refresh":
24865                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24866             break;
24867         }
24868     },
24869
24870     /**
24871      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24872      * @param {Roo.data.Store} store The data store to unbind
24873      */
24874     unbind : function(ds){
24875         ds.un("beforeload", this.beforeLoad, this);
24876         ds.un("load", this.onLoad, this);
24877         ds.un("loadexception", this.onLoadError, this);
24878         ds.un("remove", this.updateInfo, this);
24879         ds.un("add", this.updateInfo, this);
24880         this.ds = undefined;
24881     },
24882
24883     /**
24884      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24885      * @param {Roo.data.Store} store The data store to bind
24886      */
24887     bind : function(ds){
24888         ds.on("beforeload", this.beforeLoad, this);
24889         ds.on("load", this.onLoad, this);
24890         ds.on("loadexception", this.onLoadError, this);
24891         ds.on("remove", this.updateInfo, this);
24892         ds.on("add", this.updateInfo, this);
24893         this.ds = ds;
24894     }
24895 });/*
24896  * - LGPL
24897  *
24898  * element
24899  * 
24900  */
24901
24902 /**
24903  * @class Roo.bootstrap.MessageBar
24904  * @extends Roo.bootstrap.Component
24905  * Bootstrap MessageBar class
24906  * @cfg {String} html contents of the MessageBar
24907  * @cfg {String} weight (info | success | warning | danger) default info
24908  * @cfg {String} beforeClass insert the bar before the given class
24909  * @cfg {Boolean} closable (true | false) default false
24910  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24911  * 
24912  * @constructor
24913  * Create a new Element
24914  * @param {Object} config The config object
24915  */
24916
24917 Roo.bootstrap.MessageBar = function(config){
24918     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24919 };
24920
24921 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24922     
24923     html: '',
24924     weight: 'info',
24925     closable: false,
24926     fixed: false,
24927     beforeClass: 'bootstrap-sticky-wrap',
24928     
24929     getAutoCreate : function(){
24930         
24931         var cfg = {
24932             tag: 'div',
24933             cls: 'alert alert-dismissable alert-' + this.weight,
24934             cn: [
24935                 {
24936                     tag: 'span',
24937                     cls: 'message',
24938                     html: this.html || ''
24939                 }
24940             ]
24941         };
24942         
24943         if(this.fixed){
24944             cfg.cls += ' alert-messages-fixed';
24945         }
24946         
24947         if(this.closable){
24948             cfg.cn.push({
24949                 tag: 'button',
24950                 cls: 'close',
24951                 html: 'x'
24952             });
24953         }
24954         
24955         return cfg;
24956     },
24957     
24958     onRender : function(ct, position)
24959     {
24960         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24961         
24962         if(!this.el){
24963             var cfg = Roo.apply({},  this.getAutoCreate());
24964             cfg.id = Roo.id();
24965             
24966             if (this.cls) {
24967                 cfg.cls += ' ' + this.cls;
24968             }
24969             if (this.style) {
24970                 cfg.style = this.style;
24971             }
24972             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24973             
24974             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24975         }
24976         
24977         this.el.select('>button.close').on('click', this.hide, this);
24978         
24979     },
24980     
24981     show : function()
24982     {
24983         if (!this.rendered) {
24984             this.render();
24985         }
24986         
24987         this.el.show();
24988         
24989         this.fireEvent('show', this);
24990         
24991     },
24992     
24993     hide : function()
24994     {
24995         if (!this.rendered) {
24996             this.render();
24997         }
24998         
24999         this.el.hide();
25000         
25001         this.fireEvent('hide', this);
25002     },
25003     
25004     update : function()
25005     {
25006 //        var e = this.el.dom.firstChild;
25007 //        
25008 //        if(this.closable){
25009 //            e = e.nextSibling;
25010 //        }
25011 //        
25012 //        e.data = this.html || '';
25013
25014         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25015     }
25016    
25017 });
25018
25019  
25020
25021      /*
25022  * - LGPL
25023  *
25024  * Graph
25025  * 
25026  */
25027
25028
25029 /**
25030  * @class Roo.bootstrap.Graph
25031  * @extends Roo.bootstrap.Component
25032  * Bootstrap Graph class
25033 > Prameters
25034  -sm {number} sm 4
25035  -md {number} md 5
25036  @cfg {String} graphtype  bar | vbar | pie
25037  @cfg {number} g_x coodinator | centre x (pie)
25038  @cfg {number} g_y coodinator | centre y (pie)
25039  @cfg {number} g_r radius (pie)
25040  @cfg {number} g_height height of the chart (respected by all elements in the set)
25041  @cfg {number} g_width width of the chart (respected by all elements in the set)
25042  @cfg {Object} title The title of the chart
25043     
25044  -{Array}  values
25045  -opts (object) options for the chart 
25046      o {
25047      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25048      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25049      o vgutter (number)
25050      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.
25051      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25052      o to
25053      o stretch (boolean)
25054      o }
25055  -opts (object) options for the pie
25056      o{
25057      o cut
25058      o startAngle (number)
25059      o endAngle (number)
25060      } 
25061  *
25062  * @constructor
25063  * Create a new Input
25064  * @param {Object} config The config object
25065  */
25066
25067 Roo.bootstrap.Graph = function(config){
25068     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25069     
25070     this.addEvents({
25071         // img events
25072         /**
25073          * @event click
25074          * The img click event for the img.
25075          * @param {Roo.EventObject} e
25076          */
25077         "click" : true
25078     });
25079 };
25080
25081 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25082     
25083     sm: 4,
25084     md: 5,
25085     graphtype: 'bar',
25086     g_height: 250,
25087     g_width: 400,
25088     g_x: 50,
25089     g_y: 50,
25090     g_r: 30,
25091     opts:{
25092         //g_colors: this.colors,
25093         g_type: 'soft',
25094         g_gutter: '20%'
25095
25096     },
25097     title : false,
25098
25099     getAutoCreate : function(){
25100         
25101         var cfg = {
25102             tag: 'div',
25103             html : null
25104         };
25105         
25106         
25107         return  cfg;
25108     },
25109
25110     onRender : function(ct,position){
25111         
25112         
25113         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25114         
25115         if (typeof(Raphael) == 'undefined') {
25116             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25117             return;
25118         }
25119         
25120         this.raphael = Raphael(this.el.dom);
25121         
25122                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25123                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25124                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25125                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25126                 /*
25127                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25128                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25129                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25130                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25131                 
25132                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25133                 r.barchart(330, 10, 300, 220, data1);
25134                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25135                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25136                 */
25137                 
25138                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25139                 // r.barchart(30, 30, 560, 250,  xdata, {
25140                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25141                 //     axis : "0 0 1 1",
25142                 //     axisxlabels :  xdata
25143                 //     //yvalues : cols,
25144                    
25145                 // });
25146 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25147 //        
25148 //        this.load(null,xdata,{
25149 //                axis : "0 0 1 1",
25150 //                axisxlabels :  xdata
25151 //                });
25152
25153     },
25154
25155     load : function(graphtype,xdata,opts)
25156     {
25157         this.raphael.clear();
25158         if(!graphtype) {
25159             graphtype = this.graphtype;
25160         }
25161         if(!opts){
25162             opts = this.opts;
25163         }
25164         var r = this.raphael,
25165             fin = function () {
25166                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25167             },
25168             fout = function () {
25169                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25170             },
25171             pfin = function() {
25172                 this.sector.stop();
25173                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25174
25175                 if (this.label) {
25176                     this.label[0].stop();
25177                     this.label[0].attr({ r: 7.5 });
25178                     this.label[1].attr({ "font-weight": 800 });
25179                 }
25180             },
25181             pfout = function() {
25182                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25183
25184                 if (this.label) {
25185                     this.label[0].animate({ r: 5 }, 500, "bounce");
25186                     this.label[1].attr({ "font-weight": 400 });
25187                 }
25188             };
25189
25190         switch(graphtype){
25191             case 'bar':
25192                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25193                 break;
25194             case 'hbar':
25195                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25196                 break;
25197             case 'pie':
25198 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25199 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25200 //            
25201                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25202                 
25203                 break;
25204
25205         }
25206         
25207         if(this.title){
25208             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25209         }
25210         
25211     },
25212     
25213     setTitle: function(o)
25214     {
25215         this.title = o;
25216     },
25217     
25218     initEvents: function() {
25219         
25220         if(!this.href){
25221             this.el.on('click', this.onClick, this);
25222         }
25223     },
25224     
25225     onClick : function(e)
25226     {
25227         Roo.log('img onclick');
25228         this.fireEvent('click', this, e);
25229     }
25230    
25231 });
25232
25233  
25234 /*
25235  * - LGPL
25236  *
25237  * numberBox
25238  * 
25239  */
25240 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25241
25242 /**
25243  * @class Roo.bootstrap.dash.NumberBox
25244  * @extends Roo.bootstrap.Component
25245  * Bootstrap NumberBox class
25246  * @cfg {String} headline Box headline
25247  * @cfg {String} content Box content
25248  * @cfg {String} icon Box icon
25249  * @cfg {String} footer Footer text
25250  * @cfg {String} fhref Footer href
25251  * 
25252  * @constructor
25253  * Create a new NumberBox
25254  * @param {Object} config The config object
25255  */
25256
25257
25258 Roo.bootstrap.dash.NumberBox = function(config){
25259     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25260     
25261 };
25262
25263 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25264     
25265     headline : '',
25266     content : '',
25267     icon : '',
25268     footer : '',
25269     fhref : '',
25270     ficon : '',
25271     
25272     getAutoCreate : function(){
25273         
25274         var cfg = {
25275             tag : 'div',
25276             cls : 'small-box ',
25277             cn : [
25278                 {
25279                     tag : 'div',
25280                     cls : 'inner',
25281                     cn :[
25282                         {
25283                             tag : 'h3',
25284                             cls : 'roo-headline',
25285                             html : this.headline
25286                         },
25287                         {
25288                             tag : 'p',
25289                             cls : 'roo-content',
25290                             html : this.content
25291                         }
25292                     ]
25293                 }
25294             ]
25295         };
25296         
25297         if(this.icon){
25298             cfg.cn.push({
25299                 tag : 'div',
25300                 cls : 'icon',
25301                 cn :[
25302                     {
25303                         tag : 'i',
25304                         cls : 'ion ' + this.icon
25305                     }
25306                 ]
25307             });
25308         }
25309         
25310         if(this.footer){
25311             var footer = {
25312                 tag : 'a',
25313                 cls : 'small-box-footer',
25314                 href : this.fhref || '#',
25315                 html : this.footer
25316             };
25317             
25318             cfg.cn.push(footer);
25319             
25320         }
25321         
25322         return  cfg;
25323     },
25324
25325     onRender : function(ct,position){
25326         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25327
25328
25329        
25330                 
25331     },
25332
25333     setHeadline: function (value)
25334     {
25335         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25336     },
25337     
25338     setFooter: function (value, href)
25339     {
25340         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25341         
25342         if(href){
25343             this.el.select('a.small-box-footer',true).first().attr('href', href);
25344         }
25345         
25346     },
25347
25348     setContent: function (value)
25349     {
25350         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25351     },
25352
25353     initEvents: function() 
25354     {   
25355         
25356     }
25357     
25358 });
25359
25360  
25361 /*
25362  * - LGPL
25363  *
25364  * TabBox
25365  * 
25366  */
25367 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25368
25369 /**
25370  * @class Roo.bootstrap.dash.TabBox
25371  * @extends Roo.bootstrap.Component
25372  * Bootstrap TabBox class
25373  * @cfg {String} title Title of the TabBox
25374  * @cfg {String} icon Icon of the TabBox
25375  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25376  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25377  * 
25378  * @constructor
25379  * Create a new TabBox
25380  * @param {Object} config The config object
25381  */
25382
25383
25384 Roo.bootstrap.dash.TabBox = function(config){
25385     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25386     this.addEvents({
25387         // raw events
25388         /**
25389          * @event addpane
25390          * When a pane is added
25391          * @param {Roo.bootstrap.dash.TabPane} pane
25392          */
25393         "addpane" : true,
25394         /**
25395          * @event activatepane
25396          * When a pane is activated
25397          * @param {Roo.bootstrap.dash.TabPane} pane
25398          */
25399         "activatepane" : true
25400         
25401          
25402     });
25403     
25404     this.panes = [];
25405 };
25406
25407 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25408
25409     title : '',
25410     icon : false,
25411     showtabs : true,
25412     tabScrollable : false,
25413     
25414     getChildContainer : function()
25415     {
25416         return this.el.select('.tab-content', true).first();
25417     },
25418     
25419     getAutoCreate : function(){
25420         
25421         var header = {
25422             tag: 'li',
25423             cls: 'pull-left header',
25424             html: this.title,
25425             cn : []
25426         };
25427         
25428         if(this.icon){
25429             header.cn.push({
25430                 tag: 'i',
25431                 cls: 'fa ' + this.icon
25432             });
25433         }
25434         
25435         var h = {
25436             tag: 'ul',
25437             cls: 'nav nav-tabs pull-right',
25438             cn: [
25439                 header
25440             ]
25441         };
25442         
25443         if(this.tabScrollable){
25444             h = {
25445                 tag: 'div',
25446                 cls: 'tab-header',
25447                 cn: [
25448                     {
25449                         tag: 'ul',
25450                         cls: 'nav nav-tabs pull-right',
25451                         cn: [
25452                             header
25453                         ]
25454                     }
25455                 ]
25456             };
25457         }
25458         
25459         var cfg = {
25460             tag: 'div',
25461             cls: 'nav-tabs-custom',
25462             cn: [
25463                 h,
25464                 {
25465                     tag: 'div',
25466                     cls: 'tab-content no-padding',
25467                     cn: []
25468                 }
25469             ]
25470         };
25471
25472         return  cfg;
25473     },
25474     initEvents : function()
25475     {
25476         //Roo.log('add add pane handler');
25477         this.on('addpane', this.onAddPane, this);
25478     },
25479      /**
25480      * Updates the box title
25481      * @param {String} html to set the title to.
25482      */
25483     setTitle : function(value)
25484     {
25485         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25486     },
25487     onAddPane : function(pane)
25488     {
25489         this.panes.push(pane);
25490         //Roo.log('addpane');
25491         //Roo.log(pane);
25492         // tabs are rendere left to right..
25493         if(!this.showtabs){
25494             return;
25495         }
25496         
25497         var ctr = this.el.select('.nav-tabs', true).first();
25498          
25499          
25500         var existing = ctr.select('.nav-tab',true);
25501         var qty = existing.getCount();;
25502         
25503         
25504         var tab = ctr.createChild({
25505             tag : 'li',
25506             cls : 'nav-tab' + (qty ? '' : ' active'),
25507             cn : [
25508                 {
25509                     tag : 'a',
25510                     href:'#',
25511                     html : pane.title
25512                 }
25513             ]
25514         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25515         pane.tab = tab;
25516         
25517         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25518         if (!qty) {
25519             pane.el.addClass('active');
25520         }
25521         
25522                 
25523     },
25524     onTabClick : function(ev,un,ob,pane)
25525     {
25526         //Roo.log('tab - prev default');
25527         ev.preventDefault();
25528         
25529         
25530         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25531         pane.tab.addClass('active');
25532         //Roo.log(pane.title);
25533         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25534         // technically we should have a deactivate event.. but maybe add later.
25535         // and it should not de-activate the selected tab...
25536         this.fireEvent('activatepane', pane);
25537         pane.el.addClass('active');
25538         pane.fireEvent('activate');
25539         
25540         
25541     },
25542     
25543     getActivePane : function()
25544     {
25545         var r = false;
25546         Roo.each(this.panes, function(p) {
25547             if(p.el.hasClass('active')){
25548                 r = p;
25549                 return false;
25550             }
25551             
25552             return;
25553         });
25554         
25555         return r;
25556     }
25557     
25558     
25559 });
25560
25561  
25562 /*
25563  * - LGPL
25564  *
25565  * Tab pane
25566  * 
25567  */
25568 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25569 /**
25570  * @class Roo.bootstrap.TabPane
25571  * @extends Roo.bootstrap.Component
25572  * Bootstrap TabPane class
25573  * @cfg {Boolean} active (false | true) Default false
25574  * @cfg {String} title title of panel
25575
25576  * 
25577  * @constructor
25578  * Create a new TabPane
25579  * @param {Object} config The config object
25580  */
25581
25582 Roo.bootstrap.dash.TabPane = function(config){
25583     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25584     
25585     this.addEvents({
25586         // raw events
25587         /**
25588          * @event activate
25589          * When a pane is activated
25590          * @param {Roo.bootstrap.dash.TabPane} pane
25591          */
25592         "activate" : true
25593          
25594     });
25595 };
25596
25597 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25598     
25599     active : false,
25600     title : '',
25601     
25602     // the tabBox that this is attached to.
25603     tab : false,
25604      
25605     getAutoCreate : function() 
25606     {
25607         var cfg = {
25608             tag: 'div',
25609             cls: 'tab-pane'
25610         };
25611         
25612         if(this.active){
25613             cfg.cls += ' active';
25614         }
25615         
25616         return cfg;
25617     },
25618     initEvents  : function()
25619     {
25620         //Roo.log('trigger add pane handler');
25621         this.parent().fireEvent('addpane', this)
25622     },
25623     
25624      /**
25625      * Updates the tab title 
25626      * @param {String} html to set the title to.
25627      */
25628     setTitle: function(str)
25629     {
25630         if (!this.tab) {
25631             return;
25632         }
25633         this.title = str;
25634         this.tab.select('a', true).first().dom.innerHTML = str;
25635         
25636     }
25637     
25638     
25639     
25640 });
25641
25642  
25643
25644
25645  /*
25646  * - LGPL
25647  *
25648  * menu
25649  * 
25650  */
25651 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25652
25653 /**
25654  * @class Roo.bootstrap.menu.Menu
25655  * @extends Roo.bootstrap.Component
25656  * Bootstrap Menu class - container for Menu
25657  * @cfg {String} html Text of the menu
25658  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25659  * @cfg {String} icon Font awesome icon
25660  * @cfg {String} pos Menu align to (top | bottom) default bottom
25661  * 
25662  * 
25663  * @constructor
25664  * Create a new Menu
25665  * @param {Object} config The config object
25666  */
25667
25668
25669 Roo.bootstrap.menu.Menu = function(config){
25670     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25671     
25672     this.addEvents({
25673         /**
25674          * @event beforeshow
25675          * Fires before this menu is displayed
25676          * @param {Roo.bootstrap.menu.Menu} this
25677          */
25678         beforeshow : true,
25679         /**
25680          * @event beforehide
25681          * Fires before this menu is hidden
25682          * @param {Roo.bootstrap.menu.Menu} this
25683          */
25684         beforehide : true,
25685         /**
25686          * @event show
25687          * Fires after this menu is displayed
25688          * @param {Roo.bootstrap.menu.Menu} this
25689          */
25690         show : true,
25691         /**
25692          * @event hide
25693          * Fires after this menu is hidden
25694          * @param {Roo.bootstrap.menu.Menu} this
25695          */
25696         hide : true,
25697         /**
25698          * @event click
25699          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25700          * @param {Roo.bootstrap.menu.Menu} this
25701          * @param {Roo.EventObject} e
25702          */
25703         click : true
25704     });
25705     
25706 };
25707
25708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25709     
25710     submenu : false,
25711     html : '',
25712     weight : 'default',
25713     icon : false,
25714     pos : 'bottom',
25715     
25716     
25717     getChildContainer : function() {
25718         if(this.isSubMenu){
25719             return this.el;
25720         }
25721         
25722         return this.el.select('ul.dropdown-menu', true).first();  
25723     },
25724     
25725     getAutoCreate : function()
25726     {
25727         var text = [
25728             {
25729                 tag : 'span',
25730                 cls : 'roo-menu-text',
25731                 html : this.html
25732             }
25733         ];
25734         
25735         if(this.icon){
25736             text.unshift({
25737                 tag : 'i',
25738                 cls : 'fa ' + this.icon
25739             })
25740         }
25741         
25742         
25743         var cfg = {
25744             tag : 'div',
25745             cls : 'btn-group',
25746             cn : [
25747                 {
25748                     tag : 'button',
25749                     cls : 'dropdown-button btn btn-' + this.weight,
25750                     cn : text
25751                 },
25752                 {
25753                     tag : 'button',
25754                     cls : 'dropdown-toggle btn btn-' + this.weight,
25755                     cn : [
25756                         {
25757                             tag : 'span',
25758                             cls : 'caret'
25759                         }
25760                     ]
25761                 },
25762                 {
25763                     tag : 'ul',
25764                     cls : 'dropdown-menu'
25765                 }
25766             ]
25767             
25768         };
25769         
25770         if(this.pos == 'top'){
25771             cfg.cls += ' dropup';
25772         }
25773         
25774         if(this.isSubMenu){
25775             cfg = {
25776                 tag : 'ul',
25777                 cls : 'dropdown-menu'
25778             }
25779         }
25780         
25781         return cfg;
25782     },
25783     
25784     onRender : function(ct, position)
25785     {
25786         this.isSubMenu = ct.hasClass('dropdown-submenu');
25787         
25788         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25789     },
25790     
25791     initEvents : function() 
25792     {
25793         if(this.isSubMenu){
25794             return;
25795         }
25796         
25797         this.hidden = true;
25798         
25799         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25800         this.triggerEl.on('click', this.onTriggerPress, this);
25801         
25802         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25803         this.buttonEl.on('click', this.onClick, this);
25804         
25805     },
25806     
25807     list : function()
25808     {
25809         if(this.isSubMenu){
25810             return this.el;
25811         }
25812         
25813         return this.el.select('ul.dropdown-menu', true).first();
25814     },
25815     
25816     onClick : function(e)
25817     {
25818         this.fireEvent("click", this, e);
25819     },
25820     
25821     onTriggerPress  : function(e)
25822     {   
25823         if (this.isVisible()) {
25824             this.hide();
25825         } else {
25826             this.show();
25827         }
25828     },
25829     
25830     isVisible : function(){
25831         return !this.hidden;
25832     },
25833     
25834     show : function()
25835     {
25836         this.fireEvent("beforeshow", this);
25837         
25838         this.hidden = false;
25839         this.el.addClass('open');
25840         
25841         Roo.get(document).on("mouseup", this.onMouseUp, this);
25842         
25843         this.fireEvent("show", this);
25844         
25845         
25846     },
25847     
25848     hide : function()
25849     {
25850         this.fireEvent("beforehide", this);
25851         
25852         this.hidden = true;
25853         this.el.removeClass('open');
25854         
25855         Roo.get(document).un("mouseup", this.onMouseUp);
25856         
25857         this.fireEvent("hide", this);
25858     },
25859     
25860     onMouseUp : function()
25861     {
25862         this.hide();
25863     }
25864     
25865 });
25866
25867  
25868  /*
25869  * - LGPL
25870  *
25871  * menu item
25872  * 
25873  */
25874 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25875
25876 /**
25877  * @class Roo.bootstrap.menu.Item
25878  * @extends Roo.bootstrap.Component
25879  * Bootstrap MenuItem class
25880  * @cfg {Boolean} submenu (true | false) default false
25881  * @cfg {String} html text of the item
25882  * @cfg {String} href the link
25883  * @cfg {Boolean} disable (true | false) default false
25884  * @cfg {Boolean} preventDefault (true | false) default true
25885  * @cfg {String} icon Font awesome icon
25886  * @cfg {String} pos Submenu align to (left | right) default right 
25887  * 
25888  * 
25889  * @constructor
25890  * Create a new Item
25891  * @param {Object} config The config object
25892  */
25893
25894
25895 Roo.bootstrap.menu.Item = function(config){
25896     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25897     this.addEvents({
25898         /**
25899          * @event mouseover
25900          * Fires when the mouse is hovering over this menu
25901          * @param {Roo.bootstrap.menu.Item} this
25902          * @param {Roo.EventObject} e
25903          */
25904         mouseover : true,
25905         /**
25906          * @event mouseout
25907          * Fires when the mouse exits this menu
25908          * @param {Roo.bootstrap.menu.Item} this
25909          * @param {Roo.EventObject} e
25910          */
25911         mouseout : true,
25912         // raw events
25913         /**
25914          * @event click
25915          * The raw click event for the entire grid.
25916          * @param {Roo.EventObject} e
25917          */
25918         click : true
25919     });
25920 };
25921
25922 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25923     
25924     submenu : false,
25925     href : '',
25926     html : '',
25927     preventDefault: true,
25928     disable : false,
25929     icon : false,
25930     pos : 'right',
25931     
25932     getAutoCreate : function()
25933     {
25934         var text = [
25935             {
25936                 tag : 'span',
25937                 cls : 'roo-menu-item-text',
25938                 html : this.html
25939             }
25940         ];
25941         
25942         if(this.icon){
25943             text.unshift({
25944                 tag : 'i',
25945                 cls : 'fa ' + this.icon
25946             })
25947         }
25948         
25949         var cfg = {
25950             tag : 'li',
25951             cn : [
25952                 {
25953                     tag : 'a',
25954                     href : this.href || '#',
25955                     cn : text
25956                 }
25957             ]
25958         };
25959         
25960         if(this.disable){
25961             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25962         }
25963         
25964         if(this.submenu){
25965             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25966             
25967             if(this.pos == 'left'){
25968                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25969             }
25970         }
25971         
25972         return cfg;
25973     },
25974     
25975     initEvents : function() 
25976     {
25977         this.el.on('mouseover', this.onMouseOver, this);
25978         this.el.on('mouseout', this.onMouseOut, this);
25979         
25980         this.el.select('a', true).first().on('click', this.onClick, this);
25981         
25982     },
25983     
25984     onClick : function(e)
25985     {
25986         if(this.preventDefault){
25987             e.preventDefault();
25988         }
25989         
25990         this.fireEvent("click", this, e);
25991     },
25992     
25993     onMouseOver : function(e)
25994     {
25995         if(this.submenu && this.pos == 'left'){
25996             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25997         }
25998         
25999         this.fireEvent("mouseover", this, e);
26000     },
26001     
26002     onMouseOut : function(e)
26003     {
26004         this.fireEvent("mouseout", this, e);
26005     }
26006 });
26007
26008  
26009
26010  /*
26011  * - LGPL
26012  *
26013  * menu separator
26014  * 
26015  */
26016 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26017
26018 /**
26019  * @class Roo.bootstrap.menu.Separator
26020  * @extends Roo.bootstrap.Component
26021  * Bootstrap Separator class
26022  * 
26023  * @constructor
26024  * Create a new Separator
26025  * @param {Object} config The config object
26026  */
26027
26028
26029 Roo.bootstrap.menu.Separator = function(config){
26030     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26031 };
26032
26033 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26034     
26035     getAutoCreate : function(){
26036         var cfg = {
26037             tag : 'li',
26038             cls: 'divider'
26039         };
26040         
26041         return cfg;
26042     }
26043    
26044 });
26045
26046  
26047
26048  /*
26049  * - LGPL
26050  *
26051  * Tooltip
26052  * 
26053  */
26054
26055 /**
26056  * @class Roo.bootstrap.Tooltip
26057  * Bootstrap Tooltip class
26058  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26059  * to determine which dom element triggers the tooltip.
26060  * 
26061  * It needs to add support for additional attributes like tooltip-position
26062  * 
26063  * @constructor
26064  * Create a new Toolti
26065  * @param {Object} config The config object
26066  */
26067
26068 Roo.bootstrap.Tooltip = function(config){
26069     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26070     
26071     this.alignment = Roo.bootstrap.Tooltip.alignment;
26072     
26073     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26074         this.alignment = config.alignment;
26075     }
26076     
26077 };
26078
26079 Roo.apply(Roo.bootstrap.Tooltip, {
26080     /**
26081      * @function init initialize tooltip monitoring.
26082      * @static
26083      */
26084     currentEl : false,
26085     currentTip : false,
26086     currentRegion : false,
26087     
26088     //  init : delay?
26089     
26090     init : function()
26091     {
26092         Roo.get(document).on('mouseover', this.enter ,this);
26093         Roo.get(document).on('mouseout', this.leave, this);
26094          
26095         
26096         this.currentTip = new Roo.bootstrap.Tooltip();
26097     },
26098     
26099     enter : function(ev)
26100     {
26101         var dom = ev.getTarget();
26102         
26103         //Roo.log(['enter',dom]);
26104         var el = Roo.fly(dom);
26105         if (this.currentEl) {
26106             //Roo.log(dom);
26107             //Roo.log(this.currentEl);
26108             //Roo.log(this.currentEl.contains(dom));
26109             if (this.currentEl == el) {
26110                 return;
26111             }
26112             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26113                 return;
26114             }
26115
26116         }
26117         
26118         if (this.currentTip.el) {
26119             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26120         }    
26121         //Roo.log(ev);
26122         
26123         if(!el || el.dom == document){
26124             return;
26125         }
26126         
26127         var bindEl = el;
26128         
26129         // you can not look for children, as if el is the body.. then everythign is the child..
26130         if (!el.attr('tooltip')) { //
26131             if (!el.select("[tooltip]").elements.length) {
26132                 return;
26133             }
26134             // is the mouse over this child...?
26135             bindEl = el.select("[tooltip]").first();
26136             var xy = ev.getXY();
26137             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26138                 //Roo.log("not in region.");
26139                 return;
26140             }
26141             //Roo.log("child element over..");
26142             
26143         }
26144         this.currentEl = bindEl;
26145         this.currentTip.bind(bindEl);
26146         this.currentRegion = Roo.lib.Region.getRegion(dom);
26147         this.currentTip.enter();
26148         
26149     },
26150     leave : function(ev)
26151     {
26152         var dom = ev.getTarget();
26153         //Roo.log(['leave',dom]);
26154         if (!this.currentEl) {
26155             return;
26156         }
26157         
26158         
26159         if (dom != this.currentEl.dom) {
26160             return;
26161         }
26162         var xy = ev.getXY();
26163         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26164             return;
26165         }
26166         // only activate leave if mouse cursor is outside... bounding box..
26167         
26168         
26169         
26170         
26171         if (this.currentTip) {
26172             this.currentTip.leave();
26173         }
26174         //Roo.log('clear currentEl');
26175         this.currentEl = false;
26176         
26177         
26178     },
26179     alignment : {
26180         'left' : ['r-l', [-2,0], 'right'],
26181         'right' : ['l-r', [2,0], 'left'],
26182         'bottom' : ['t-b', [0,2], 'top'],
26183         'top' : [ 'b-t', [0,-2], 'bottom']
26184     }
26185     
26186 });
26187
26188
26189 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26190     
26191     
26192     bindEl : false,
26193     
26194     delay : null, // can be { show : 300 , hide: 500}
26195     
26196     timeout : null,
26197     
26198     hoverState : null, //???
26199     
26200     placement : 'bottom', 
26201     
26202     alignment : false,
26203     
26204     getAutoCreate : function(){
26205     
26206         var cfg = {
26207            cls : 'tooltip',
26208            role : 'tooltip',
26209            cn : [
26210                 {
26211                     cls : 'tooltip-arrow'
26212                 },
26213                 {
26214                     cls : 'tooltip-inner'
26215                 }
26216            ]
26217         };
26218         
26219         return cfg;
26220     },
26221     bind : function(el)
26222     {
26223         this.bindEl = el;
26224     },
26225       
26226     
26227     enter : function () {
26228        
26229         if (this.timeout != null) {
26230             clearTimeout(this.timeout);
26231         }
26232         
26233         this.hoverState = 'in';
26234          //Roo.log("enter - show");
26235         if (!this.delay || !this.delay.show) {
26236             this.show();
26237             return;
26238         }
26239         var _t = this;
26240         this.timeout = setTimeout(function () {
26241             if (_t.hoverState == 'in') {
26242                 _t.show();
26243             }
26244         }, this.delay.show);
26245     },
26246     leave : function()
26247     {
26248         clearTimeout(this.timeout);
26249     
26250         this.hoverState = 'out';
26251          if (!this.delay || !this.delay.hide) {
26252             this.hide();
26253             return;
26254         }
26255        
26256         var _t = this;
26257         this.timeout = setTimeout(function () {
26258             //Roo.log("leave - timeout");
26259             
26260             if (_t.hoverState == 'out') {
26261                 _t.hide();
26262                 Roo.bootstrap.Tooltip.currentEl = false;
26263             }
26264         }, delay);
26265     },
26266     
26267     show : function (msg)
26268     {
26269         if (!this.el) {
26270             this.render(document.body);
26271         }
26272         // set content.
26273         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26274         
26275         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26276         
26277         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26278         
26279         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26280         
26281         var placement = typeof this.placement == 'function' ?
26282             this.placement.call(this, this.el, on_el) :
26283             this.placement;
26284             
26285         var autoToken = /\s?auto?\s?/i;
26286         var autoPlace = autoToken.test(placement);
26287         if (autoPlace) {
26288             placement = placement.replace(autoToken, '') || 'top';
26289         }
26290         
26291         //this.el.detach()
26292         //this.el.setXY([0,0]);
26293         this.el.show();
26294         //this.el.dom.style.display='block';
26295         
26296         //this.el.appendTo(on_el);
26297         
26298         var p = this.getPosition();
26299         var box = this.el.getBox();
26300         
26301         if (autoPlace) {
26302             // fixme..
26303         }
26304         
26305         var align = this.alignment[placement];
26306         
26307         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26308         
26309         if(placement == 'top' || placement == 'bottom'){
26310             if(xy[0] < 0){
26311                 placement = 'right';
26312             }
26313             
26314             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26315                 placement = 'left';
26316             }
26317             
26318             var scroll = Roo.select('body', true).first().getScroll();
26319             
26320             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26321                 placement = 'top';
26322             }
26323             
26324             align = this.alignment[placement];
26325         }
26326         
26327         this.el.alignTo(this.bindEl, align[0],align[1]);
26328         //var arrow = this.el.select('.arrow',true).first();
26329         //arrow.set(align[2], 
26330         
26331         this.el.addClass(placement);
26332         
26333         this.el.addClass('in fade');
26334         
26335         this.hoverState = null;
26336         
26337         if (this.el.hasClass('fade')) {
26338             // fade it?
26339         }
26340         
26341     },
26342     hide : function()
26343     {
26344          
26345         if (!this.el) {
26346             return;
26347         }
26348         //this.el.setXY([0,0]);
26349         this.el.removeClass('in');
26350         //this.el.hide();
26351         
26352     }
26353     
26354 });
26355  
26356
26357  /*
26358  * - LGPL
26359  *
26360  * Location Picker
26361  * 
26362  */
26363
26364 /**
26365  * @class Roo.bootstrap.LocationPicker
26366  * @extends Roo.bootstrap.Component
26367  * Bootstrap LocationPicker class
26368  * @cfg {Number} latitude Position when init default 0
26369  * @cfg {Number} longitude Position when init default 0
26370  * @cfg {Number} zoom default 15
26371  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26372  * @cfg {Boolean} mapTypeControl default false
26373  * @cfg {Boolean} disableDoubleClickZoom default false
26374  * @cfg {Boolean} scrollwheel default true
26375  * @cfg {Boolean} streetViewControl default false
26376  * @cfg {Number} radius default 0
26377  * @cfg {String} locationName
26378  * @cfg {Boolean} draggable default true
26379  * @cfg {Boolean} enableAutocomplete default false
26380  * @cfg {Boolean} enableReverseGeocode default true
26381  * @cfg {String} markerTitle
26382  * 
26383  * @constructor
26384  * Create a new LocationPicker
26385  * @param {Object} config The config object
26386  */
26387
26388
26389 Roo.bootstrap.LocationPicker = function(config){
26390     
26391     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26392     
26393     this.addEvents({
26394         /**
26395          * @event initial
26396          * Fires when the picker initialized.
26397          * @param {Roo.bootstrap.LocationPicker} this
26398          * @param {Google Location} location
26399          */
26400         initial : true,
26401         /**
26402          * @event positionchanged
26403          * Fires when the picker position changed.
26404          * @param {Roo.bootstrap.LocationPicker} this
26405          * @param {Google Location} location
26406          */
26407         positionchanged : true,
26408         /**
26409          * @event resize
26410          * Fires when the map resize.
26411          * @param {Roo.bootstrap.LocationPicker} this
26412          */
26413         resize : true,
26414         /**
26415          * @event show
26416          * Fires when the map show.
26417          * @param {Roo.bootstrap.LocationPicker} this
26418          */
26419         show : true,
26420         /**
26421          * @event hide
26422          * Fires when the map hide.
26423          * @param {Roo.bootstrap.LocationPicker} this
26424          */
26425         hide : true,
26426         /**
26427          * @event mapClick
26428          * Fires when click the map.
26429          * @param {Roo.bootstrap.LocationPicker} this
26430          * @param {Map event} e
26431          */
26432         mapClick : true,
26433         /**
26434          * @event mapRightClick
26435          * Fires when right click the map.
26436          * @param {Roo.bootstrap.LocationPicker} this
26437          * @param {Map event} e
26438          */
26439         mapRightClick : true,
26440         /**
26441          * @event markerClick
26442          * Fires when click the marker.
26443          * @param {Roo.bootstrap.LocationPicker} this
26444          * @param {Map event} e
26445          */
26446         markerClick : true,
26447         /**
26448          * @event markerRightClick
26449          * Fires when right click the marker.
26450          * @param {Roo.bootstrap.LocationPicker} this
26451          * @param {Map event} e
26452          */
26453         markerRightClick : true,
26454         /**
26455          * @event OverlayViewDraw
26456          * Fires when OverlayView Draw
26457          * @param {Roo.bootstrap.LocationPicker} this
26458          */
26459         OverlayViewDraw : true,
26460         /**
26461          * @event OverlayViewOnAdd
26462          * Fires when OverlayView Draw
26463          * @param {Roo.bootstrap.LocationPicker} this
26464          */
26465         OverlayViewOnAdd : true,
26466         /**
26467          * @event OverlayViewOnRemove
26468          * Fires when OverlayView Draw
26469          * @param {Roo.bootstrap.LocationPicker} this
26470          */
26471         OverlayViewOnRemove : true,
26472         /**
26473          * @event OverlayViewShow
26474          * Fires when OverlayView Draw
26475          * @param {Roo.bootstrap.LocationPicker} this
26476          * @param {Pixel} cpx
26477          */
26478         OverlayViewShow : true,
26479         /**
26480          * @event OverlayViewHide
26481          * Fires when OverlayView Draw
26482          * @param {Roo.bootstrap.LocationPicker} this
26483          */
26484         OverlayViewHide : true,
26485         /**
26486          * @event loadexception
26487          * Fires when load google lib failed.
26488          * @param {Roo.bootstrap.LocationPicker} this
26489          */
26490         loadexception : true
26491     });
26492         
26493 };
26494
26495 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26496     
26497     gMapContext: false,
26498     
26499     latitude: 0,
26500     longitude: 0,
26501     zoom: 15,
26502     mapTypeId: false,
26503     mapTypeControl: false,
26504     disableDoubleClickZoom: false,
26505     scrollwheel: true,
26506     streetViewControl: false,
26507     radius: 0,
26508     locationName: '',
26509     draggable: true,
26510     enableAutocomplete: false,
26511     enableReverseGeocode: true,
26512     markerTitle: '',
26513     
26514     getAutoCreate: function()
26515     {
26516
26517         var cfg = {
26518             tag: 'div',
26519             cls: 'roo-location-picker'
26520         };
26521         
26522         return cfg
26523     },
26524     
26525     initEvents: function(ct, position)
26526     {       
26527         if(!this.el.getWidth() || this.isApplied()){
26528             return;
26529         }
26530         
26531         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26532         
26533         this.initial();
26534     },
26535     
26536     initial: function()
26537     {
26538         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26539             this.fireEvent('loadexception', this);
26540             return;
26541         }
26542         
26543         if(!this.mapTypeId){
26544             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26545         }
26546         
26547         this.gMapContext = this.GMapContext();
26548         
26549         this.initOverlayView();
26550         
26551         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26552         
26553         var _this = this;
26554                 
26555         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26556             _this.setPosition(_this.gMapContext.marker.position);
26557         });
26558         
26559         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26560             _this.fireEvent('mapClick', this, event);
26561             
26562         });
26563
26564         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26565             _this.fireEvent('mapRightClick', this, event);
26566             
26567         });
26568         
26569         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26570             _this.fireEvent('markerClick', this, event);
26571             
26572         });
26573
26574         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26575             _this.fireEvent('markerRightClick', this, event);
26576             
26577         });
26578         
26579         this.setPosition(this.gMapContext.location);
26580         
26581         this.fireEvent('initial', this, this.gMapContext.location);
26582     },
26583     
26584     initOverlayView: function()
26585     {
26586         var _this = this;
26587         
26588         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26589             
26590             draw: function()
26591             {
26592                 _this.fireEvent('OverlayViewDraw', _this);
26593             },
26594             
26595             onAdd: function()
26596             {
26597                 _this.fireEvent('OverlayViewOnAdd', _this);
26598             },
26599             
26600             onRemove: function()
26601             {
26602                 _this.fireEvent('OverlayViewOnRemove', _this);
26603             },
26604             
26605             show: function(cpx)
26606             {
26607                 _this.fireEvent('OverlayViewShow', _this, cpx);
26608             },
26609             
26610             hide: function()
26611             {
26612                 _this.fireEvent('OverlayViewHide', _this);
26613             }
26614             
26615         });
26616     },
26617     
26618     fromLatLngToContainerPixel: function(event)
26619     {
26620         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26621     },
26622     
26623     isApplied: function() 
26624     {
26625         return this.getGmapContext() == false ? false : true;
26626     },
26627     
26628     getGmapContext: function() 
26629     {
26630         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26631     },
26632     
26633     GMapContext: function() 
26634     {
26635         var position = new google.maps.LatLng(this.latitude, this.longitude);
26636         
26637         var _map = new google.maps.Map(this.el.dom, {
26638             center: position,
26639             zoom: this.zoom,
26640             mapTypeId: this.mapTypeId,
26641             mapTypeControl: this.mapTypeControl,
26642             disableDoubleClickZoom: this.disableDoubleClickZoom,
26643             scrollwheel: this.scrollwheel,
26644             streetViewControl: this.streetViewControl,
26645             locationName: this.locationName,
26646             draggable: this.draggable,
26647             enableAutocomplete: this.enableAutocomplete,
26648             enableReverseGeocode: this.enableReverseGeocode
26649         });
26650         
26651         var _marker = new google.maps.Marker({
26652             position: position,
26653             map: _map,
26654             title: this.markerTitle,
26655             draggable: this.draggable
26656         });
26657         
26658         return {
26659             map: _map,
26660             marker: _marker,
26661             circle: null,
26662             location: position,
26663             radius: this.radius,
26664             locationName: this.locationName,
26665             addressComponents: {
26666                 formatted_address: null,
26667                 addressLine1: null,
26668                 addressLine2: null,
26669                 streetName: null,
26670                 streetNumber: null,
26671                 city: null,
26672                 district: null,
26673                 state: null,
26674                 stateOrProvince: null
26675             },
26676             settings: this,
26677             domContainer: this.el.dom,
26678             geodecoder: new google.maps.Geocoder()
26679         };
26680     },
26681     
26682     drawCircle: function(center, radius, options) 
26683     {
26684         if (this.gMapContext.circle != null) {
26685             this.gMapContext.circle.setMap(null);
26686         }
26687         if (radius > 0) {
26688             radius *= 1;
26689             options = Roo.apply({}, options, {
26690                 strokeColor: "#0000FF",
26691                 strokeOpacity: .35,
26692                 strokeWeight: 2,
26693                 fillColor: "#0000FF",
26694                 fillOpacity: .2
26695             });
26696             
26697             options.map = this.gMapContext.map;
26698             options.radius = radius;
26699             options.center = center;
26700             this.gMapContext.circle = new google.maps.Circle(options);
26701             return this.gMapContext.circle;
26702         }
26703         
26704         return null;
26705     },
26706     
26707     setPosition: function(location) 
26708     {
26709         this.gMapContext.location = location;
26710         this.gMapContext.marker.setPosition(location);
26711         this.gMapContext.map.panTo(location);
26712         this.drawCircle(location, this.gMapContext.radius, {});
26713         
26714         var _this = this;
26715         
26716         if (this.gMapContext.settings.enableReverseGeocode) {
26717             this.gMapContext.geodecoder.geocode({
26718                 latLng: this.gMapContext.location
26719             }, function(results, status) {
26720                 
26721                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26722                     _this.gMapContext.locationName = results[0].formatted_address;
26723                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26724                     
26725                     _this.fireEvent('positionchanged', this, location);
26726                 }
26727             });
26728             
26729             return;
26730         }
26731         
26732         this.fireEvent('positionchanged', this, location);
26733     },
26734     
26735     resize: function()
26736     {
26737         google.maps.event.trigger(this.gMapContext.map, "resize");
26738         
26739         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26740         
26741         this.fireEvent('resize', this);
26742     },
26743     
26744     setPositionByLatLng: function(latitude, longitude)
26745     {
26746         this.setPosition(new google.maps.LatLng(latitude, longitude));
26747     },
26748     
26749     getCurrentPosition: function() 
26750     {
26751         return {
26752             latitude: this.gMapContext.location.lat(),
26753             longitude: this.gMapContext.location.lng()
26754         };
26755     },
26756     
26757     getAddressName: function() 
26758     {
26759         return this.gMapContext.locationName;
26760     },
26761     
26762     getAddressComponents: function() 
26763     {
26764         return this.gMapContext.addressComponents;
26765     },
26766     
26767     address_component_from_google_geocode: function(address_components) 
26768     {
26769         var result = {};
26770         
26771         for (var i = 0; i < address_components.length; i++) {
26772             var component = address_components[i];
26773             if (component.types.indexOf("postal_code") >= 0) {
26774                 result.postalCode = component.short_name;
26775             } else if (component.types.indexOf("street_number") >= 0) {
26776                 result.streetNumber = component.short_name;
26777             } else if (component.types.indexOf("route") >= 0) {
26778                 result.streetName = component.short_name;
26779             } else if (component.types.indexOf("neighborhood") >= 0) {
26780                 result.city = component.short_name;
26781             } else if (component.types.indexOf("locality") >= 0) {
26782                 result.city = component.short_name;
26783             } else if (component.types.indexOf("sublocality") >= 0) {
26784                 result.district = component.short_name;
26785             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26786                 result.stateOrProvince = component.short_name;
26787             } else if (component.types.indexOf("country") >= 0) {
26788                 result.country = component.short_name;
26789             }
26790         }
26791         
26792         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26793         result.addressLine2 = "";
26794         return result;
26795     },
26796     
26797     setZoomLevel: function(zoom)
26798     {
26799         this.gMapContext.map.setZoom(zoom);
26800     },
26801     
26802     show: function()
26803     {
26804         if(!this.el){
26805             return;
26806         }
26807         
26808         this.el.show();
26809         
26810         this.resize();
26811         
26812         this.fireEvent('show', this);
26813     },
26814     
26815     hide: function()
26816     {
26817         if(!this.el){
26818             return;
26819         }
26820         
26821         this.el.hide();
26822         
26823         this.fireEvent('hide', this);
26824     }
26825     
26826 });
26827
26828 Roo.apply(Roo.bootstrap.LocationPicker, {
26829     
26830     OverlayView : function(map, options)
26831     {
26832         options = options || {};
26833         
26834         this.setMap(map);
26835     }
26836     
26837     
26838 });/*
26839  * - LGPL
26840  *
26841  * Alert
26842  * 
26843  */
26844
26845 /**
26846  * @class Roo.bootstrap.Alert
26847  * @extends Roo.bootstrap.Component
26848  * Bootstrap Alert class
26849  * @cfg {String} title The title of alert
26850  * @cfg {String} html The content of alert
26851  * @cfg {String} weight (  success | info | warning | danger )
26852  * @cfg {String} faicon font-awesomeicon
26853  * 
26854  * @constructor
26855  * Create a new alert
26856  * @param {Object} config The config object
26857  */
26858
26859
26860 Roo.bootstrap.Alert = function(config){
26861     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26862     
26863 };
26864
26865 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26866     
26867     title: '',
26868     html: '',
26869     weight: false,
26870     faicon: false,
26871     
26872     getAutoCreate : function()
26873     {
26874         
26875         var cfg = {
26876             tag : 'div',
26877             cls : 'alert',
26878             cn : [
26879                 {
26880                     tag : 'i',
26881                     cls : 'roo-alert-icon'
26882                     
26883                 },
26884                 {
26885                     tag : 'b',
26886                     cls : 'roo-alert-title',
26887                     html : this.title
26888                 },
26889                 {
26890                     tag : 'span',
26891                     cls : 'roo-alert-text',
26892                     html : this.html
26893                 }
26894             ]
26895         };
26896         
26897         if(this.faicon){
26898             cfg.cn[0].cls += ' fa ' + this.faicon;
26899         }
26900         
26901         if(this.weight){
26902             cfg.cls += ' alert-' + this.weight;
26903         }
26904         
26905         return cfg;
26906     },
26907     
26908     initEvents: function() 
26909     {
26910         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26911     },
26912     
26913     setTitle : function(str)
26914     {
26915         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26916     },
26917     
26918     setText : function(str)
26919     {
26920         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26921     },
26922     
26923     setWeight : function(weight)
26924     {
26925         if(this.weight){
26926             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26927         }
26928         
26929         this.weight = weight;
26930         
26931         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26932     },
26933     
26934     setIcon : function(icon)
26935     {
26936         if(this.faicon){
26937             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26938         }
26939         
26940         this.faicon = icon;
26941         
26942         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26943     },
26944     
26945     hide: function() 
26946     {
26947         this.el.hide();   
26948     },
26949     
26950     show: function() 
26951     {  
26952         this.el.show();   
26953     }
26954     
26955 });
26956
26957  
26958 /*
26959 * Licence: LGPL
26960 */
26961
26962 /**
26963  * @class Roo.bootstrap.UploadCropbox
26964  * @extends Roo.bootstrap.Component
26965  * Bootstrap UploadCropbox class
26966  * @cfg {String} emptyText show when image has been loaded
26967  * @cfg {String} rotateNotify show when image too small to rotate
26968  * @cfg {Number} errorTimeout default 3000
26969  * @cfg {Number} minWidth default 300
26970  * @cfg {Number} minHeight default 300
26971  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26972  * @cfg {Boolean} isDocument (true|false) default false
26973  * @cfg {String} url action url
26974  * @cfg {String} paramName default 'imageUpload'
26975  * @cfg {String} method default POST
26976  * @cfg {Boolean} loadMask (true|false) default true
26977  * @cfg {Boolean} loadingText default 'Loading...'
26978  * 
26979  * @constructor
26980  * Create a new UploadCropbox
26981  * @param {Object} config The config object
26982  */
26983
26984 Roo.bootstrap.UploadCropbox = function(config){
26985     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26986     
26987     this.addEvents({
26988         /**
26989          * @event beforeselectfile
26990          * Fire before select file
26991          * @param {Roo.bootstrap.UploadCropbox} this
26992          */
26993         "beforeselectfile" : true,
26994         /**
26995          * @event initial
26996          * Fire after initEvent
26997          * @param {Roo.bootstrap.UploadCropbox} this
26998          */
26999         "initial" : true,
27000         /**
27001          * @event crop
27002          * Fire after initEvent
27003          * @param {Roo.bootstrap.UploadCropbox} this
27004          * @param {String} data
27005          */
27006         "crop" : true,
27007         /**
27008          * @event prepare
27009          * Fire when preparing the file data
27010          * @param {Roo.bootstrap.UploadCropbox} this
27011          * @param {Object} file
27012          */
27013         "prepare" : true,
27014         /**
27015          * @event exception
27016          * Fire when get exception
27017          * @param {Roo.bootstrap.UploadCropbox} this
27018          * @param {XMLHttpRequest} xhr
27019          */
27020         "exception" : true,
27021         /**
27022          * @event beforeloadcanvas
27023          * Fire before load the canvas
27024          * @param {Roo.bootstrap.UploadCropbox} this
27025          * @param {String} src
27026          */
27027         "beforeloadcanvas" : true,
27028         /**
27029          * @event trash
27030          * Fire when trash image
27031          * @param {Roo.bootstrap.UploadCropbox} this
27032          */
27033         "trash" : true,
27034         /**
27035          * @event download
27036          * Fire when download the image
27037          * @param {Roo.bootstrap.UploadCropbox} this
27038          */
27039         "download" : true,
27040         /**
27041          * @event footerbuttonclick
27042          * Fire when footerbuttonclick
27043          * @param {Roo.bootstrap.UploadCropbox} this
27044          * @param {String} type
27045          */
27046         "footerbuttonclick" : true,
27047         /**
27048          * @event resize
27049          * Fire when resize
27050          * @param {Roo.bootstrap.UploadCropbox} this
27051          */
27052         "resize" : true,
27053         /**
27054          * @event rotate
27055          * Fire when rotate the image
27056          * @param {Roo.bootstrap.UploadCropbox} this
27057          * @param {String} pos
27058          */
27059         "rotate" : true,
27060         /**
27061          * @event inspect
27062          * Fire when inspect the file
27063          * @param {Roo.bootstrap.UploadCropbox} this
27064          * @param {Object} file
27065          */
27066         "inspect" : true,
27067         /**
27068          * @event upload
27069          * Fire when xhr upload the file
27070          * @param {Roo.bootstrap.UploadCropbox} this
27071          * @param {Object} data
27072          */
27073         "upload" : true,
27074         /**
27075          * @event arrange
27076          * Fire when arrange the file data
27077          * @param {Roo.bootstrap.UploadCropbox} this
27078          * @param {Object} formData
27079          */
27080         "arrange" : true
27081     });
27082     
27083     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27084 };
27085
27086 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27087     
27088     emptyText : 'Click to upload image',
27089     rotateNotify : 'Image is too small to rotate',
27090     errorTimeout : 3000,
27091     scale : 0,
27092     baseScale : 1,
27093     rotate : 0,
27094     dragable : false,
27095     pinching : false,
27096     mouseX : 0,
27097     mouseY : 0,
27098     cropData : false,
27099     minWidth : 300,
27100     minHeight : 300,
27101     file : false,
27102     exif : {},
27103     baseRotate : 1,
27104     cropType : 'image/jpeg',
27105     buttons : false,
27106     canvasLoaded : false,
27107     isDocument : false,
27108     method : 'POST',
27109     paramName : 'imageUpload',
27110     loadMask : true,
27111     loadingText : 'Loading...',
27112     maskEl : false,
27113     
27114     getAutoCreate : function()
27115     {
27116         var cfg = {
27117             tag : 'div',
27118             cls : 'roo-upload-cropbox',
27119             cn : [
27120                 {
27121                     tag : 'input',
27122                     cls : 'roo-upload-cropbox-selector',
27123                     type : 'file'
27124                 },
27125                 {
27126                     tag : 'div',
27127                     cls : 'roo-upload-cropbox-body',
27128                     style : 'cursor:pointer',
27129                     cn : [
27130                         {
27131                             tag : 'div',
27132                             cls : 'roo-upload-cropbox-preview'
27133                         },
27134                         {
27135                             tag : 'div',
27136                             cls : 'roo-upload-cropbox-thumb'
27137                         },
27138                         {
27139                             tag : 'div',
27140                             cls : 'roo-upload-cropbox-empty-notify',
27141                             html : this.emptyText
27142                         },
27143                         {
27144                             tag : 'div',
27145                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27146                             html : this.rotateNotify
27147                         }
27148                     ]
27149                 },
27150                 {
27151                     tag : 'div',
27152                     cls : 'roo-upload-cropbox-footer',
27153                     cn : {
27154                         tag : 'div',
27155                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27156                         cn : []
27157                     }
27158                 }
27159             ]
27160         };
27161         
27162         return cfg;
27163     },
27164     
27165     onRender : function(ct, position)
27166     {
27167         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27168         
27169         if (this.buttons.length) {
27170             
27171             Roo.each(this.buttons, function(bb) {
27172                 
27173                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27174                 
27175                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27176                 
27177             }, this);
27178         }
27179         
27180         if(this.loadMask){
27181             this.maskEl = this.el;
27182         }
27183     },
27184     
27185     initEvents : function()
27186     {
27187         this.urlAPI = (window.createObjectURL && window) || 
27188                                 (window.URL && URL.revokeObjectURL && URL) || 
27189                                 (window.webkitURL && webkitURL);
27190                         
27191         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27192         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27193         
27194         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27195         this.selectorEl.hide();
27196         
27197         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27198         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27199         
27200         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27201         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27202         this.thumbEl.hide();
27203         
27204         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27205         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27206         
27207         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27208         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27209         this.errorEl.hide();
27210         
27211         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27212         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27213         this.footerEl.hide();
27214         
27215         this.setThumbBoxSize();
27216         
27217         this.bind();
27218         
27219         this.resize();
27220         
27221         this.fireEvent('initial', this);
27222     },
27223
27224     bind : function()
27225     {
27226         var _this = this;
27227         
27228         window.addEventListener("resize", function() { _this.resize(); } );
27229         
27230         this.bodyEl.on('click', this.beforeSelectFile, this);
27231         
27232         if(Roo.isTouch){
27233             this.bodyEl.on('touchstart', this.onTouchStart, this);
27234             this.bodyEl.on('touchmove', this.onTouchMove, this);
27235             this.bodyEl.on('touchend', this.onTouchEnd, this);
27236         }
27237         
27238         if(!Roo.isTouch){
27239             this.bodyEl.on('mousedown', this.onMouseDown, this);
27240             this.bodyEl.on('mousemove', this.onMouseMove, this);
27241             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27242             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27243             Roo.get(document).on('mouseup', this.onMouseUp, this);
27244         }
27245         
27246         this.selectorEl.on('change', this.onFileSelected, this);
27247     },
27248     
27249     reset : function()
27250     {    
27251         this.scale = 0;
27252         this.baseScale = 1;
27253         this.rotate = 0;
27254         this.baseRotate = 1;
27255         this.dragable = false;
27256         this.pinching = false;
27257         this.mouseX = 0;
27258         this.mouseY = 0;
27259         this.cropData = false;
27260         this.notifyEl.dom.innerHTML = this.emptyText;
27261         
27262         this.selectorEl.dom.value = '';
27263         
27264     },
27265     
27266     resize : function()
27267     {
27268         if(this.fireEvent('resize', this) != false){
27269             this.setThumbBoxPosition();
27270             this.setCanvasPosition();
27271         }
27272     },
27273     
27274     onFooterButtonClick : function(e, el, o, type)
27275     {
27276         switch (type) {
27277             case 'rotate-left' :
27278                 this.onRotateLeft(e);
27279                 break;
27280             case 'rotate-right' :
27281                 this.onRotateRight(e);
27282                 break;
27283             case 'picture' :
27284                 this.beforeSelectFile(e);
27285                 break;
27286             case 'trash' :
27287                 this.trash(e);
27288                 break;
27289             case 'crop' :
27290                 this.crop(e);
27291                 break;
27292             case 'download' :
27293                 this.download(e);
27294                 break;
27295             default :
27296                 break;
27297         }
27298         
27299         this.fireEvent('footerbuttonclick', this, type);
27300     },
27301     
27302     beforeSelectFile : function(e)
27303     {
27304         e.preventDefault();
27305         
27306         if(this.fireEvent('beforeselectfile', this) != false){
27307             this.selectorEl.dom.click();
27308         }
27309     },
27310     
27311     onFileSelected : function(e)
27312     {
27313         e.preventDefault();
27314         
27315         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27316             return;
27317         }
27318         
27319         var file = this.selectorEl.dom.files[0];
27320         
27321         if(this.fireEvent('inspect', this, file) != false){
27322             this.prepare(file);
27323         }
27324         
27325     },
27326     
27327     trash : function(e)
27328     {
27329         this.fireEvent('trash', this);
27330     },
27331     
27332     download : function(e)
27333     {
27334         this.fireEvent('download', this);
27335     },
27336     
27337     loadCanvas : function(src)
27338     {   
27339         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27340             
27341             this.reset();
27342             
27343             this.imageEl = document.createElement('img');
27344             
27345             var _this = this;
27346             
27347             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27348             
27349             this.imageEl.src = src;
27350         }
27351     },
27352     
27353     onLoadCanvas : function()
27354     {   
27355         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27356         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27357         
27358         this.bodyEl.un('click', this.beforeSelectFile, this);
27359         
27360         this.notifyEl.hide();
27361         this.thumbEl.show();
27362         this.footerEl.show();
27363         
27364         this.baseRotateLevel();
27365         
27366         if(this.isDocument){
27367             this.setThumbBoxSize();
27368         }
27369         
27370         this.setThumbBoxPosition();
27371         
27372         this.baseScaleLevel();
27373         
27374         this.draw();
27375         
27376         this.resize();
27377         
27378         this.canvasLoaded = true;
27379         
27380         if(this.loadMask){
27381             this.maskEl.unmask();
27382         }
27383         
27384     },
27385     
27386     setCanvasPosition : function()
27387     {   
27388         if(!this.canvasEl){
27389             return;
27390         }
27391         
27392         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27393         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27394         
27395         this.previewEl.setLeft(pw);
27396         this.previewEl.setTop(ph);
27397         
27398     },
27399     
27400     onMouseDown : function(e)
27401     {   
27402         e.stopEvent();
27403         
27404         this.dragable = true;
27405         this.pinching = false;
27406         
27407         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27408             this.dragable = false;
27409             return;
27410         }
27411         
27412         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27413         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27414         
27415     },
27416     
27417     onMouseMove : function(e)
27418     {   
27419         e.stopEvent();
27420         
27421         if(!this.canvasLoaded){
27422             return;
27423         }
27424         
27425         if (!this.dragable){
27426             return;
27427         }
27428         
27429         var minX = Math.ceil(this.thumbEl.getLeft(true));
27430         var minY = Math.ceil(this.thumbEl.getTop(true));
27431         
27432         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27433         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27434         
27435         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27436         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27437         
27438         x = x - this.mouseX;
27439         y = y - this.mouseY;
27440         
27441         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27442         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27443         
27444         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27445         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27446         
27447         this.previewEl.setLeft(bgX);
27448         this.previewEl.setTop(bgY);
27449         
27450         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27451         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27452     },
27453     
27454     onMouseUp : function(e)
27455     {   
27456         e.stopEvent();
27457         
27458         this.dragable = false;
27459     },
27460     
27461     onMouseWheel : function(e)
27462     {   
27463         e.stopEvent();
27464         
27465         this.startScale = this.scale;
27466         
27467         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27468         
27469         if(!this.zoomable()){
27470             this.scale = this.startScale;
27471             return;
27472         }
27473         
27474         this.draw();
27475         
27476         return;
27477     },
27478     
27479     zoomable : function()
27480     {
27481         var minScale = this.thumbEl.getWidth() / this.minWidth;
27482         
27483         if(this.minWidth < this.minHeight){
27484             minScale = this.thumbEl.getHeight() / this.minHeight;
27485         }
27486         
27487         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27488         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27489         
27490         if(
27491                 this.isDocument &&
27492                 (this.rotate == 0 || this.rotate == 180) && 
27493                 (
27494                     width > this.imageEl.OriginWidth || 
27495                     height > this.imageEl.OriginHeight ||
27496                     (width < this.minWidth && height < this.minHeight)
27497                 )
27498         ){
27499             return false;
27500         }
27501         
27502         if(
27503                 this.isDocument &&
27504                 (this.rotate == 90 || this.rotate == 270) && 
27505                 (
27506                     width > this.imageEl.OriginWidth || 
27507                     height > this.imageEl.OriginHeight ||
27508                     (width < this.minHeight && height < this.minWidth)
27509                 )
27510         ){
27511             return false;
27512         }
27513         
27514         if(
27515                 !this.isDocument &&
27516                 (this.rotate == 0 || this.rotate == 180) && 
27517                 (
27518                     width < this.minWidth || 
27519                     width > this.imageEl.OriginWidth || 
27520                     height < this.minHeight || 
27521                     height > this.imageEl.OriginHeight
27522                 )
27523         ){
27524             return false;
27525         }
27526         
27527         if(
27528                 !this.isDocument &&
27529                 (this.rotate == 90 || this.rotate == 270) && 
27530                 (
27531                     width < this.minHeight || 
27532                     width > this.imageEl.OriginWidth || 
27533                     height < this.minWidth || 
27534                     height > this.imageEl.OriginHeight
27535                 )
27536         ){
27537             return false;
27538         }
27539         
27540         return true;
27541         
27542     },
27543     
27544     onRotateLeft : function(e)
27545     {   
27546         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27547             
27548             var minScale = this.thumbEl.getWidth() / this.minWidth;
27549             
27550             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27551             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27552             
27553             this.startScale = this.scale;
27554             
27555             while (this.getScaleLevel() < minScale){
27556             
27557                 this.scale = this.scale + 1;
27558                 
27559                 if(!this.zoomable()){
27560                     break;
27561                 }
27562                 
27563                 if(
27564                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27565                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27566                 ){
27567                     continue;
27568                 }
27569                 
27570                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27571
27572                 this.draw();
27573                 
27574                 return;
27575             }
27576             
27577             this.scale = this.startScale;
27578             
27579             this.onRotateFail();
27580             
27581             return false;
27582         }
27583         
27584         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27585
27586         if(this.isDocument){
27587             this.setThumbBoxSize();
27588             this.setThumbBoxPosition();
27589             this.setCanvasPosition();
27590         }
27591         
27592         this.draw();
27593         
27594         this.fireEvent('rotate', this, 'left');
27595         
27596     },
27597     
27598     onRotateRight : function(e)
27599     {
27600         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27601             
27602             var minScale = this.thumbEl.getWidth() / this.minWidth;
27603         
27604             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27605             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27606             
27607             this.startScale = this.scale;
27608             
27609             while (this.getScaleLevel() < minScale){
27610             
27611                 this.scale = this.scale + 1;
27612                 
27613                 if(!this.zoomable()){
27614                     break;
27615                 }
27616                 
27617                 if(
27618                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27619                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27620                 ){
27621                     continue;
27622                 }
27623                 
27624                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27625
27626                 this.draw();
27627                 
27628                 return;
27629             }
27630             
27631             this.scale = this.startScale;
27632             
27633             this.onRotateFail();
27634             
27635             return false;
27636         }
27637         
27638         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27639
27640         if(this.isDocument){
27641             this.setThumbBoxSize();
27642             this.setThumbBoxPosition();
27643             this.setCanvasPosition();
27644         }
27645         
27646         this.draw();
27647         
27648         this.fireEvent('rotate', this, 'right');
27649     },
27650     
27651     onRotateFail : function()
27652     {
27653         this.errorEl.show(true);
27654         
27655         var _this = this;
27656         
27657         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27658     },
27659     
27660     draw : function()
27661     {
27662         this.previewEl.dom.innerHTML = '';
27663         
27664         var canvasEl = document.createElement("canvas");
27665         
27666         var contextEl = canvasEl.getContext("2d");
27667         
27668         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27669         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27670         var center = this.imageEl.OriginWidth / 2;
27671         
27672         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27673             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27674             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27675             center = this.imageEl.OriginHeight / 2;
27676         }
27677         
27678         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27679         
27680         contextEl.translate(center, center);
27681         contextEl.rotate(this.rotate * Math.PI / 180);
27682
27683         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27684         
27685         this.canvasEl = document.createElement("canvas");
27686         
27687         this.contextEl = this.canvasEl.getContext("2d");
27688         
27689         switch (this.rotate) {
27690             case 0 :
27691                 
27692                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27693                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27694                 
27695                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27696                 
27697                 break;
27698             case 90 : 
27699                 
27700                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27701                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27702                 
27703                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27704                     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);
27705                     break;
27706                 }
27707                 
27708                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27709                 
27710                 break;
27711             case 180 :
27712                 
27713                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27714                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27715                 
27716                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27717                     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);
27718                     break;
27719                 }
27720                 
27721                 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);
27722                 
27723                 break;
27724             case 270 :
27725                 
27726                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27727                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27728         
27729                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27730                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27731                     break;
27732                 }
27733                 
27734                 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);
27735                 
27736                 break;
27737             default : 
27738                 break;
27739         }
27740         
27741         this.previewEl.appendChild(this.canvasEl);
27742         
27743         this.setCanvasPosition();
27744     },
27745     
27746     crop : function()
27747     {
27748         if(!this.canvasLoaded){
27749             return;
27750         }
27751         
27752         var imageCanvas = document.createElement("canvas");
27753         
27754         var imageContext = imageCanvas.getContext("2d");
27755         
27756         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27757         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27758         
27759         var center = imageCanvas.width / 2;
27760         
27761         imageContext.translate(center, center);
27762         
27763         imageContext.rotate(this.rotate * Math.PI / 180);
27764         
27765         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27766         
27767         var canvas = document.createElement("canvas");
27768         
27769         var context = canvas.getContext("2d");
27770                 
27771         canvas.width = this.minWidth;
27772         canvas.height = this.minHeight;
27773
27774         switch (this.rotate) {
27775             case 0 :
27776                 
27777                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27778                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27779                 
27780                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27781                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27782                 
27783                 var targetWidth = this.minWidth - 2 * x;
27784                 var targetHeight = this.minHeight - 2 * y;
27785                 
27786                 var scale = 1;
27787                 
27788                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27789                     scale = targetWidth / width;
27790                 }
27791                 
27792                 if(x > 0 && y == 0){
27793                     scale = targetHeight / height;
27794                 }
27795                 
27796                 if(x > 0 && y > 0){
27797                     scale = targetWidth / width;
27798                     
27799                     if(width < height){
27800                         scale = targetHeight / height;
27801                     }
27802                 }
27803                 
27804                 context.scale(scale, scale);
27805                 
27806                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27807                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27808
27809                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27810                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27811
27812                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27813                 
27814                 break;
27815             case 90 : 
27816                 
27817                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27818                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27819                 
27820                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27821                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27822                 
27823                 var targetWidth = this.minWidth - 2 * x;
27824                 var targetHeight = this.minHeight - 2 * y;
27825                 
27826                 var scale = 1;
27827                 
27828                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27829                     scale = targetWidth / width;
27830                 }
27831                 
27832                 if(x > 0 && y == 0){
27833                     scale = targetHeight / height;
27834                 }
27835                 
27836                 if(x > 0 && y > 0){
27837                     scale = targetWidth / width;
27838                     
27839                     if(width < height){
27840                         scale = targetHeight / height;
27841                     }
27842                 }
27843                 
27844                 context.scale(scale, scale);
27845                 
27846                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27847                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27848
27849                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27850                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27851                 
27852                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27853                 
27854                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27855                 
27856                 break;
27857             case 180 :
27858                 
27859                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27860                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27861                 
27862                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27863                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27864                 
27865                 var targetWidth = this.minWidth - 2 * x;
27866                 var targetHeight = this.minHeight - 2 * y;
27867                 
27868                 var scale = 1;
27869                 
27870                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27871                     scale = targetWidth / width;
27872                 }
27873                 
27874                 if(x > 0 && y == 0){
27875                     scale = targetHeight / height;
27876                 }
27877                 
27878                 if(x > 0 && y > 0){
27879                     scale = targetWidth / width;
27880                     
27881                     if(width < height){
27882                         scale = targetHeight / height;
27883                     }
27884                 }
27885                 
27886                 context.scale(scale, scale);
27887                 
27888                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27889                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27890
27891                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27892                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27893
27894                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27895                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27896                 
27897                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27898                 
27899                 break;
27900             case 270 :
27901                 
27902                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27903                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27904                 
27905                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27906                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27907                 
27908                 var targetWidth = this.minWidth - 2 * x;
27909                 var targetHeight = this.minHeight - 2 * y;
27910                 
27911                 var scale = 1;
27912                 
27913                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27914                     scale = targetWidth / width;
27915                 }
27916                 
27917                 if(x > 0 && y == 0){
27918                     scale = targetHeight / height;
27919                 }
27920                 
27921                 if(x > 0 && y > 0){
27922                     scale = targetWidth / width;
27923                     
27924                     if(width < height){
27925                         scale = targetHeight / height;
27926                     }
27927                 }
27928                 
27929                 context.scale(scale, scale);
27930                 
27931                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27932                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27933
27934                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27935                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27936                 
27937                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27938                 
27939                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27940                 
27941                 break;
27942             default : 
27943                 break;
27944         }
27945         
27946         this.cropData = canvas.toDataURL(this.cropType);
27947         
27948         if(this.fireEvent('crop', this, this.cropData) !== false){
27949             this.process(this.file, this.cropData);
27950         }
27951         
27952         return;
27953         
27954     },
27955     
27956     setThumbBoxSize : function()
27957     {
27958         var width, height;
27959         
27960         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27961             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27962             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27963             
27964             this.minWidth = width;
27965             this.minHeight = height;
27966             
27967             if(this.rotate == 90 || this.rotate == 270){
27968                 this.minWidth = height;
27969                 this.minHeight = width;
27970             }
27971         }
27972         
27973         height = 300;
27974         width = Math.ceil(this.minWidth * height / this.minHeight);
27975         
27976         if(this.minWidth > this.minHeight){
27977             width = 300;
27978             height = Math.ceil(this.minHeight * width / this.minWidth);
27979         }
27980         
27981         this.thumbEl.setStyle({
27982             width : width + 'px',
27983             height : height + 'px'
27984         });
27985
27986         return;
27987             
27988     },
27989     
27990     setThumbBoxPosition : function()
27991     {
27992         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27993         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27994         
27995         this.thumbEl.setLeft(x);
27996         this.thumbEl.setTop(y);
27997         
27998     },
27999     
28000     baseRotateLevel : function()
28001     {
28002         this.baseRotate = 1;
28003         
28004         if(
28005                 typeof(this.exif) != 'undefined' &&
28006                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28007                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28008         ){
28009             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28010         }
28011         
28012         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28013         
28014     },
28015     
28016     baseScaleLevel : function()
28017     {
28018         var width, height;
28019         
28020         if(this.isDocument){
28021             
28022             if(this.baseRotate == 6 || this.baseRotate == 8){
28023             
28024                 height = this.thumbEl.getHeight();
28025                 this.baseScale = height / this.imageEl.OriginWidth;
28026
28027                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28028                     width = this.thumbEl.getWidth();
28029                     this.baseScale = width / this.imageEl.OriginHeight;
28030                 }
28031
28032                 return;
28033             }
28034
28035             height = this.thumbEl.getHeight();
28036             this.baseScale = height / this.imageEl.OriginHeight;
28037
28038             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28039                 width = this.thumbEl.getWidth();
28040                 this.baseScale = width / this.imageEl.OriginWidth;
28041             }
28042
28043             return;
28044         }
28045         
28046         if(this.baseRotate == 6 || this.baseRotate == 8){
28047             
28048             width = this.thumbEl.getHeight();
28049             this.baseScale = width / this.imageEl.OriginHeight;
28050             
28051             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28052                 height = this.thumbEl.getWidth();
28053                 this.baseScale = height / this.imageEl.OriginHeight;
28054             }
28055             
28056             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28057                 height = this.thumbEl.getWidth();
28058                 this.baseScale = height / this.imageEl.OriginHeight;
28059                 
28060                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28061                     width = this.thumbEl.getHeight();
28062                     this.baseScale = width / this.imageEl.OriginWidth;
28063                 }
28064             }
28065             
28066             return;
28067         }
28068         
28069         width = this.thumbEl.getWidth();
28070         this.baseScale = width / this.imageEl.OriginWidth;
28071         
28072         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28073             height = this.thumbEl.getHeight();
28074             this.baseScale = height / this.imageEl.OriginHeight;
28075         }
28076         
28077         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28078             
28079             height = this.thumbEl.getHeight();
28080             this.baseScale = height / this.imageEl.OriginHeight;
28081             
28082             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28083                 width = this.thumbEl.getWidth();
28084                 this.baseScale = width / this.imageEl.OriginWidth;
28085             }
28086             
28087         }
28088         
28089         return;
28090     },
28091     
28092     getScaleLevel : function()
28093     {
28094         return this.baseScale * Math.pow(1.1, this.scale);
28095     },
28096     
28097     onTouchStart : function(e)
28098     {
28099         if(!this.canvasLoaded){
28100             this.beforeSelectFile(e);
28101             return;
28102         }
28103         
28104         var touches = e.browserEvent.touches;
28105         
28106         if(!touches){
28107             return;
28108         }
28109         
28110         if(touches.length == 1){
28111             this.onMouseDown(e);
28112             return;
28113         }
28114         
28115         if(touches.length != 2){
28116             return;
28117         }
28118         
28119         var coords = [];
28120         
28121         for(var i = 0, finger; finger = touches[i]; i++){
28122             coords.push(finger.pageX, finger.pageY);
28123         }
28124         
28125         var x = Math.pow(coords[0] - coords[2], 2);
28126         var y = Math.pow(coords[1] - coords[3], 2);
28127         
28128         this.startDistance = Math.sqrt(x + y);
28129         
28130         this.startScale = this.scale;
28131         
28132         this.pinching = true;
28133         this.dragable = false;
28134         
28135     },
28136     
28137     onTouchMove : function(e)
28138     {
28139         if(!this.pinching && !this.dragable){
28140             return;
28141         }
28142         
28143         var touches = e.browserEvent.touches;
28144         
28145         if(!touches){
28146             return;
28147         }
28148         
28149         if(this.dragable){
28150             this.onMouseMove(e);
28151             return;
28152         }
28153         
28154         var coords = [];
28155         
28156         for(var i = 0, finger; finger = touches[i]; i++){
28157             coords.push(finger.pageX, finger.pageY);
28158         }
28159         
28160         var x = Math.pow(coords[0] - coords[2], 2);
28161         var y = Math.pow(coords[1] - coords[3], 2);
28162         
28163         this.endDistance = Math.sqrt(x + y);
28164         
28165         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28166         
28167         if(!this.zoomable()){
28168             this.scale = this.startScale;
28169             return;
28170         }
28171         
28172         this.draw();
28173         
28174     },
28175     
28176     onTouchEnd : function(e)
28177     {
28178         this.pinching = false;
28179         this.dragable = false;
28180         
28181     },
28182     
28183     process : function(file, crop)
28184     {
28185         if(this.loadMask){
28186             this.maskEl.mask(this.loadingText);
28187         }
28188         
28189         this.xhr = new XMLHttpRequest();
28190         
28191         file.xhr = this.xhr;
28192
28193         this.xhr.open(this.method, this.url, true);
28194         
28195         var headers = {
28196             "Accept": "application/json",
28197             "Cache-Control": "no-cache",
28198             "X-Requested-With": "XMLHttpRequest"
28199         };
28200         
28201         for (var headerName in headers) {
28202             var headerValue = headers[headerName];
28203             if (headerValue) {
28204                 this.xhr.setRequestHeader(headerName, headerValue);
28205             }
28206         }
28207         
28208         var _this = this;
28209         
28210         this.xhr.onload = function()
28211         {
28212             _this.xhrOnLoad(_this.xhr);
28213         }
28214         
28215         this.xhr.onerror = function()
28216         {
28217             _this.xhrOnError(_this.xhr);
28218         }
28219         
28220         var formData = new FormData();
28221
28222         formData.append('returnHTML', 'NO');
28223         
28224         if(crop){
28225             formData.append('crop', crop);
28226         }
28227         
28228         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28229             formData.append(this.paramName, file, file.name);
28230         }
28231         
28232         if(typeof(file.filename) != 'undefined'){
28233             formData.append('filename', file.filename);
28234         }
28235         
28236         if(typeof(file.mimetype) != 'undefined'){
28237             formData.append('mimetype', file.mimetype);
28238         }
28239         
28240         if(this.fireEvent('arrange', this, formData) != false){
28241             this.xhr.send(formData);
28242         };
28243     },
28244     
28245     xhrOnLoad : function(xhr)
28246     {
28247         if(this.loadMask){
28248             this.maskEl.unmask();
28249         }
28250         
28251         if (xhr.readyState !== 4) {
28252             this.fireEvent('exception', this, xhr);
28253             return;
28254         }
28255
28256         var response = Roo.decode(xhr.responseText);
28257         
28258         if(!response.success){
28259             this.fireEvent('exception', this, xhr);
28260             return;
28261         }
28262         
28263         var response = Roo.decode(xhr.responseText);
28264         
28265         this.fireEvent('upload', this, response);
28266         
28267     },
28268     
28269     xhrOnError : function()
28270     {
28271         if(this.loadMask){
28272             this.maskEl.unmask();
28273         }
28274         
28275         Roo.log('xhr on error');
28276         
28277         var response = Roo.decode(xhr.responseText);
28278           
28279         Roo.log(response);
28280         
28281     },
28282     
28283     prepare : function(file)
28284     {   
28285         if(this.loadMask){
28286             this.maskEl.mask(this.loadingText);
28287         }
28288         
28289         this.file = false;
28290         this.exif = {};
28291         
28292         if(typeof(file) === 'string'){
28293             this.loadCanvas(file);
28294             return;
28295         }
28296         
28297         if(!file || !this.urlAPI){
28298             return;
28299         }
28300         
28301         this.file = file;
28302         this.cropType = file.type;
28303         
28304         var _this = this;
28305         
28306         if(this.fireEvent('prepare', this, this.file) != false){
28307             
28308             var reader = new FileReader();
28309             
28310             reader.onload = function (e) {
28311                 if (e.target.error) {
28312                     Roo.log(e.target.error);
28313                     return;
28314                 }
28315                 
28316                 var buffer = e.target.result,
28317                     dataView = new DataView(buffer),
28318                     offset = 2,
28319                     maxOffset = dataView.byteLength - 4,
28320                     markerBytes,
28321                     markerLength;
28322                 
28323                 if (dataView.getUint16(0) === 0xffd8) {
28324                     while (offset < maxOffset) {
28325                         markerBytes = dataView.getUint16(offset);
28326                         
28327                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28328                             markerLength = dataView.getUint16(offset + 2) + 2;
28329                             if (offset + markerLength > dataView.byteLength) {
28330                                 Roo.log('Invalid meta data: Invalid segment size.');
28331                                 break;
28332                             }
28333                             
28334                             if(markerBytes == 0xffe1){
28335                                 _this.parseExifData(
28336                                     dataView,
28337                                     offset,
28338                                     markerLength
28339                                 );
28340                             }
28341                             
28342                             offset += markerLength;
28343                             
28344                             continue;
28345                         }
28346                         
28347                         break;
28348                     }
28349                     
28350                 }
28351                 
28352                 var url = _this.urlAPI.createObjectURL(_this.file);
28353                 
28354                 _this.loadCanvas(url);
28355                 
28356                 return;
28357             }
28358             
28359             reader.readAsArrayBuffer(this.file);
28360             
28361         }
28362         
28363     },
28364     
28365     parseExifData : function(dataView, offset, length)
28366     {
28367         var tiffOffset = offset + 10,
28368             littleEndian,
28369             dirOffset;
28370     
28371         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28372             // No Exif data, might be XMP data instead
28373             return;
28374         }
28375         
28376         // Check for the ASCII code for "Exif" (0x45786966):
28377         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28378             // No Exif data, might be XMP data instead
28379             return;
28380         }
28381         if (tiffOffset + 8 > dataView.byteLength) {
28382             Roo.log('Invalid Exif data: Invalid segment size.');
28383             return;
28384         }
28385         // Check for the two null bytes:
28386         if (dataView.getUint16(offset + 8) !== 0x0000) {
28387             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28388             return;
28389         }
28390         // Check the byte alignment:
28391         switch (dataView.getUint16(tiffOffset)) {
28392         case 0x4949:
28393             littleEndian = true;
28394             break;
28395         case 0x4D4D:
28396             littleEndian = false;
28397             break;
28398         default:
28399             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28400             return;
28401         }
28402         // Check for the TIFF tag marker (0x002A):
28403         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28404             Roo.log('Invalid Exif data: Missing TIFF marker.');
28405             return;
28406         }
28407         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28408         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28409         
28410         this.parseExifTags(
28411             dataView,
28412             tiffOffset,
28413             tiffOffset + dirOffset,
28414             littleEndian
28415         );
28416     },
28417     
28418     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28419     {
28420         var tagsNumber,
28421             dirEndOffset,
28422             i;
28423         if (dirOffset + 6 > dataView.byteLength) {
28424             Roo.log('Invalid Exif data: Invalid directory offset.');
28425             return;
28426         }
28427         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28428         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28429         if (dirEndOffset + 4 > dataView.byteLength) {
28430             Roo.log('Invalid Exif data: Invalid directory size.');
28431             return;
28432         }
28433         for (i = 0; i < tagsNumber; i += 1) {
28434             this.parseExifTag(
28435                 dataView,
28436                 tiffOffset,
28437                 dirOffset + 2 + 12 * i, // tag offset
28438                 littleEndian
28439             );
28440         }
28441         // Return the offset to the next directory:
28442         return dataView.getUint32(dirEndOffset, littleEndian);
28443     },
28444     
28445     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28446     {
28447         var tag = dataView.getUint16(offset, littleEndian);
28448         
28449         this.exif[tag] = this.getExifValue(
28450             dataView,
28451             tiffOffset,
28452             offset,
28453             dataView.getUint16(offset + 2, littleEndian), // tag type
28454             dataView.getUint32(offset + 4, littleEndian), // tag length
28455             littleEndian
28456         );
28457     },
28458     
28459     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28460     {
28461         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28462             tagSize,
28463             dataOffset,
28464             values,
28465             i,
28466             str,
28467             c;
28468     
28469         if (!tagType) {
28470             Roo.log('Invalid Exif data: Invalid tag type.');
28471             return;
28472         }
28473         
28474         tagSize = tagType.size * length;
28475         // Determine if the value is contained in the dataOffset bytes,
28476         // or if the value at the dataOffset is a pointer to the actual data:
28477         dataOffset = tagSize > 4 ?
28478                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28479         if (dataOffset + tagSize > dataView.byteLength) {
28480             Roo.log('Invalid Exif data: Invalid data offset.');
28481             return;
28482         }
28483         if (length === 1) {
28484             return tagType.getValue(dataView, dataOffset, littleEndian);
28485         }
28486         values = [];
28487         for (i = 0; i < length; i += 1) {
28488             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28489         }
28490         
28491         if (tagType.ascii) {
28492             str = '';
28493             // Concatenate the chars:
28494             for (i = 0; i < values.length; i += 1) {
28495                 c = values[i];
28496                 // Ignore the terminating NULL byte(s):
28497                 if (c === '\u0000') {
28498                     break;
28499                 }
28500                 str += c;
28501             }
28502             return str;
28503         }
28504         return values;
28505     }
28506     
28507 });
28508
28509 Roo.apply(Roo.bootstrap.UploadCropbox, {
28510     tags : {
28511         'Orientation': 0x0112
28512     },
28513     
28514     Orientation: {
28515             1: 0, //'top-left',
28516 //            2: 'top-right',
28517             3: 180, //'bottom-right',
28518 //            4: 'bottom-left',
28519 //            5: 'left-top',
28520             6: 90, //'right-top',
28521 //            7: 'right-bottom',
28522             8: 270 //'left-bottom'
28523     },
28524     
28525     exifTagTypes : {
28526         // byte, 8-bit unsigned int:
28527         1: {
28528             getValue: function (dataView, dataOffset) {
28529                 return dataView.getUint8(dataOffset);
28530             },
28531             size: 1
28532         },
28533         // ascii, 8-bit byte:
28534         2: {
28535             getValue: function (dataView, dataOffset) {
28536                 return String.fromCharCode(dataView.getUint8(dataOffset));
28537             },
28538             size: 1,
28539             ascii: true
28540         },
28541         // short, 16 bit int:
28542         3: {
28543             getValue: function (dataView, dataOffset, littleEndian) {
28544                 return dataView.getUint16(dataOffset, littleEndian);
28545             },
28546             size: 2
28547         },
28548         // long, 32 bit int:
28549         4: {
28550             getValue: function (dataView, dataOffset, littleEndian) {
28551                 return dataView.getUint32(dataOffset, littleEndian);
28552             },
28553             size: 4
28554         },
28555         // rational = two long values, first is numerator, second is denominator:
28556         5: {
28557             getValue: function (dataView, dataOffset, littleEndian) {
28558                 return dataView.getUint32(dataOffset, littleEndian) /
28559                     dataView.getUint32(dataOffset + 4, littleEndian);
28560             },
28561             size: 8
28562         },
28563         // slong, 32 bit signed int:
28564         9: {
28565             getValue: function (dataView, dataOffset, littleEndian) {
28566                 return dataView.getInt32(dataOffset, littleEndian);
28567             },
28568             size: 4
28569         },
28570         // srational, two slongs, first is numerator, second is denominator:
28571         10: {
28572             getValue: function (dataView, dataOffset, littleEndian) {
28573                 return dataView.getInt32(dataOffset, littleEndian) /
28574                     dataView.getInt32(dataOffset + 4, littleEndian);
28575             },
28576             size: 8
28577         }
28578     },
28579     
28580     footer : {
28581         STANDARD : [
28582             {
28583                 tag : 'div',
28584                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28585                 action : 'rotate-left',
28586                 cn : [
28587                     {
28588                         tag : 'button',
28589                         cls : 'btn btn-default',
28590                         html : '<i class="fa fa-undo"></i>'
28591                     }
28592                 ]
28593             },
28594             {
28595                 tag : 'div',
28596                 cls : 'btn-group roo-upload-cropbox-picture',
28597                 action : 'picture',
28598                 cn : [
28599                     {
28600                         tag : 'button',
28601                         cls : 'btn btn-default',
28602                         html : '<i class="fa fa-picture-o"></i>'
28603                     }
28604                 ]
28605             },
28606             {
28607                 tag : 'div',
28608                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28609                 action : 'rotate-right',
28610                 cn : [
28611                     {
28612                         tag : 'button',
28613                         cls : 'btn btn-default',
28614                         html : '<i class="fa fa-repeat"></i>'
28615                     }
28616                 ]
28617             }
28618         ],
28619         DOCUMENT : [
28620             {
28621                 tag : 'div',
28622                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28623                 action : 'rotate-left',
28624                 cn : [
28625                     {
28626                         tag : 'button',
28627                         cls : 'btn btn-default',
28628                         html : '<i class="fa fa-undo"></i>'
28629                     }
28630                 ]
28631             },
28632             {
28633                 tag : 'div',
28634                 cls : 'btn-group roo-upload-cropbox-download',
28635                 action : 'download',
28636                 cn : [
28637                     {
28638                         tag : 'button',
28639                         cls : 'btn btn-default',
28640                         html : '<i class="fa fa-download"></i>'
28641                     }
28642                 ]
28643             },
28644             {
28645                 tag : 'div',
28646                 cls : 'btn-group roo-upload-cropbox-crop',
28647                 action : 'crop',
28648                 cn : [
28649                     {
28650                         tag : 'button',
28651                         cls : 'btn btn-default',
28652                         html : '<i class="fa fa-crop"></i>'
28653                     }
28654                 ]
28655             },
28656             {
28657                 tag : 'div',
28658                 cls : 'btn-group roo-upload-cropbox-trash',
28659                 action : 'trash',
28660                 cn : [
28661                     {
28662                         tag : 'button',
28663                         cls : 'btn btn-default',
28664                         html : '<i class="fa fa-trash"></i>'
28665                     }
28666                 ]
28667             },
28668             {
28669                 tag : 'div',
28670                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28671                 action : 'rotate-right',
28672                 cn : [
28673                     {
28674                         tag : 'button',
28675                         cls : 'btn btn-default',
28676                         html : '<i class="fa fa-repeat"></i>'
28677                     }
28678                 ]
28679             }
28680         ],
28681         ROTATOR : [
28682             {
28683                 tag : 'div',
28684                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28685                 action : 'rotate-left',
28686                 cn : [
28687                     {
28688                         tag : 'button',
28689                         cls : 'btn btn-default',
28690                         html : '<i class="fa fa-undo"></i>'
28691                     }
28692                 ]
28693             },
28694             {
28695                 tag : 'div',
28696                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28697                 action : 'rotate-right',
28698                 cn : [
28699                     {
28700                         tag : 'button',
28701                         cls : 'btn btn-default',
28702                         html : '<i class="fa fa-repeat"></i>'
28703                     }
28704                 ]
28705             }
28706         ]
28707     }
28708 });
28709
28710 /*
28711 * Licence: LGPL
28712 */
28713
28714 /**
28715  * @class Roo.bootstrap.DocumentManager
28716  * @extends Roo.bootstrap.Component
28717  * Bootstrap DocumentManager class
28718  * @cfg {String} paramName default 'imageUpload'
28719  * @cfg {String} toolTipName default 'filename'
28720  * @cfg {String} method default POST
28721  * @cfg {String} url action url
28722  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28723  * @cfg {Boolean} multiple multiple upload default true
28724  * @cfg {Number} thumbSize default 300
28725  * @cfg {String} fieldLabel
28726  * @cfg {Number} labelWidth default 4
28727  * @cfg {String} labelAlign (left|top) default left
28728  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28729 * @cfg {Number} labellg set the width of label (1-12)
28730  * @cfg {Number} labelmd set the width of label (1-12)
28731  * @cfg {Number} labelsm set the width of label (1-12)
28732  * @cfg {Number} labelxs set the width of label (1-12)
28733  * 
28734  * @constructor
28735  * Create a new DocumentManager
28736  * @param {Object} config The config object
28737  */
28738
28739 Roo.bootstrap.DocumentManager = function(config){
28740     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28741     
28742     this.files = [];
28743     this.delegates = [];
28744     
28745     this.addEvents({
28746         /**
28747          * @event initial
28748          * Fire when initial the DocumentManager
28749          * @param {Roo.bootstrap.DocumentManager} this
28750          */
28751         "initial" : true,
28752         /**
28753          * @event inspect
28754          * inspect selected file
28755          * @param {Roo.bootstrap.DocumentManager} this
28756          * @param {File} file
28757          */
28758         "inspect" : true,
28759         /**
28760          * @event exception
28761          * Fire when xhr load exception
28762          * @param {Roo.bootstrap.DocumentManager} this
28763          * @param {XMLHttpRequest} xhr
28764          */
28765         "exception" : true,
28766         /**
28767          * @event afterupload
28768          * Fire when xhr load exception
28769          * @param {Roo.bootstrap.DocumentManager} this
28770          * @param {XMLHttpRequest} xhr
28771          */
28772         "afterupload" : true,
28773         /**
28774          * @event prepare
28775          * prepare the form data
28776          * @param {Roo.bootstrap.DocumentManager} this
28777          * @param {Object} formData
28778          */
28779         "prepare" : true,
28780         /**
28781          * @event remove
28782          * Fire when remove the file
28783          * @param {Roo.bootstrap.DocumentManager} this
28784          * @param {Object} file
28785          */
28786         "remove" : true,
28787         /**
28788          * @event refresh
28789          * Fire after refresh the file
28790          * @param {Roo.bootstrap.DocumentManager} this
28791          */
28792         "refresh" : true,
28793         /**
28794          * @event click
28795          * Fire after click the image
28796          * @param {Roo.bootstrap.DocumentManager} this
28797          * @param {Object} file
28798          */
28799         "click" : true,
28800         /**
28801          * @event edit
28802          * Fire when upload a image and editable set to true
28803          * @param {Roo.bootstrap.DocumentManager} this
28804          * @param {Object} file
28805          */
28806         "edit" : true,
28807         /**
28808          * @event beforeselectfile
28809          * Fire before select file
28810          * @param {Roo.bootstrap.DocumentManager} this
28811          */
28812         "beforeselectfile" : true,
28813         /**
28814          * @event process
28815          * Fire before process file
28816          * @param {Roo.bootstrap.DocumentManager} this
28817          * @param {Object} file
28818          */
28819         "process" : true,
28820         /**
28821          * @event previewrendered
28822          * Fire when preview rendered
28823          * @param {Roo.bootstrap.DocumentManager} this
28824          * @param {Object} file
28825          */
28826         "previewrendered" : true,
28827         /**
28828          */
28829         "previewResize" : true
28830         
28831     });
28832 };
28833
28834 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28835     
28836     boxes : 0,
28837     inputName : '',
28838     thumbSize : 300,
28839     multiple : true,
28840     files : false,
28841     method : 'POST',
28842     url : '',
28843     paramName : 'imageUpload',
28844     toolTipName : 'filename',
28845     fieldLabel : '',
28846     labelWidth : 4,
28847     labelAlign : 'left',
28848     editable : true,
28849     delegates : false,
28850     xhr : false, 
28851     
28852     labellg : 0,
28853     labelmd : 0,
28854     labelsm : 0,
28855     labelxs : 0,
28856     
28857     getAutoCreate : function()
28858     {   
28859         var managerWidget = {
28860             tag : 'div',
28861             cls : 'roo-document-manager',
28862             cn : [
28863                 {
28864                     tag : 'input',
28865                     cls : 'roo-document-manager-selector',
28866                     type : 'file'
28867                 },
28868                 {
28869                     tag : 'div',
28870                     cls : 'roo-document-manager-uploader',
28871                     cn : [
28872                         {
28873                             tag : 'div',
28874                             cls : 'roo-document-manager-upload-btn',
28875                             html : '<i class="fa fa-plus"></i>'
28876                         }
28877                     ]
28878                     
28879                 }
28880             ]
28881         };
28882         
28883         var content = [
28884             {
28885                 tag : 'div',
28886                 cls : 'column col-md-12',
28887                 cn : managerWidget
28888             }
28889         ];
28890         
28891         if(this.fieldLabel.length){
28892             
28893             content = [
28894                 {
28895                     tag : 'div',
28896                     cls : 'column col-md-12',
28897                     html : this.fieldLabel
28898                 },
28899                 {
28900                     tag : 'div',
28901                     cls : 'column col-md-12',
28902                     cn : managerWidget
28903                 }
28904             ];
28905
28906             if(this.labelAlign == 'left'){
28907                 content = [
28908                     {
28909                         tag : 'div',
28910                         cls : 'column',
28911                         html : this.fieldLabel
28912                     },
28913                     {
28914                         tag : 'div',
28915                         cls : 'column',
28916                         cn : managerWidget
28917                     }
28918                 ];
28919                 
28920                 if(this.labelWidth > 12){
28921                     content[0].style = "width: " + this.labelWidth + 'px';
28922                 }
28923
28924                 if(this.labelWidth < 13 && this.labelmd == 0){
28925                     this.labelmd = this.labelWidth;
28926                 }
28927
28928                 if(this.labellg > 0){
28929                     content[0].cls += ' col-lg-' + this.labellg;
28930                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28931                 }
28932
28933                 if(this.labelmd > 0){
28934                     content[0].cls += ' col-md-' + this.labelmd;
28935                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28936                 }
28937
28938                 if(this.labelsm > 0){
28939                     content[0].cls += ' col-sm-' + this.labelsm;
28940                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28941                 }
28942
28943                 if(this.labelxs > 0){
28944                     content[0].cls += ' col-xs-' + this.labelxs;
28945                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28946                 }
28947                 
28948             }
28949         }
28950         
28951         var cfg = {
28952             tag : 'div',
28953             cls : 'row clearfix',
28954             cn : content
28955         };
28956         
28957         return cfg;
28958         
28959     },
28960     
28961     initEvents : function()
28962     {
28963         this.managerEl = this.el.select('.roo-document-manager', true).first();
28964         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28965         
28966         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28967         this.selectorEl.hide();
28968         
28969         if(this.multiple){
28970             this.selectorEl.attr('multiple', 'multiple');
28971         }
28972         
28973         this.selectorEl.on('change', this.onFileSelected, this);
28974         
28975         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28976         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28977         
28978         this.uploader.on('click', this.onUploaderClick, this);
28979         
28980         this.renderProgressDialog();
28981         
28982         var _this = this;
28983         
28984         window.addEventListener("resize", function() { _this.refresh(); } );
28985         
28986         this.fireEvent('initial', this);
28987     },
28988     
28989     renderProgressDialog : function()
28990     {
28991         var _this = this;
28992         
28993         this.progressDialog = new Roo.bootstrap.Modal({
28994             cls : 'roo-document-manager-progress-dialog',
28995             allow_close : false,
28996             title : '',
28997             buttons : [
28998                 {
28999                     name  :'cancel',
29000                     weight : 'danger',
29001                     html : 'Cancel'
29002                 }
29003             ], 
29004             listeners : { 
29005                 btnclick : function() {
29006                     _this.uploadCancel();
29007                     this.hide();
29008                 }
29009             }
29010         });
29011          
29012         this.progressDialog.render(Roo.get(document.body));
29013          
29014         this.progress = new Roo.bootstrap.Progress({
29015             cls : 'roo-document-manager-progress',
29016             active : true,
29017             striped : true
29018         });
29019         
29020         this.progress.render(this.progressDialog.getChildContainer());
29021         
29022         this.progressBar = new Roo.bootstrap.ProgressBar({
29023             cls : 'roo-document-manager-progress-bar',
29024             aria_valuenow : 0,
29025             aria_valuemin : 0,
29026             aria_valuemax : 12,
29027             panel : 'success'
29028         });
29029         
29030         this.progressBar.render(this.progress.getChildContainer());
29031     },
29032     
29033     onUploaderClick : function(e)
29034     {
29035         e.preventDefault();
29036      
29037         if(this.fireEvent('beforeselectfile', this) != false){
29038             this.selectorEl.dom.click();
29039         }
29040         
29041     },
29042     
29043     onFileSelected : function(e)
29044     {
29045         e.preventDefault();
29046         
29047         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29048             return;
29049         }
29050         
29051         Roo.each(this.selectorEl.dom.files, function(file){
29052             if(this.fireEvent('inspect', this, file) != false){
29053                 this.files.push(file);
29054             }
29055         }, this);
29056         
29057         this.queue();
29058         
29059     },
29060     
29061     queue : function()
29062     {
29063         this.selectorEl.dom.value = '';
29064         
29065         if(!this.files || !this.files.length){
29066             return;
29067         }
29068         
29069         if(this.boxes > 0 && this.files.length > this.boxes){
29070             this.files = this.files.slice(0, this.boxes);
29071         }
29072         
29073         this.uploader.show();
29074         
29075         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29076             this.uploader.hide();
29077         }
29078         
29079         var _this = this;
29080         
29081         var files = [];
29082         
29083         var docs = [];
29084         
29085         Roo.each(this.files, function(file){
29086             
29087             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29088                 var f = this.renderPreview(file);
29089                 files.push(f);
29090                 return;
29091             }
29092             
29093             if(file.type.indexOf('image') != -1){
29094                 this.delegates.push(
29095                     (function(){
29096                         _this.process(file);
29097                     }).createDelegate(this)
29098                 );
29099         
29100                 return;
29101             }
29102             
29103             docs.push(
29104                 (function(){
29105                     _this.process(file);
29106                 }).createDelegate(this)
29107             );
29108             
29109         }, this);
29110         
29111         this.files = files;
29112         
29113         this.delegates = this.delegates.concat(docs);
29114         
29115         if(!this.delegates.length){
29116             this.refresh();
29117             return;
29118         }
29119         
29120         this.progressBar.aria_valuemax = this.delegates.length;
29121         
29122         this.arrange();
29123         
29124         return;
29125     },
29126     
29127     arrange : function()
29128     {
29129         if(!this.delegates.length){
29130             this.progressDialog.hide();
29131             this.refresh();
29132             return;
29133         }
29134         
29135         var delegate = this.delegates.shift();
29136         
29137         this.progressDialog.show();
29138         
29139         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29140         
29141         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29142         
29143         delegate();
29144     },
29145     
29146     refresh : function()
29147     {
29148         this.uploader.show();
29149         
29150         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29151             this.uploader.hide();
29152         }
29153         
29154         Roo.isTouch ? this.closable(false) : this.closable(true);
29155         
29156         this.fireEvent('refresh', this);
29157     },
29158     
29159     onRemove : function(e, el, o)
29160     {
29161         e.preventDefault();
29162         
29163         this.fireEvent('remove', this, o);
29164         
29165     },
29166     
29167     remove : function(o)
29168     {
29169         var files = [];
29170         
29171         Roo.each(this.files, function(file){
29172             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29173                 files.push(file);
29174                 return;
29175             }
29176
29177             o.target.remove();
29178
29179         }, this);
29180         
29181         this.files = files;
29182         
29183         this.refresh();
29184     },
29185     
29186     clear : function()
29187     {
29188         Roo.each(this.files, function(file){
29189             if(!file.target){
29190                 return;
29191             }
29192             
29193             file.target.remove();
29194
29195         }, this);
29196         
29197         this.files = [];
29198         
29199         this.refresh();
29200     },
29201     
29202     onClick : function(e, el, o)
29203     {
29204         e.preventDefault();
29205         
29206         this.fireEvent('click', this, o);
29207         
29208     },
29209     
29210     closable : function(closable)
29211     {
29212         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29213             
29214             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29215             
29216             if(closable){
29217                 el.show();
29218                 return;
29219             }
29220             
29221             el.hide();
29222             
29223         }, this);
29224     },
29225     
29226     xhrOnLoad : function(xhr)
29227     {
29228         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29229             el.remove();
29230         }, this);
29231         
29232         if (xhr.readyState !== 4) {
29233             this.arrange();
29234             this.fireEvent('exception', this, xhr);
29235             return;
29236         }
29237
29238         var response = Roo.decode(xhr.responseText);
29239         
29240         if(!response.success){
29241             this.arrange();
29242             this.fireEvent('exception', this, xhr);
29243             return;
29244         }
29245         
29246         var file = this.renderPreview(response.data);
29247         
29248         this.files.push(file);
29249         
29250         this.arrange();
29251         
29252         this.fireEvent('afterupload', this, xhr);
29253         
29254     },
29255     
29256     xhrOnError : function(xhr)
29257     {
29258         Roo.log('xhr on error');
29259         
29260         var response = Roo.decode(xhr.responseText);
29261           
29262         Roo.log(response);
29263         
29264         this.arrange();
29265     },
29266     
29267     process : function(file)
29268     {
29269         if(this.fireEvent('process', this, file) !== false){
29270             if(this.editable && file.type.indexOf('image') != -1){
29271                 this.fireEvent('edit', this, file);
29272                 return;
29273             }
29274
29275             this.uploadStart(file, false);
29276
29277             return;
29278         }
29279         
29280     },
29281     
29282     uploadStart : function(file, crop)
29283     {
29284         this.xhr = new XMLHttpRequest();
29285         
29286         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29287             this.arrange();
29288             return;
29289         }
29290         
29291         file.xhr = this.xhr;
29292             
29293         this.managerEl.createChild({
29294             tag : 'div',
29295             cls : 'roo-document-manager-loading',
29296             cn : [
29297                 {
29298                     tag : 'div',
29299                     tooltip : file.name,
29300                     cls : 'roo-document-manager-thumb',
29301                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29302                 }
29303             ]
29304
29305         });
29306
29307         this.xhr.open(this.method, this.url, true);
29308         
29309         var headers = {
29310             "Accept": "application/json",
29311             "Cache-Control": "no-cache",
29312             "X-Requested-With": "XMLHttpRequest"
29313         };
29314         
29315         for (var headerName in headers) {
29316             var headerValue = headers[headerName];
29317             if (headerValue) {
29318                 this.xhr.setRequestHeader(headerName, headerValue);
29319             }
29320         }
29321         
29322         var _this = this;
29323         
29324         this.xhr.onload = function()
29325         {
29326             _this.xhrOnLoad(_this.xhr);
29327         }
29328         
29329         this.xhr.onerror = function()
29330         {
29331             _this.xhrOnError(_this.xhr);
29332         }
29333         
29334         var formData = new FormData();
29335
29336         formData.append('returnHTML', 'NO');
29337         
29338         if(crop){
29339             formData.append('crop', crop);
29340         }
29341         
29342         formData.append(this.paramName, file, file.name);
29343         
29344         var options = {
29345             file : file, 
29346             manually : false
29347         };
29348         
29349         if(this.fireEvent('prepare', this, formData, options) != false){
29350             
29351             if(options.manually){
29352                 return;
29353             }
29354             
29355             this.xhr.send(formData);
29356             return;
29357         };
29358         
29359         this.uploadCancel();
29360     },
29361     
29362     uploadCancel : function()
29363     {
29364         if (this.xhr) {
29365             this.xhr.abort();
29366         }
29367         
29368         this.delegates = [];
29369         
29370         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29371             el.remove();
29372         }, this);
29373         
29374         this.arrange();
29375     },
29376     
29377     renderPreview : function(file)
29378     {
29379         if(typeof(file.target) != 'undefined' && file.target){
29380             return file;
29381         }
29382         
29383         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29384         
29385         var previewEl = this.managerEl.createChild({
29386             tag : 'div',
29387             cls : 'roo-document-manager-preview',
29388             cn : [
29389                 {
29390                     tag : 'div',
29391                     tooltip : file[this.toolTipName],
29392                     cls : 'roo-document-manager-thumb',
29393                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29394                 },
29395                 {
29396                     tag : 'button',
29397                     cls : 'close',
29398                     html : '<i class="fa fa-times-circle"></i>'
29399                 }
29400             ]
29401         });
29402
29403         var close = previewEl.select('button.close', true).first();
29404
29405         close.on('click', this.onRemove, this, file);
29406
29407         file.target = previewEl;
29408
29409         var image = previewEl.select('img', true).first();
29410         
29411         var _this = this;
29412         
29413         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29414         
29415         image.on('click', this.onClick, this, file);
29416         
29417         this.fireEvent('previewrendered', this, file);
29418         
29419         return file;
29420         
29421     },
29422     
29423     onPreviewLoad : function(file, image)
29424     {
29425         if(typeof(file.target) == 'undefined' || !file.target){
29426             return;
29427         }
29428         
29429         var width = image.dom.naturalWidth || image.dom.width;
29430         var height = image.dom.naturalHeight || image.dom.height;
29431         
29432         if(!this.previewResize) {
29433             return;
29434         }
29435         
29436         if(width > height){
29437             file.target.addClass('wide');
29438             return;
29439         }
29440         
29441         file.target.addClass('tall');
29442         return;
29443         
29444     },
29445     
29446     uploadFromSource : function(file, crop)
29447     {
29448         this.xhr = new XMLHttpRequest();
29449         
29450         this.managerEl.createChild({
29451             tag : 'div',
29452             cls : 'roo-document-manager-loading',
29453             cn : [
29454                 {
29455                     tag : 'div',
29456                     tooltip : file.name,
29457                     cls : 'roo-document-manager-thumb',
29458                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29459                 }
29460             ]
29461
29462         });
29463
29464         this.xhr.open(this.method, this.url, true);
29465         
29466         var headers = {
29467             "Accept": "application/json",
29468             "Cache-Control": "no-cache",
29469             "X-Requested-With": "XMLHttpRequest"
29470         };
29471         
29472         for (var headerName in headers) {
29473             var headerValue = headers[headerName];
29474             if (headerValue) {
29475                 this.xhr.setRequestHeader(headerName, headerValue);
29476             }
29477         }
29478         
29479         var _this = this;
29480         
29481         this.xhr.onload = function()
29482         {
29483             _this.xhrOnLoad(_this.xhr);
29484         }
29485         
29486         this.xhr.onerror = function()
29487         {
29488             _this.xhrOnError(_this.xhr);
29489         }
29490         
29491         var formData = new FormData();
29492
29493         formData.append('returnHTML', 'NO');
29494         
29495         formData.append('crop', crop);
29496         
29497         if(typeof(file.filename) != 'undefined'){
29498             formData.append('filename', file.filename);
29499         }
29500         
29501         if(typeof(file.mimetype) != 'undefined'){
29502             formData.append('mimetype', file.mimetype);
29503         }
29504         
29505         Roo.log(formData);
29506         
29507         if(this.fireEvent('prepare', this, formData) != false){
29508             this.xhr.send(formData);
29509         };
29510     }
29511 });
29512
29513 /*
29514 * Licence: LGPL
29515 */
29516
29517 /**
29518  * @class Roo.bootstrap.DocumentViewer
29519  * @extends Roo.bootstrap.Component
29520  * Bootstrap DocumentViewer class
29521  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29522  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29523  * 
29524  * @constructor
29525  * Create a new DocumentViewer
29526  * @param {Object} config The config object
29527  */
29528
29529 Roo.bootstrap.DocumentViewer = function(config){
29530     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29531     
29532     this.addEvents({
29533         /**
29534          * @event initial
29535          * Fire after initEvent
29536          * @param {Roo.bootstrap.DocumentViewer} this
29537          */
29538         "initial" : true,
29539         /**
29540          * @event click
29541          * Fire after click
29542          * @param {Roo.bootstrap.DocumentViewer} this
29543          */
29544         "click" : true,
29545         /**
29546          * @event download
29547          * Fire after download button
29548          * @param {Roo.bootstrap.DocumentViewer} this
29549          */
29550         "download" : true,
29551         /**
29552          * @event trash
29553          * Fire after trash button
29554          * @param {Roo.bootstrap.DocumentViewer} this
29555          */
29556         "trash" : true
29557         
29558     });
29559 };
29560
29561 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29562     
29563     showDownload : true,
29564     
29565     showTrash : true,
29566     
29567     getAutoCreate : function()
29568     {
29569         var cfg = {
29570             tag : 'div',
29571             cls : 'roo-document-viewer',
29572             cn : [
29573                 {
29574                     tag : 'div',
29575                     cls : 'roo-document-viewer-body',
29576                     cn : [
29577                         {
29578                             tag : 'div',
29579                             cls : 'roo-document-viewer-thumb',
29580                             cn : [
29581                                 {
29582                                     tag : 'img',
29583                                     cls : 'roo-document-viewer-image'
29584                                 }
29585                             ]
29586                         }
29587                     ]
29588                 },
29589                 {
29590                     tag : 'div',
29591                     cls : 'roo-document-viewer-footer',
29592                     cn : {
29593                         tag : 'div',
29594                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29595                         cn : [
29596                             {
29597                                 tag : 'div',
29598                                 cls : 'btn-group roo-document-viewer-download',
29599                                 cn : [
29600                                     {
29601                                         tag : 'button',
29602                                         cls : 'btn btn-default',
29603                                         html : '<i class="fa fa-download"></i>'
29604                                     }
29605                                 ]
29606                             },
29607                             {
29608                                 tag : 'div',
29609                                 cls : 'btn-group roo-document-viewer-trash',
29610                                 cn : [
29611                                     {
29612                                         tag : 'button',
29613                                         cls : 'btn btn-default',
29614                                         html : '<i class="fa fa-trash"></i>'
29615                                     }
29616                                 ]
29617                             }
29618                         ]
29619                     }
29620                 }
29621             ]
29622         };
29623         
29624         return cfg;
29625     },
29626     
29627     initEvents : function()
29628     {
29629         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29630         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29631         
29632         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29633         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29634         
29635         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29636         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29637         
29638         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29639         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29640         
29641         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29642         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29643         
29644         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29645         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29646         
29647         this.bodyEl.on('click', this.onClick, this);
29648         this.downloadBtn.on('click', this.onDownload, this);
29649         this.trashBtn.on('click', this.onTrash, this);
29650         
29651         this.downloadBtn.hide();
29652         this.trashBtn.hide();
29653         
29654         if(this.showDownload){
29655             this.downloadBtn.show();
29656         }
29657         
29658         if(this.showTrash){
29659             this.trashBtn.show();
29660         }
29661         
29662         if(!this.showDownload && !this.showTrash) {
29663             this.footerEl.hide();
29664         }
29665         
29666     },
29667     
29668     initial : function()
29669     {
29670         this.fireEvent('initial', this);
29671         
29672     },
29673     
29674     onClick : function(e)
29675     {
29676         e.preventDefault();
29677         
29678         this.fireEvent('click', this);
29679     },
29680     
29681     onDownload : function(e)
29682     {
29683         e.preventDefault();
29684         
29685         this.fireEvent('download', this);
29686     },
29687     
29688     onTrash : function(e)
29689     {
29690         e.preventDefault();
29691         
29692         this.fireEvent('trash', this);
29693     }
29694     
29695 });
29696 /*
29697  * - LGPL
29698  *
29699  * nav progress bar
29700  * 
29701  */
29702
29703 /**
29704  * @class Roo.bootstrap.NavProgressBar
29705  * @extends Roo.bootstrap.Component
29706  * Bootstrap NavProgressBar class
29707  * 
29708  * @constructor
29709  * Create a new nav progress bar
29710  * @param {Object} config The config object
29711  */
29712
29713 Roo.bootstrap.NavProgressBar = function(config){
29714     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29715
29716     this.bullets = this.bullets || [];
29717    
29718 //    Roo.bootstrap.NavProgressBar.register(this);
29719      this.addEvents({
29720         /**
29721              * @event changed
29722              * Fires when the active item changes
29723              * @param {Roo.bootstrap.NavProgressBar} this
29724              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29725              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29726          */
29727         'changed': true
29728      });
29729     
29730 };
29731
29732 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29733     
29734     bullets : [],
29735     barItems : [],
29736     
29737     getAutoCreate : function()
29738     {
29739         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29740         
29741         cfg = {
29742             tag : 'div',
29743             cls : 'roo-navigation-bar-group',
29744             cn : [
29745                 {
29746                     tag : 'div',
29747                     cls : 'roo-navigation-top-bar'
29748                 },
29749                 {
29750                     tag : 'div',
29751                     cls : 'roo-navigation-bullets-bar',
29752                     cn : [
29753                         {
29754                             tag : 'ul',
29755                             cls : 'roo-navigation-bar'
29756                         }
29757                     ]
29758                 },
29759                 
29760                 {
29761                     tag : 'div',
29762                     cls : 'roo-navigation-bottom-bar'
29763                 }
29764             ]
29765             
29766         };
29767         
29768         return cfg;
29769         
29770     },
29771     
29772     initEvents: function() 
29773     {
29774         
29775     },
29776     
29777     onRender : function(ct, position) 
29778     {
29779         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29780         
29781         if(this.bullets.length){
29782             Roo.each(this.bullets, function(b){
29783                this.addItem(b);
29784             }, this);
29785         }
29786         
29787         this.format();
29788         
29789     },
29790     
29791     addItem : function(cfg)
29792     {
29793         var item = new Roo.bootstrap.NavProgressItem(cfg);
29794         
29795         item.parentId = this.id;
29796         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29797         
29798         if(cfg.html){
29799             var top = new Roo.bootstrap.Element({
29800                 tag : 'div',
29801                 cls : 'roo-navigation-bar-text'
29802             });
29803             
29804             var bottom = new Roo.bootstrap.Element({
29805                 tag : 'div',
29806                 cls : 'roo-navigation-bar-text'
29807             });
29808             
29809             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29810             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29811             
29812             var topText = new Roo.bootstrap.Element({
29813                 tag : 'span',
29814                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29815             });
29816             
29817             var bottomText = new Roo.bootstrap.Element({
29818                 tag : 'span',
29819                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29820             });
29821             
29822             topText.onRender(top.el, null);
29823             bottomText.onRender(bottom.el, null);
29824             
29825             item.topEl = top;
29826             item.bottomEl = bottom;
29827         }
29828         
29829         this.barItems.push(item);
29830         
29831         return item;
29832     },
29833     
29834     getActive : function()
29835     {
29836         var active = false;
29837         
29838         Roo.each(this.barItems, function(v){
29839             
29840             if (!v.isActive()) {
29841                 return;
29842             }
29843             
29844             active = v;
29845             return false;
29846             
29847         });
29848         
29849         return active;
29850     },
29851     
29852     setActiveItem : function(item)
29853     {
29854         var prev = false;
29855         
29856         Roo.each(this.barItems, function(v){
29857             if (v.rid == item.rid) {
29858                 return ;
29859             }
29860             
29861             if (v.isActive()) {
29862                 v.setActive(false);
29863                 prev = v;
29864             }
29865         });
29866
29867         item.setActive(true);
29868         
29869         this.fireEvent('changed', this, item, prev);
29870     },
29871     
29872     getBarItem: function(rid)
29873     {
29874         var ret = false;
29875         
29876         Roo.each(this.barItems, function(e) {
29877             if (e.rid != rid) {
29878                 return;
29879             }
29880             
29881             ret =  e;
29882             return false;
29883         });
29884         
29885         return ret;
29886     },
29887     
29888     indexOfItem : function(item)
29889     {
29890         var index = false;
29891         
29892         Roo.each(this.barItems, function(v, i){
29893             
29894             if (v.rid != item.rid) {
29895                 return;
29896             }
29897             
29898             index = i;
29899             return false
29900         });
29901         
29902         return index;
29903     },
29904     
29905     setActiveNext : function()
29906     {
29907         var i = this.indexOfItem(this.getActive());
29908         
29909         if (i > this.barItems.length) {
29910             return;
29911         }
29912         
29913         this.setActiveItem(this.barItems[i+1]);
29914     },
29915     
29916     setActivePrev : function()
29917     {
29918         var i = this.indexOfItem(this.getActive());
29919         
29920         if (i  < 1) {
29921             return;
29922         }
29923         
29924         this.setActiveItem(this.barItems[i-1]);
29925     },
29926     
29927     format : function()
29928     {
29929         if(!this.barItems.length){
29930             return;
29931         }
29932      
29933         var width = 100 / this.barItems.length;
29934         
29935         Roo.each(this.barItems, function(i){
29936             i.el.setStyle('width', width + '%');
29937             i.topEl.el.setStyle('width', width + '%');
29938             i.bottomEl.el.setStyle('width', width + '%');
29939         }, this);
29940         
29941     }
29942     
29943 });
29944 /*
29945  * - LGPL
29946  *
29947  * Nav Progress Item
29948  * 
29949  */
29950
29951 /**
29952  * @class Roo.bootstrap.NavProgressItem
29953  * @extends Roo.bootstrap.Component
29954  * Bootstrap NavProgressItem class
29955  * @cfg {String} rid the reference id
29956  * @cfg {Boolean} active (true|false) Is item active default false
29957  * @cfg {Boolean} disabled (true|false) Is item active default false
29958  * @cfg {String} html
29959  * @cfg {String} position (top|bottom) text position default bottom
29960  * @cfg {String} icon show icon instead of number
29961  * 
29962  * @constructor
29963  * Create a new NavProgressItem
29964  * @param {Object} config The config object
29965  */
29966 Roo.bootstrap.NavProgressItem = function(config){
29967     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29968     this.addEvents({
29969         // raw events
29970         /**
29971          * @event click
29972          * The raw click event for the entire grid.
29973          * @param {Roo.bootstrap.NavProgressItem} this
29974          * @param {Roo.EventObject} e
29975          */
29976         "click" : true
29977     });
29978    
29979 };
29980
29981 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29982     
29983     rid : '',
29984     active : false,
29985     disabled : false,
29986     html : '',
29987     position : 'bottom',
29988     icon : false,
29989     
29990     getAutoCreate : function()
29991     {
29992         var iconCls = 'roo-navigation-bar-item-icon';
29993         
29994         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29995         
29996         var cfg = {
29997             tag: 'li',
29998             cls: 'roo-navigation-bar-item',
29999             cn : [
30000                 {
30001                     tag : 'i',
30002                     cls : iconCls
30003                 }
30004             ]
30005         };
30006         
30007         if(this.active){
30008             cfg.cls += ' active';
30009         }
30010         if(this.disabled){
30011             cfg.cls += ' disabled';
30012         }
30013         
30014         return cfg;
30015     },
30016     
30017     disable : function()
30018     {
30019         this.setDisabled(true);
30020     },
30021     
30022     enable : function()
30023     {
30024         this.setDisabled(false);
30025     },
30026     
30027     initEvents: function() 
30028     {
30029         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30030         
30031         this.iconEl.on('click', this.onClick, this);
30032     },
30033     
30034     onClick : function(e)
30035     {
30036         e.preventDefault();
30037         
30038         if(this.disabled){
30039             return;
30040         }
30041         
30042         if(this.fireEvent('click', this, e) === false){
30043             return;
30044         };
30045         
30046         this.parent().setActiveItem(this);
30047     },
30048     
30049     isActive: function () 
30050     {
30051         return this.active;
30052     },
30053     
30054     setActive : function(state)
30055     {
30056         if(this.active == state){
30057             return;
30058         }
30059         
30060         this.active = state;
30061         
30062         if (state) {
30063             this.el.addClass('active');
30064             return;
30065         }
30066         
30067         this.el.removeClass('active');
30068         
30069         return;
30070     },
30071     
30072     setDisabled : function(state)
30073     {
30074         if(this.disabled == state){
30075             return;
30076         }
30077         
30078         this.disabled = state;
30079         
30080         if (state) {
30081             this.el.addClass('disabled');
30082             return;
30083         }
30084         
30085         this.el.removeClass('disabled');
30086     },
30087     
30088     tooltipEl : function()
30089     {
30090         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30091     }
30092 });
30093  
30094
30095  /*
30096  * - LGPL
30097  *
30098  * FieldLabel
30099  * 
30100  */
30101
30102 /**
30103  * @class Roo.bootstrap.FieldLabel
30104  * @extends Roo.bootstrap.Component
30105  * Bootstrap FieldLabel class
30106  * @cfg {String} html contents of the element
30107  * @cfg {String} tag tag of the element default label
30108  * @cfg {String} cls class of the element
30109  * @cfg {String} target label target 
30110  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30111  * @cfg {String} invalidClass default "text-warning"
30112  * @cfg {String} validClass default "text-success"
30113  * @cfg {String} iconTooltip default "This field is required"
30114  * @cfg {String} indicatorpos (left|right) default left
30115  * 
30116  * @constructor
30117  * Create a new FieldLabel
30118  * @param {Object} config The config object
30119  */
30120
30121 Roo.bootstrap.FieldLabel = function(config){
30122     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30123     
30124     this.addEvents({
30125             /**
30126              * @event invalid
30127              * Fires after the field has been marked as invalid.
30128              * @param {Roo.form.FieldLabel} this
30129              * @param {String} msg The validation message
30130              */
30131             invalid : true,
30132             /**
30133              * @event valid
30134              * Fires after the field has been validated with no errors.
30135              * @param {Roo.form.FieldLabel} this
30136              */
30137             valid : true
30138         });
30139 };
30140
30141 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30142     
30143     tag: 'label',
30144     cls: '',
30145     html: '',
30146     target: '',
30147     allowBlank : true,
30148     invalidClass : 'has-warning',
30149     validClass : 'has-success',
30150     iconTooltip : 'This field is required',
30151     indicatorpos : 'left',
30152     
30153     getAutoCreate : function(){
30154         
30155         var cls = "";
30156         if (!this.allowBlank) {
30157             cls  = "visible";
30158         }
30159         
30160         var cfg = {
30161             tag : this.tag,
30162             cls : 'roo-bootstrap-field-label ' + this.cls,
30163             for : this.target,
30164             cn : [
30165                 {
30166                     tag : 'i',
30167                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30168                     tooltip : this.iconTooltip
30169                 },
30170                 {
30171                     tag : 'span',
30172                     html : this.html
30173                 }
30174             ] 
30175         };
30176         
30177         if(this.indicatorpos == 'right'){
30178             var cfg = {
30179                 tag : this.tag,
30180                 cls : 'roo-bootstrap-field-label ' + this.cls,
30181                 for : this.target,
30182                 cn : [
30183                     {
30184                         tag : 'span',
30185                         html : this.html
30186                     },
30187                     {
30188                         tag : 'i',
30189                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30190                         tooltip : this.iconTooltip
30191                     }
30192                 ] 
30193             };
30194         }
30195         
30196         return cfg;
30197     },
30198     
30199     initEvents: function() 
30200     {
30201         Roo.bootstrap.Element.superclass.initEvents.call(this);
30202         
30203         this.indicator = this.indicatorEl();
30204         
30205         if(this.indicator){
30206             this.indicator.removeClass('visible');
30207             this.indicator.addClass('invisible');
30208         }
30209         
30210         Roo.bootstrap.FieldLabel.register(this);
30211     },
30212     
30213     indicatorEl : function()
30214     {
30215         var indicator = this.el.select('i.roo-required-indicator',true).first();
30216         
30217         if(!indicator){
30218             return false;
30219         }
30220         
30221         return indicator;
30222         
30223     },
30224     
30225     /**
30226      * Mark this field as valid
30227      */
30228     markValid : function()
30229     {
30230         if(this.indicator){
30231             this.indicator.removeClass('visible');
30232             this.indicator.addClass('invisible');
30233         }
30234         
30235         this.el.removeClass(this.invalidClass);
30236         
30237         this.el.addClass(this.validClass);
30238         
30239         this.fireEvent('valid', this);
30240     },
30241     
30242     /**
30243      * Mark this field as invalid
30244      * @param {String} msg The validation message
30245      */
30246     markInvalid : function(msg)
30247     {
30248         if(this.indicator){
30249             this.indicator.removeClass('invisible');
30250             this.indicator.addClass('visible');
30251         }
30252         
30253         this.el.removeClass(this.validClass);
30254         
30255         this.el.addClass(this.invalidClass);
30256         
30257         this.fireEvent('invalid', this, msg);
30258     }
30259     
30260    
30261 });
30262
30263 Roo.apply(Roo.bootstrap.FieldLabel, {
30264     
30265     groups: {},
30266     
30267      /**
30268     * register a FieldLabel Group
30269     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30270     */
30271     register : function(label)
30272     {
30273         if(this.groups.hasOwnProperty(label.target)){
30274             return;
30275         }
30276      
30277         this.groups[label.target] = label;
30278         
30279     },
30280     /**
30281     * fetch a FieldLabel Group based on the target
30282     * @param {string} target
30283     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30284     */
30285     get: function(target) {
30286         if (typeof(this.groups[target]) == 'undefined') {
30287             return false;
30288         }
30289         
30290         return this.groups[target] ;
30291     }
30292 });
30293
30294  
30295
30296  /*
30297  * - LGPL
30298  *
30299  * page DateSplitField.
30300  * 
30301  */
30302
30303
30304 /**
30305  * @class Roo.bootstrap.DateSplitField
30306  * @extends Roo.bootstrap.Component
30307  * Bootstrap DateSplitField class
30308  * @cfg {string} fieldLabel - the label associated
30309  * @cfg {Number} labelWidth set the width of label (0-12)
30310  * @cfg {String} labelAlign (top|left)
30311  * @cfg {Boolean} dayAllowBlank (true|false) default false
30312  * @cfg {Boolean} monthAllowBlank (true|false) default false
30313  * @cfg {Boolean} yearAllowBlank (true|false) default false
30314  * @cfg {string} dayPlaceholder 
30315  * @cfg {string} monthPlaceholder
30316  * @cfg {string} yearPlaceholder
30317  * @cfg {string} dayFormat default 'd'
30318  * @cfg {string} monthFormat default 'm'
30319  * @cfg {string} yearFormat default 'Y'
30320  * @cfg {Number} labellg set the width of label (1-12)
30321  * @cfg {Number} labelmd set the width of label (1-12)
30322  * @cfg {Number} labelsm set the width of label (1-12)
30323  * @cfg {Number} labelxs set the width of label (1-12)
30324
30325  *     
30326  * @constructor
30327  * Create a new DateSplitField
30328  * @param {Object} config The config object
30329  */
30330
30331 Roo.bootstrap.DateSplitField = function(config){
30332     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30333     
30334     this.addEvents({
30335         // raw events
30336          /**
30337          * @event years
30338          * getting the data of years
30339          * @param {Roo.bootstrap.DateSplitField} this
30340          * @param {Object} years
30341          */
30342         "years" : true,
30343         /**
30344          * @event days
30345          * getting the data of days
30346          * @param {Roo.bootstrap.DateSplitField} this
30347          * @param {Object} days
30348          */
30349         "days" : true,
30350         /**
30351          * @event invalid
30352          * Fires after the field has been marked as invalid.
30353          * @param {Roo.form.Field} this
30354          * @param {String} msg The validation message
30355          */
30356         invalid : true,
30357        /**
30358          * @event valid
30359          * Fires after the field has been validated with no errors.
30360          * @param {Roo.form.Field} this
30361          */
30362         valid : true
30363     });
30364 };
30365
30366 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30367     
30368     fieldLabel : '',
30369     labelAlign : 'top',
30370     labelWidth : 3,
30371     dayAllowBlank : false,
30372     monthAllowBlank : false,
30373     yearAllowBlank : false,
30374     dayPlaceholder : '',
30375     monthPlaceholder : '',
30376     yearPlaceholder : '',
30377     dayFormat : 'd',
30378     monthFormat : 'm',
30379     yearFormat : 'Y',
30380     isFormField : true,
30381     labellg : 0,
30382     labelmd : 0,
30383     labelsm : 0,
30384     labelxs : 0,
30385     
30386     getAutoCreate : function()
30387     {
30388         var cfg = {
30389             tag : 'div',
30390             cls : 'row roo-date-split-field-group',
30391             cn : [
30392                 {
30393                     tag : 'input',
30394                     type : 'hidden',
30395                     cls : 'form-hidden-field roo-date-split-field-group-value',
30396                     name : this.name
30397                 }
30398             ]
30399         };
30400         
30401         var labelCls = 'col-md-12';
30402         var contentCls = 'col-md-4';
30403         
30404         if(this.fieldLabel){
30405             
30406             var label = {
30407                 tag : 'div',
30408                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30409                 cn : [
30410                     {
30411                         tag : 'label',
30412                         html : this.fieldLabel
30413                     }
30414                 ]
30415             };
30416             
30417             if(this.labelAlign == 'left'){
30418             
30419                 if(this.labelWidth > 12){
30420                     label.style = "width: " + this.labelWidth + 'px';
30421                 }
30422
30423                 if(this.labelWidth < 13 && this.labelmd == 0){
30424                     this.labelmd = this.labelWidth;
30425                 }
30426
30427                 if(this.labellg > 0){
30428                     labelCls = ' col-lg-' + this.labellg;
30429                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30430                 }
30431
30432                 if(this.labelmd > 0){
30433                     labelCls = ' col-md-' + this.labelmd;
30434                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30435                 }
30436
30437                 if(this.labelsm > 0){
30438                     labelCls = ' col-sm-' + this.labelsm;
30439                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30440                 }
30441
30442                 if(this.labelxs > 0){
30443                     labelCls = ' col-xs-' + this.labelxs;
30444                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30445                 }
30446             }
30447             
30448             label.cls += ' ' + labelCls;
30449             
30450             cfg.cn.push(label);
30451         }
30452         
30453         Roo.each(['day', 'month', 'year'], function(t){
30454             cfg.cn.push({
30455                 tag : 'div',
30456                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30457             });
30458         }, this);
30459         
30460         return cfg;
30461     },
30462     
30463     inputEl: function ()
30464     {
30465         return this.el.select('.roo-date-split-field-group-value', true).first();
30466     },
30467     
30468     onRender : function(ct, position) 
30469     {
30470         var _this = this;
30471         
30472         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30473         
30474         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30475         
30476         this.dayField = new Roo.bootstrap.ComboBox({
30477             allowBlank : this.dayAllowBlank,
30478             alwaysQuery : true,
30479             displayField : 'value',
30480             editable : false,
30481             fieldLabel : '',
30482             forceSelection : true,
30483             mode : 'local',
30484             placeholder : this.dayPlaceholder,
30485             selectOnFocus : true,
30486             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30487             triggerAction : 'all',
30488             typeAhead : true,
30489             valueField : 'value',
30490             store : new Roo.data.SimpleStore({
30491                 data : (function() {    
30492                     var days = [];
30493                     _this.fireEvent('days', _this, days);
30494                     return days;
30495                 })(),
30496                 fields : [ 'value' ]
30497             }),
30498             listeners : {
30499                 select : function (_self, record, index)
30500                 {
30501                     _this.setValue(_this.getValue());
30502                 }
30503             }
30504         });
30505
30506         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30507         
30508         this.monthField = new Roo.bootstrap.MonthField({
30509             after : '<i class=\"fa fa-calendar\"></i>',
30510             allowBlank : this.monthAllowBlank,
30511             placeholder : this.monthPlaceholder,
30512             readOnly : true,
30513             listeners : {
30514                 render : function (_self)
30515                 {
30516                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30517                         e.preventDefault();
30518                         _self.focus();
30519                     });
30520                 },
30521                 select : function (_self, oldvalue, newvalue)
30522                 {
30523                     _this.setValue(_this.getValue());
30524                 }
30525             }
30526         });
30527         
30528         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30529         
30530         this.yearField = new Roo.bootstrap.ComboBox({
30531             allowBlank : this.yearAllowBlank,
30532             alwaysQuery : true,
30533             displayField : 'value',
30534             editable : false,
30535             fieldLabel : '',
30536             forceSelection : true,
30537             mode : 'local',
30538             placeholder : this.yearPlaceholder,
30539             selectOnFocus : true,
30540             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30541             triggerAction : 'all',
30542             typeAhead : true,
30543             valueField : 'value',
30544             store : new Roo.data.SimpleStore({
30545                 data : (function() {
30546                     var years = [];
30547                     _this.fireEvent('years', _this, years);
30548                     return years;
30549                 })(),
30550                 fields : [ 'value' ]
30551             }),
30552             listeners : {
30553                 select : function (_self, record, index)
30554                 {
30555                     _this.setValue(_this.getValue());
30556                 }
30557             }
30558         });
30559
30560         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30561     },
30562     
30563     setValue : function(v, format)
30564     {
30565         this.inputEl.dom.value = v;
30566         
30567         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30568         
30569         var d = Date.parseDate(v, f);
30570         
30571         if(!d){
30572             this.validate();
30573             return;
30574         }
30575         
30576         this.setDay(d.format(this.dayFormat));
30577         this.setMonth(d.format(this.monthFormat));
30578         this.setYear(d.format(this.yearFormat));
30579         
30580         this.validate();
30581         
30582         return;
30583     },
30584     
30585     setDay : function(v)
30586     {
30587         this.dayField.setValue(v);
30588         this.inputEl.dom.value = this.getValue();
30589         this.validate();
30590         return;
30591     },
30592     
30593     setMonth : function(v)
30594     {
30595         this.monthField.setValue(v, true);
30596         this.inputEl.dom.value = this.getValue();
30597         this.validate();
30598         return;
30599     },
30600     
30601     setYear : function(v)
30602     {
30603         this.yearField.setValue(v);
30604         this.inputEl.dom.value = this.getValue();
30605         this.validate();
30606         return;
30607     },
30608     
30609     getDay : function()
30610     {
30611         return this.dayField.getValue();
30612     },
30613     
30614     getMonth : function()
30615     {
30616         return this.monthField.getValue();
30617     },
30618     
30619     getYear : function()
30620     {
30621         return this.yearField.getValue();
30622     },
30623     
30624     getValue : function()
30625     {
30626         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30627         
30628         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30629         
30630         return date;
30631     },
30632     
30633     reset : function()
30634     {
30635         this.setDay('');
30636         this.setMonth('');
30637         this.setYear('');
30638         this.inputEl.dom.value = '';
30639         this.validate();
30640         return;
30641     },
30642     
30643     validate : function()
30644     {
30645         var d = this.dayField.validate();
30646         var m = this.monthField.validate();
30647         var y = this.yearField.validate();
30648         
30649         var valid = true;
30650         
30651         if(
30652                 (!this.dayAllowBlank && !d) ||
30653                 (!this.monthAllowBlank && !m) ||
30654                 (!this.yearAllowBlank && !y)
30655         ){
30656             valid = false;
30657         }
30658         
30659         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30660             return valid;
30661         }
30662         
30663         if(valid){
30664             this.markValid();
30665             return valid;
30666         }
30667         
30668         this.markInvalid();
30669         
30670         return valid;
30671     },
30672     
30673     markValid : function()
30674     {
30675         
30676         var label = this.el.select('label', true).first();
30677         var icon = this.el.select('i.fa-star', true).first();
30678
30679         if(label && icon){
30680             icon.remove();
30681         }
30682         
30683         this.fireEvent('valid', this);
30684     },
30685     
30686      /**
30687      * Mark this field as invalid
30688      * @param {String} msg The validation message
30689      */
30690     markInvalid : function(msg)
30691     {
30692         
30693         var label = this.el.select('label', true).first();
30694         var icon = this.el.select('i.fa-star', true).first();
30695
30696         if(label && !icon){
30697             this.el.select('.roo-date-split-field-label', true).createChild({
30698                 tag : 'i',
30699                 cls : 'text-danger fa fa-lg fa-star',
30700                 tooltip : 'This field is required',
30701                 style : 'margin-right:5px;'
30702             }, label, true);
30703         }
30704         
30705         this.fireEvent('invalid', this, msg);
30706     },
30707     
30708     clearInvalid : function()
30709     {
30710         var label = this.el.select('label', true).first();
30711         var icon = this.el.select('i.fa-star', true).first();
30712
30713         if(label && icon){
30714             icon.remove();
30715         }
30716         
30717         this.fireEvent('valid', this);
30718     },
30719     
30720     getName: function()
30721     {
30722         return this.name;
30723     }
30724     
30725 });
30726
30727  /**
30728  *
30729  * This is based on 
30730  * http://masonry.desandro.com
30731  *
30732  * The idea is to render all the bricks based on vertical width...
30733  *
30734  * The original code extends 'outlayer' - we might need to use that....
30735  * 
30736  */
30737
30738
30739 /**
30740  * @class Roo.bootstrap.LayoutMasonry
30741  * @extends Roo.bootstrap.Component
30742  * Bootstrap Layout Masonry class
30743  * 
30744  * @constructor
30745  * Create a new Element
30746  * @param {Object} config The config object
30747  */
30748
30749 Roo.bootstrap.LayoutMasonry = function(config){
30750     
30751     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30752     
30753     this.bricks = [];
30754     
30755     Roo.bootstrap.LayoutMasonry.register(this);
30756     
30757     this.addEvents({
30758         // raw events
30759         /**
30760          * @event layout
30761          * Fire after layout the items
30762          * @param {Roo.bootstrap.LayoutMasonry} this
30763          * @param {Roo.EventObject} e
30764          */
30765         "layout" : true
30766     });
30767     
30768 };
30769
30770 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30771     
30772     /**
30773      * @cfg {Boolean} isLayoutInstant = no animation?
30774      */   
30775     isLayoutInstant : false, // needed?
30776    
30777     /**
30778      * @cfg {Number} boxWidth  width of the columns
30779      */   
30780     boxWidth : 450,
30781     
30782       /**
30783      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30784      */   
30785     boxHeight : 0,
30786     
30787     /**
30788      * @cfg {Number} padWidth padding below box..
30789      */   
30790     padWidth : 10, 
30791     
30792     /**
30793      * @cfg {Number} gutter gutter width..
30794      */   
30795     gutter : 10,
30796     
30797      /**
30798      * @cfg {Number} maxCols maximum number of columns
30799      */   
30800     
30801     maxCols: 0,
30802     
30803     /**
30804      * @cfg {Boolean} isAutoInitial defalut true
30805      */   
30806     isAutoInitial : true, 
30807     
30808     containerWidth: 0,
30809     
30810     /**
30811      * @cfg {Boolean} isHorizontal defalut false
30812      */   
30813     isHorizontal : false, 
30814
30815     currentSize : null,
30816     
30817     tag: 'div',
30818     
30819     cls: '',
30820     
30821     bricks: null, //CompositeElement
30822     
30823     cols : 1,
30824     
30825     _isLayoutInited : false,
30826     
30827 //    isAlternative : false, // only use for vertical layout...
30828     
30829     /**
30830      * @cfg {Number} alternativePadWidth padding below box..
30831      */   
30832     alternativePadWidth : 50,
30833     
30834     selectedBrick : [],
30835     
30836     getAutoCreate : function(){
30837         
30838         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30839         
30840         var cfg = {
30841             tag: this.tag,
30842             cls: 'blog-masonary-wrapper ' + this.cls,
30843             cn : {
30844                 cls : 'mas-boxes masonary'
30845             }
30846         };
30847         
30848         return cfg;
30849     },
30850     
30851     getChildContainer: function( )
30852     {
30853         if (this.boxesEl) {
30854             return this.boxesEl;
30855         }
30856         
30857         this.boxesEl = this.el.select('.mas-boxes').first();
30858         
30859         return this.boxesEl;
30860     },
30861     
30862     
30863     initEvents : function()
30864     {
30865         var _this = this;
30866         
30867         if(this.isAutoInitial){
30868             Roo.log('hook children rendered');
30869             this.on('childrenrendered', function() {
30870                 Roo.log('children rendered');
30871                 _this.initial();
30872             } ,this);
30873         }
30874     },
30875     
30876     initial : function()
30877     {
30878         this.selectedBrick = [];
30879         
30880         this.currentSize = this.el.getBox(true);
30881         
30882         Roo.EventManager.onWindowResize(this.resize, this); 
30883
30884         if(!this.isAutoInitial){
30885             this.layout();
30886             return;
30887         }
30888         
30889         this.layout();
30890         
30891         return;
30892         //this.layout.defer(500,this);
30893         
30894     },
30895     
30896     resize : function()
30897     {
30898         var cs = this.el.getBox(true);
30899         
30900         if (
30901                 this.currentSize.width == cs.width && 
30902                 this.currentSize.x == cs.x && 
30903                 this.currentSize.height == cs.height && 
30904                 this.currentSize.y == cs.y 
30905         ) {
30906             Roo.log("no change in with or X or Y");
30907             return;
30908         }
30909         
30910         this.currentSize = cs;
30911         
30912         this.layout();
30913         
30914     },
30915     
30916     layout : function()
30917     {   
30918         this._resetLayout();
30919         
30920         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30921         
30922         this.layoutItems( isInstant );
30923       
30924         this._isLayoutInited = true;
30925         
30926         this.fireEvent('layout', this);
30927         
30928     },
30929     
30930     _resetLayout : function()
30931     {
30932         if(this.isHorizontal){
30933             this.horizontalMeasureColumns();
30934             return;
30935         }
30936         
30937         this.verticalMeasureColumns();
30938         
30939     },
30940     
30941     verticalMeasureColumns : function()
30942     {
30943         this.getContainerWidth();
30944         
30945 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30946 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30947 //            return;
30948 //        }
30949         
30950         var boxWidth = this.boxWidth + this.padWidth;
30951         
30952         if(this.containerWidth < this.boxWidth){
30953             boxWidth = this.containerWidth
30954         }
30955         
30956         var containerWidth = this.containerWidth;
30957         
30958         var cols = Math.floor(containerWidth / boxWidth);
30959         
30960         this.cols = Math.max( cols, 1 );
30961         
30962         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30963         
30964         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30965         
30966         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30967         
30968         this.colWidth = boxWidth + avail - this.padWidth;
30969         
30970         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30971         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30972     },
30973     
30974     horizontalMeasureColumns : function()
30975     {
30976         this.getContainerWidth();
30977         
30978         var boxWidth = this.boxWidth;
30979         
30980         if(this.containerWidth < boxWidth){
30981             boxWidth = this.containerWidth;
30982         }
30983         
30984         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30985         
30986         this.el.setHeight(boxWidth);
30987         
30988     },
30989     
30990     getContainerWidth : function()
30991     {
30992         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30993     },
30994     
30995     layoutItems : function( isInstant )
30996     {
30997         Roo.log(this.bricks);
30998         
30999         var items = Roo.apply([], this.bricks);
31000         
31001         if(this.isHorizontal){
31002             this._horizontalLayoutItems( items , isInstant );
31003             return;
31004         }
31005         
31006 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31007 //            this._verticalAlternativeLayoutItems( items , isInstant );
31008 //            return;
31009 //        }
31010         
31011         this._verticalLayoutItems( items , isInstant );
31012         
31013     },
31014     
31015     _verticalLayoutItems : function ( items , isInstant)
31016     {
31017         if ( !items || !items.length ) {
31018             return;
31019         }
31020         
31021         var standard = [
31022             ['xs', 'xs', 'xs', 'tall'],
31023             ['xs', 'xs', 'tall'],
31024             ['xs', 'xs', 'sm'],
31025             ['xs', 'xs', 'xs'],
31026             ['xs', 'tall'],
31027             ['xs', 'sm'],
31028             ['xs', 'xs'],
31029             ['xs'],
31030             
31031             ['sm', 'xs', 'xs'],
31032             ['sm', 'xs'],
31033             ['sm'],
31034             
31035             ['tall', 'xs', 'xs', 'xs'],
31036             ['tall', 'xs', 'xs'],
31037             ['tall', 'xs'],
31038             ['tall']
31039             
31040         ];
31041         
31042         var queue = [];
31043         
31044         var boxes = [];
31045         
31046         var box = [];
31047         
31048         Roo.each(items, function(item, k){
31049             
31050             switch (item.size) {
31051                 // these layouts take up a full box,
31052                 case 'md' :
31053                 case 'md-left' :
31054                 case 'md-right' :
31055                 case 'wide' :
31056                     
31057                     if(box.length){
31058                         boxes.push(box);
31059                         box = [];
31060                     }
31061                     
31062                     boxes.push([item]);
31063                     
31064                     break;
31065                     
31066                 case 'xs' :
31067                 case 'sm' :
31068                 case 'tall' :
31069                     
31070                     box.push(item);
31071                     
31072                     break;
31073                 default :
31074                     break;
31075                     
31076             }
31077             
31078         }, this);
31079         
31080         if(box.length){
31081             boxes.push(box);
31082             box = [];
31083         }
31084         
31085         var filterPattern = function(box, length)
31086         {
31087             if(!box.length){
31088                 return;
31089             }
31090             
31091             var match = false;
31092             
31093             var pattern = box.slice(0, length);
31094             
31095             var format = [];
31096             
31097             Roo.each(pattern, function(i){
31098                 format.push(i.size);
31099             }, this);
31100             
31101             Roo.each(standard, function(s){
31102                 
31103                 if(String(s) != String(format)){
31104                     return;
31105                 }
31106                 
31107                 match = true;
31108                 return false;
31109                 
31110             }, this);
31111             
31112             if(!match && length == 1){
31113                 return;
31114             }
31115             
31116             if(!match){
31117                 filterPattern(box, length - 1);
31118                 return;
31119             }
31120                 
31121             queue.push(pattern);
31122
31123             box = box.slice(length, box.length);
31124
31125             filterPattern(box, 4);
31126
31127             return;
31128             
31129         }
31130         
31131         Roo.each(boxes, function(box, k){
31132             
31133             if(!box.length){
31134                 return;
31135             }
31136             
31137             if(box.length == 1){
31138                 queue.push(box);
31139                 return;
31140             }
31141             
31142             filterPattern(box, 4);
31143             
31144         }, this);
31145         
31146         this._processVerticalLayoutQueue( queue, isInstant );
31147         
31148     },
31149     
31150 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31151 //    {
31152 //        if ( !items || !items.length ) {
31153 //            return;
31154 //        }
31155 //
31156 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31157 //        
31158 //    },
31159     
31160     _horizontalLayoutItems : function ( items , isInstant)
31161     {
31162         if ( !items || !items.length || items.length < 3) {
31163             return;
31164         }
31165         
31166         items.reverse();
31167         
31168         var eItems = items.slice(0, 3);
31169         
31170         items = items.slice(3, items.length);
31171         
31172         var standard = [
31173             ['xs', 'xs', 'xs', 'wide'],
31174             ['xs', 'xs', 'wide'],
31175             ['xs', 'xs', 'sm'],
31176             ['xs', 'xs', 'xs'],
31177             ['xs', 'wide'],
31178             ['xs', 'sm'],
31179             ['xs', 'xs'],
31180             ['xs'],
31181             
31182             ['sm', 'xs', 'xs'],
31183             ['sm', 'xs'],
31184             ['sm'],
31185             
31186             ['wide', 'xs', 'xs', 'xs'],
31187             ['wide', 'xs', 'xs'],
31188             ['wide', 'xs'],
31189             ['wide'],
31190             
31191             ['wide-thin']
31192         ];
31193         
31194         var queue = [];
31195         
31196         var boxes = [];
31197         
31198         var box = [];
31199         
31200         Roo.each(items, function(item, k){
31201             
31202             switch (item.size) {
31203                 case 'md' :
31204                 case 'md-left' :
31205                 case 'md-right' :
31206                 case 'tall' :
31207                     
31208                     if(box.length){
31209                         boxes.push(box);
31210                         box = [];
31211                     }
31212                     
31213                     boxes.push([item]);
31214                     
31215                     break;
31216                     
31217                 case 'xs' :
31218                 case 'sm' :
31219                 case 'wide' :
31220                 case 'wide-thin' :
31221                     
31222                     box.push(item);
31223                     
31224                     break;
31225                 default :
31226                     break;
31227                     
31228             }
31229             
31230         }, this);
31231         
31232         if(box.length){
31233             boxes.push(box);
31234             box = [];
31235         }
31236         
31237         var filterPattern = function(box, length)
31238         {
31239             if(!box.length){
31240                 return;
31241             }
31242             
31243             var match = false;
31244             
31245             var pattern = box.slice(0, length);
31246             
31247             var format = [];
31248             
31249             Roo.each(pattern, function(i){
31250                 format.push(i.size);
31251             }, this);
31252             
31253             Roo.each(standard, function(s){
31254                 
31255                 if(String(s) != String(format)){
31256                     return;
31257                 }
31258                 
31259                 match = true;
31260                 return false;
31261                 
31262             }, this);
31263             
31264             if(!match && length == 1){
31265                 return;
31266             }
31267             
31268             if(!match){
31269                 filterPattern(box, length - 1);
31270                 return;
31271             }
31272                 
31273             queue.push(pattern);
31274
31275             box = box.slice(length, box.length);
31276
31277             filterPattern(box, 4);
31278
31279             return;
31280             
31281         }
31282         
31283         Roo.each(boxes, function(box, k){
31284             
31285             if(!box.length){
31286                 return;
31287             }
31288             
31289             if(box.length == 1){
31290                 queue.push(box);
31291                 return;
31292             }
31293             
31294             filterPattern(box, 4);
31295             
31296         }, this);
31297         
31298         
31299         var prune = [];
31300         
31301         var pos = this.el.getBox(true);
31302         
31303         var minX = pos.x;
31304         
31305         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31306         
31307         var hit_end = false;
31308         
31309         Roo.each(queue, function(box){
31310             
31311             if(hit_end){
31312                 
31313                 Roo.each(box, function(b){
31314                 
31315                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31316                     b.el.hide();
31317
31318                 }, this);
31319
31320                 return;
31321             }
31322             
31323             var mx = 0;
31324             
31325             Roo.each(box, function(b){
31326                 
31327                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31328                 b.el.show();
31329
31330                 mx = Math.max(mx, b.x);
31331                 
31332             }, this);
31333             
31334             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31335             
31336             if(maxX < minX){
31337                 
31338                 Roo.each(box, function(b){
31339                 
31340                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31341                     b.el.hide();
31342                     
31343                 }, this);
31344                 
31345                 hit_end = true;
31346                 
31347                 return;
31348             }
31349             
31350             prune.push(box);
31351             
31352         }, this);
31353         
31354         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31355     },
31356     
31357     /** Sets position of item in DOM
31358     * @param {Element} item
31359     * @param {Number} x - horizontal position
31360     * @param {Number} y - vertical position
31361     * @param {Boolean} isInstant - disables transitions
31362     */
31363     _processVerticalLayoutQueue : function( queue, isInstant )
31364     {
31365         var pos = this.el.getBox(true);
31366         var x = pos.x;
31367         var y = pos.y;
31368         var maxY = [];
31369         
31370         for (var i = 0; i < this.cols; i++){
31371             maxY[i] = pos.y;
31372         }
31373         
31374         Roo.each(queue, function(box, k){
31375             
31376             var col = k % this.cols;
31377             
31378             Roo.each(box, function(b,kk){
31379                 
31380                 b.el.position('absolute');
31381                 
31382                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31383                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31384                 
31385                 if(b.size == 'md-left' || b.size == 'md-right'){
31386                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31387                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31388                 }
31389                 
31390                 b.el.setWidth(width);
31391                 b.el.setHeight(height);
31392                 // iframe?
31393                 b.el.select('iframe',true).setSize(width,height);
31394                 
31395             }, this);
31396             
31397             for (var i = 0; i < this.cols; i++){
31398                 
31399                 if(maxY[i] < maxY[col]){
31400                     col = i;
31401                     continue;
31402                 }
31403                 
31404                 col = Math.min(col, i);
31405                 
31406             }
31407             
31408             x = pos.x + col * (this.colWidth + this.padWidth);
31409             
31410             y = maxY[col];
31411             
31412             var positions = [];
31413             
31414             switch (box.length){
31415                 case 1 :
31416                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31417                     break;
31418                 case 2 :
31419                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31420                     break;
31421                 case 3 :
31422                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31423                     break;
31424                 case 4 :
31425                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31426                     break;
31427                 default :
31428                     break;
31429             }
31430             
31431             Roo.each(box, function(b,kk){
31432                 
31433                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31434                 
31435                 var sz = b.el.getSize();
31436                 
31437                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31438                 
31439             }, this);
31440             
31441         }, this);
31442         
31443         var mY = 0;
31444         
31445         for (var i = 0; i < this.cols; i++){
31446             mY = Math.max(mY, maxY[i]);
31447         }
31448         
31449         this.el.setHeight(mY - pos.y);
31450         
31451     },
31452     
31453 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31454 //    {
31455 //        var pos = this.el.getBox(true);
31456 //        var x = pos.x;
31457 //        var y = pos.y;
31458 //        var maxX = pos.right;
31459 //        
31460 //        var maxHeight = 0;
31461 //        
31462 //        Roo.each(items, function(item, k){
31463 //            
31464 //            var c = k % 2;
31465 //            
31466 //            item.el.position('absolute');
31467 //                
31468 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31469 //
31470 //            item.el.setWidth(width);
31471 //
31472 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31473 //
31474 //            item.el.setHeight(height);
31475 //            
31476 //            if(c == 0){
31477 //                item.el.setXY([x, y], isInstant ? false : true);
31478 //            } else {
31479 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31480 //            }
31481 //            
31482 //            y = y + height + this.alternativePadWidth;
31483 //            
31484 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31485 //            
31486 //        }, this);
31487 //        
31488 //        this.el.setHeight(maxHeight);
31489 //        
31490 //    },
31491     
31492     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31493     {
31494         var pos = this.el.getBox(true);
31495         
31496         var minX = pos.x;
31497         var minY = pos.y;
31498         
31499         var maxX = pos.right;
31500         
31501         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31502         
31503         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31504         
31505         Roo.each(queue, function(box, k){
31506             
31507             Roo.each(box, function(b, kk){
31508                 
31509                 b.el.position('absolute');
31510                 
31511                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31512                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31513                 
31514                 if(b.size == 'md-left' || b.size == 'md-right'){
31515                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31516                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31517                 }
31518                 
31519                 b.el.setWidth(width);
31520                 b.el.setHeight(height);
31521                 
31522             }, this);
31523             
31524             if(!box.length){
31525                 return;
31526             }
31527             
31528             var positions = [];
31529             
31530             switch (box.length){
31531                 case 1 :
31532                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31533                     break;
31534                 case 2 :
31535                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31536                     break;
31537                 case 3 :
31538                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31539                     break;
31540                 case 4 :
31541                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31542                     break;
31543                 default :
31544                     break;
31545             }
31546             
31547             Roo.each(box, function(b,kk){
31548                 
31549                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31550                 
31551                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31552                 
31553             }, this);
31554             
31555         }, this);
31556         
31557     },
31558     
31559     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31560     {
31561         Roo.each(eItems, function(b,k){
31562             
31563             b.size = (k == 0) ? 'sm' : 'xs';
31564             b.x = (k == 0) ? 2 : 1;
31565             b.y = (k == 0) ? 2 : 1;
31566             
31567             b.el.position('absolute');
31568             
31569             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31570                 
31571             b.el.setWidth(width);
31572             
31573             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31574             
31575             b.el.setHeight(height);
31576             
31577         }, this);
31578
31579         var positions = [];
31580         
31581         positions.push({
31582             x : maxX - this.unitWidth * 2 - this.gutter,
31583             y : minY
31584         });
31585         
31586         positions.push({
31587             x : maxX - this.unitWidth,
31588             y : minY + (this.unitWidth + this.gutter) * 2
31589         });
31590         
31591         positions.push({
31592             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31593             y : minY
31594         });
31595         
31596         Roo.each(eItems, function(b,k){
31597             
31598             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31599
31600         }, this);
31601         
31602     },
31603     
31604     getVerticalOneBoxColPositions : function(x, y, box)
31605     {
31606         var pos = [];
31607         
31608         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31609         
31610         if(box[0].size == 'md-left'){
31611             rand = 0;
31612         }
31613         
31614         if(box[0].size == 'md-right'){
31615             rand = 1;
31616         }
31617         
31618         pos.push({
31619             x : x + (this.unitWidth + this.gutter) * rand,
31620             y : y
31621         });
31622         
31623         return pos;
31624     },
31625     
31626     getVerticalTwoBoxColPositions : function(x, y, box)
31627     {
31628         var pos = [];
31629         
31630         if(box[0].size == 'xs'){
31631             
31632             pos.push({
31633                 x : x,
31634                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31635             });
31636
31637             pos.push({
31638                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31639                 y : y
31640             });
31641             
31642             return pos;
31643             
31644         }
31645         
31646         pos.push({
31647             x : x,
31648             y : y
31649         });
31650
31651         pos.push({
31652             x : x + (this.unitWidth + this.gutter) * 2,
31653             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31654         });
31655         
31656         return pos;
31657         
31658     },
31659     
31660     getVerticalThreeBoxColPositions : function(x, y, box)
31661     {
31662         var pos = [];
31663         
31664         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31665             
31666             pos.push({
31667                 x : x,
31668                 y : y
31669             });
31670
31671             pos.push({
31672                 x : x + (this.unitWidth + this.gutter) * 1,
31673                 y : y
31674             });
31675             
31676             pos.push({
31677                 x : x + (this.unitWidth + this.gutter) * 2,
31678                 y : y
31679             });
31680             
31681             return pos;
31682             
31683         }
31684         
31685         if(box[0].size == 'xs' && box[1].size == 'xs'){
31686             
31687             pos.push({
31688                 x : x,
31689                 y : y
31690             });
31691
31692             pos.push({
31693                 x : x,
31694                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31695             });
31696             
31697             pos.push({
31698                 x : x + (this.unitWidth + this.gutter) * 1,
31699                 y : y
31700             });
31701             
31702             return pos;
31703             
31704         }
31705         
31706         pos.push({
31707             x : x,
31708             y : y
31709         });
31710
31711         pos.push({
31712             x : x + (this.unitWidth + this.gutter) * 2,
31713             y : y
31714         });
31715
31716         pos.push({
31717             x : x + (this.unitWidth + this.gutter) * 2,
31718             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31719         });
31720             
31721         return pos;
31722         
31723     },
31724     
31725     getVerticalFourBoxColPositions : function(x, y, box)
31726     {
31727         var pos = [];
31728         
31729         if(box[0].size == 'xs'){
31730             
31731             pos.push({
31732                 x : x,
31733                 y : y
31734             });
31735
31736             pos.push({
31737                 x : x,
31738                 y : y + (this.unitHeight + this.gutter) * 1
31739             });
31740             
31741             pos.push({
31742                 x : x,
31743                 y : y + (this.unitHeight + this.gutter) * 2
31744             });
31745             
31746             pos.push({
31747                 x : x + (this.unitWidth + this.gutter) * 1,
31748                 y : y
31749             });
31750             
31751             return pos;
31752             
31753         }
31754         
31755         pos.push({
31756             x : x,
31757             y : y
31758         });
31759
31760         pos.push({
31761             x : x + (this.unitWidth + this.gutter) * 2,
31762             y : y
31763         });
31764
31765         pos.push({
31766             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31767             y : y + (this.unitHeight + this.gutter) * 1
31768         });
31769
31770         pos.push({
31771             x : x + (this.unitWidth + this.gutter) * 2,
31772             y : y + (this.unitWidth + this.gutter) * 2
31773         });
31774
31775         return pos;
31776         
31777     },
31778     
31779     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31780     {
31781         var pos = [];
31782         
31783         if(box[0].size == 'md-left'){
31784             pos.push({
31785                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31786                 y : minY
31787             });
31788             
31789             return pos;
31790         }
31791         
31792         if(box[0].size == 'md-right'){
31793             pos.push({
31794                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31795                 y : minY + (this.unitWidth + this.gutter) * 1
31796             });
31797             
31798             return pos;
31799         }
31800         
31801         var rand = Math.floor(Math.random() * (4 - box[0].y));
31802         
31803         pos.push({
31804             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31805             y : minY + (this.unitWidth + this.gutter) * rand
31806         });
31807         
31808         return pos;
31809         
31810     },
31811     
31812     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31813     {
31814         var pos = [];
31815         
31816         if(box[0].size == 'xs'){
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) * (3 - box[1].y)
31826             });
31827             
31828             return pos;
31829             
31830         }
31831         
31832         pos.push({
31833             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31834             y : minY
31835         });
31836
31837         pos.push({
31838             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31839             y : minY + (this.unitWidth + this.gutter) * 2
31840         });
31841         
31842         return pos;
31843         
31844     },
31845     
31846     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31847     {
31848         var pos = [];
31849         
31850         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31851             
31852             pos.push({
31853                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31854                 y : minY
31855             });
31856
31857             pos.push({
31858                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31859                 y : minY + (this.unitWidth + this.gutter) * 1
31860             });
31861             
31862             pos.push({
31863                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31864                 y : minY + (this.unitWidth + this.gutter) * 2
31865             });
31866             
31867             return pos;
31868             
31869         }
31870         
31871         if(box[0].size == 'xs' && box[1].size == 'xs'){
31872             
31873             pos.push({
31874                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31875                 y : minY
31876             });
31877
31878             pos.push({
31879                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31880                 y : minY
31881             });
31882             
31883             pos.push({
31884                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31885                 y : minY + (this.unitWidth + this.gutter) * 1
31886             });
31887             
31888             return pos;
31889             
31890         }
31891         
31892         pos.push({
31893             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31894             y : minY
31895         });
31896
31897         pos.push({
31898             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31899             y : minY + (this.unitWidth + this.gutter) * 2
31900         });
31901
31902         pos.push({
31903             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31904             y : minY + (this.unitWidth + this.gutter) * 2
31905         });
31906             
31907         return pos;
31908         
31909     },
31910     
31911     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31912     {
31913         var pos = [];
31914         
31915         if(box[0].size == 'xs'){
31916             
31917             pos.push({
31918                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31919                 y : minY
31920             });
31921
31922             pos.push({
31923                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31924                 y : minY
31925             });
31926             
31927             pos.push({
31928                 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),
31929                 y : minY
31930             });
31931             
31932             pos.push({
31933                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31934                 y : minY + (this.unitWidth + this.gutter) * 1
31935             });
31936             
31937             return pos;
31938             
31939         }
31940         
31941         pos.push({
31942             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31943             y : minY
31944         });
31945         
31946         pos.push({
31947             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31948             y : minY + (this.unitWidth + this.gutter) * 2
31949         });
31950         
31951         pos.push({
31952             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31953             y : minY + (this.unitWidth + this.gutter) * 2
31954         });
31955         
31956         pos.push({
31957             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),
31958             y : minY + (this.unitWidth + this.gutter) * 2
31959         });
31960
31961         return pos;
31962         
31963     },
31964     
31965     /**
31966     * remove a Masonry Brick
31967     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31968     */
31969     removeBrick : function(brick_id)
31970     {
31971         if (!brick_id) {
31972             return;
31973         }
31974         
31975         for (var i = 0; i<this.bricks.length; i++) {
31976             if (this.bricks[i].id == brick_id) {
31977                 this.bricks.splice(i,1);
31978                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31979                 this.initial();
31980             }
31981         }
31982     },
31983     
31984     /**
31985     * adds a Masonry Brick
31986     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31987     */
31988     addBrick : function(cfg)
31989     {
31990         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31991         //this.register(cn);
31992         cn.parentId = this.id;
31993         cn.render(this.el);
31994         return cn;
31995     },
31996     
31997     /**
31998     * register a Masonry Brick
31999     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32000     */
32001     
32002     register : function(brick)
32003     {
32004         this.bricks.push(brick);
32005         brick.masonryId = this.id;
32006     },
32007     
32008     /**
32009     * clear all the Masonry Brick
32010     */
32011     clearAll : function()
32012     {
32013         this.bricks = [];
32014         //this.getChildContainer().dom.innerHTML = "";
32015         this.el.dom.innerHTML = '';
32016     },
32017     
32018     getSelected : function()
32019     {
32020         if (!this.selectedBrick) {
32021             return false;
32022         }
32023         
32024         return this.selectedBrick;
32025     }
32026 });
32027
32028 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32029     
32030     groups: {},
32031      /**
32032     * register a Masonry Layout
32033     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32034     */
32035     
32036     register : function(layout)
32037     {
32038         this.groups[layout.id] = layout;
32039     },
32040     /**
32041     * fetch a  Masonry Layout based on the masonry layout ID
32042     * @param {string} the masonry layout to add
32043     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32044     */
32045     
32046     get: function(layout_id) {
32047         if (typeof(this.groups[layout_id]) == 'undefined') {
32048             return false;
32049         }
32050         return this.groups[layout_id] ;
32051     }
32052     
32053     
32054     
32055 });
32056
32057  
32058
32059  /**
32060  *
32061  * This is based on 
32062  * http://masonry.desandro.com
32063  *
32064  * The idea is to render all the bricks based on vertical width...
32065  *
32066  * The original code extends 'outlayer' - we might need to use that....
32067  * 
32068  */
32069
32070
32071 /**
32072  * @class Roo.bootstrap.LayoutMasonryAuto
32073  * @extends Roo.bootstrap.Component
32074  * Bootstrap Layout Masonry class
32075  * 
32076  * @constructor
32077  * Create a new Element
32078  * @param {Object} config The config object
32079  */
32080
32081 Roo.bootstrap.LayoutMasonryAuto = function(config){
32082     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32083 };
32084
32085 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32086     
32087       /**
32088      * @cfg {Boolean} isFitWidth  - resize the width..
32089      */   
32090     isFitWidth : false,  // options..
32091     /**
32092      * @cfg {Boolean} isOriginLeft = left align?
32093      */   
32094     isOriginLeft : true,
32095     /**
32096      * @cfg {Boolean} isOriginTop = top align?
32097      */   
32098     isOriginTop : false,
32099     /**
32100      * @cfg {Boolean} isLayoutInstant = no animation?
32101      */   
32102     isLayoutInstant : false, // needed?
32103     /**
32104      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32105      */   
32106     isResizingContainer : true,
32107     /**
32108      * @cfg {Number} columnWidth  width of the columns 
32109      */   
32110     
32111     columnWidth : 0,
32112     
32113     /**
32114      * @cfg {Number} maxCols maximum number of columns
32115      */   
32116     
32117     maxCols: 0,
32118     /**
32119      * @cfg {Number} padHeight padding below box..
32120      */   
32121     
32122     padHeight : 10, 
32123     
32124     /**
32125      * @cfg {Boolean} isAutoInitial defalut true
32126      */   
32127     
32128     isAutoInitial : true, 
32129     
32130     // private?
32131     gutter : 0,
32132     
32133     containerWidth: 0,
32134     initialColumnWidth : 0,
32135     currentSize : null,
32136     
32137     colYs : null, // array.
32138     maxY : 0,
32139     padWidth: 10,
32140     
32141     
32142     tag: 'div',
32143     cls: '',
32144     bricks: null, //CompositeElement
32145     cols : 0, // array?
32146     // element : null, // wrapped now this.el
32147     _isLayoutInited : null, 
32148     
32149     
32150     getAutoCreate : function(){
32151         
32152         var cfg = {
32153             tag: this.tag,
32154             cls: 'blog-masonary-wrapper ' + this.cls,
32155             cn : {
32156                 cls : 'mas-boxes masonary'
32157             }
32158         };
32159         
32160         return cfg;
32161     },
32162     
32163     getChildContainer: function( )
32164     {
32165         if (this.boxesEl) {
32166             return this.boxesEl;
32167         }
32168         
32169         this.boxesEl = this.el.select('.mas-boxes').first();
32170         
32171         return this.boxesEl;
32172     },
32173     
32174     
32175     initEvents : function()
32176     {
32177         var _this = this;
32178         
32179         if(this.isAutoInitial){
32180             Roo.log('hook children rendered');
32181             this.on('childrenrendered', function() {
32182                 Roo.log('children rendered');
32183                 _this.initial();
32184             } ,this);
32185         }
32186         
32187     },
32188     
32189     initial : function()
32190     {
32191         this.reloadItems();
32192
32193         this.currentSize = this.el.getBox(true);
32194
32195         /// was window resize... - let's see if this works..
32196         Roo.EventManager.onWindowResize(this.resize, this); 
32197
32198         if(!this.isAutoInitial){
32199             this.layout();
32200             return;
32201         }
32202         
32203         this.layout.defer(500,this);
32204     },
32205     
32206     reloadItems: function()
32207     {
32208         this.bricks = this.el.select('.masonry-brick', true);
32209         
32210         this.bricks.each(function(b) {
32211             //Roo.log(b.getSize());
32212             if (!b.attr('originalwidth')) {
32213                 b.attr('originalwidth',  b.getSize().width);
32214             }
32215             
32216         });
32217         
32218         Roo.log(this.bricks.elements.length);
32219     },
32220     
32221     resize : function()
32222     {
32223         Roo.log('resize');
32224         var cs = this.el.getBox(true);
32225         
32226         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32227             Roo.log("no change in with or X");
32228             return;
32229         }
32230         this.currentSize = cs;
32231         this.layout();
32232     },
32233     
32234     layout : function()
32235     {
32236          Roo.log('layout');
32237         this._resetLayout();
32238         //this._manageStamps();
32239       
32240         // don't animate first layout
32241         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32242         this.layoutItems( isInstant );
32243       
32244         // flag for initalized
32245         this._isLayoutInited = true;
32246     },
32247     
32248     layoutItems : function( isInstant )
32249     {
32250         //var items = this._getItemsForLayout( this.items );
32251         // original code supports filtering layout items.. we just ignore it..
32252         
32253         this._layoutItems( this.bricks , isInstant );
32254       
32255         this._postLayout();
32256     },
32257     _layoutItems : function ( items , isInstant)
32258     {
32259        //this.fireEvent( 'layout', this, items );
32260     
32261
32262         if ( !items || !items.elements.length ) {
32263           // no items, emit event with empty array
32264             return;
32265         }
32266
32267         var queue = [];
32268         items.each(function(item) {
32269             Roo.log("layout item");
32270             Roo.log(item);
32271             // get x/y object from method
32272             var position = this._getItemLayoutPosition( item );
32273             // enqueue
32274             position.item = item;
32275             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32276             queue.push( position );
32277         }, this);
32278       
32279         this._processLayoutQueue( queue );
32280     },
32281     /** Sets position of item in DOM
32282     * @param {Element} item
32283     * @param {Number} x - horizontal position
32284     * @param {Number} y - vertical position
32285     * @param {Boolean} isInstant - disables transitions
32286     */
32287     _processLayoutQueue : function( queue )
32288     {
32289         for ( var i=0, len = queue.length; i < len; i++ ) {
32290             var obj = queue[i];
32291             obj.item.position('absolute');
32292             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32293         }
32294     },
32295       
32296     
32297     /**
32298     * Any logic you want to do after each layout,
32299     * i.e. size the container
32300     */
32301     _postLayout : function()
32302     {
32303         this.resizeContainer();
32304     },
32305     
32306     resizeContainer : function()
32307     {
32308         if ( !this.isResizingContainer ) {
32309             return;
32310         }
32311         var size = this._getContainerSize();
32312         if ( size ) {
32313             this.el.setSize(size.width,size.height);
32314             this.boxesEl.setSize(size.width,size.height);
32315         }
32316     },
32317     
32318     
32319     
32320     _resetLayout : function()
32321     {
32322         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32323         this.colWidth = this.el.getWidth();
32324         //this.gutter = this.el.getWidth(); 
32325         
32326         this.measureColumns();
32327
32328         // reset column Y
32329         var i = this.cols;
32330         this.colYs = [];
32331         while (i--) {
32332             this.colYs.push( 0 );
32333         }
32334     
32335         this.maxY = 0;
32336     },
32337
32338     measureColumns : function()
32339     {
32340         this.getContainerWidth();
32341       // if columnWidth is 0, default to outerWidth of first item
32342         if ( !this.columnWidth ) {
32343             var firstItem = this.bricks.first();
32344             Roo.log(firstItem);
32345             this.columnWidth  = this.containerWidth;
32346             if (firstItem && firstItem.attr('originalwidth') ) {
32347                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32348             }
32349             // columnWidth fall back to item of first element
32350             Roo.log("set column width?");
32351                         this.initialColumnWidth = this.columnWidth  ;
32352
32353             // if first elem has no width, default to size of container
32354             
32355         }
32356         
32357         
32358         if (this.initialColumnWidth) {
32359             this.columnWidth = this.initialColumnWidth;
32360         }
32361         
32362         
32363             
32364         // column width is fixed at the top - however if container width get's smaller we should
32365         // reduce it...
32366         
32367         // this bit calcs how man columns..
32368             
32369         var columnWidth = this.columnWidth += this.gutter;
32370       
32371         // calculate columns
32372         var containerWidth = this.containerWidth + this.gutter;
32373         
32374         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32375         // fix rounding errors, typically with gutters
32376         var excess = columnWidth - containerWidth % columnWidth;
32377         
32378         
32379         // if overshoot is less than a pixel, round up, otherwise floor it
32380         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32381         cols = Math[ mathMethod ]( cols );
32382         this.cols = Math.max( cols, 1 );
32383         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32384         
32385          // padding positioning..
32386         var totalColWidth = this.cols * this.columnWidth;
32387         var padavail = this.containerWidth - totalColWidth;
32388         // so for 2 columns - we need 3 'pads'
32389         
32390         var padNeeded = (1+this.cols) * this.padWidth;
32391         
32392         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32393         
32394         this.columnWidth += padExtra
32395         //this.padWidth = Math.floor(padavail /  ( this.cols));
32396         
32397         // adjust colum width so that padding is fixed??
32398         
32399         // we have 3 columns ... total = width * 3
32400         // we have X left over... that should be used by 
32401         
32402         //if (this.expandC) {
32403             
32404         //}
32405         
32406         
32407         
32408     },
32409     
32410     getContainerWidth : function()
32411     {
32412        /* // container is parent if fit width
32413         var container = this.isFitWidth ? this.element.parentNode : this.element;
32414         // check that this.size and size are there
32415         // IE8 triggers resize on body size change, so they might not be
32416         
32417         var size = getSize( container );  //FIXME
32418         this.containerWidth = size && size.innerWidth; //FIXME
32419         */
32420          
32421         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32422         
32423     },
32424     
32425     _getItemLayoutPosition : function( item )  // what is item?
32426     {
32427         // we resize the item to our columnWidth..
32428       
32429         item.setWidth(this.columnWidth);
32430         item.autoBoxAdjust  = false;
32431         
32432         var sz = item.getSize();
32433  
32434         // how many columns does this brick span
32435         var remainder = this.containerWidth % this.columnWidth;
32436         
32437         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32438         // round if off by 1 pixel, otherwise use ceil
32439         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32440         colSpan = Math.min( colSpan, this.cols );
32441         
32442         // normally this should be '1' as we dont' currently allow multi width columns..
32443         
32444         var colGroup = this._getColGroup( colSpan );
32445         // get the minimum Y value from the columns
32446         var minimumY = Math.min.apply( Math, colGroup );
32447         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32448         
32449         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32450          
32451         // position the brick
32452         var position = {
32453             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32454             y: this.currentSize.y + minimumY + this.padHeight
32455         };
32456         
32457         Roo.log(position);
32458         // apply setHeight to necessary columns
32459         var setHeight = minimumY + sz.height + this.padHeight;
32460         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32461         
32462         var setSpan = this.cols + 1 - colGroup.length;
32463         for ( var i = 0; i < setSpan; i++ ) {
32464           this.colYs[ shortColIndex + i ] = setHeight ;
32465         }
32466       
32467         return position;
32468     },
32469     
32470     /**
32471      * @param {Number} colSpan - number of columns the element spans
32472      * @returns {Array} colGroup
32473      */
32474     _getColGroup : function( colSpan )
32475     {
32476         if ( colSpan < 2 ) {
32477           // if brick spans only one column, use all the column Ys
32478           return this.colYs;
32479         }
32480       
32481         var colGroup = [];
32482         // how many different places could this brick fit horizontally
32483         var groupCount = this.cols + 1 - colSpan;
32484         // for each group potential horizontal position
32485         for ( var i = 0; i < groupCount; i++ ) {
32486           // make an array of colY values for that one group
32487           var groupColYs = this.colYs.slice( i, i + colSpan );
32488           // and get the max value of the array
32489           colGroup[i] = Math.max.apply( Math, groupColYs );
32490         }
32491         return colGroup;
32492     },
32493     /*
32494     _manageStamp : function( stamp )
32495     {
32496         var stampSize =  stamp.getSize();
32497         var offset = stamp.getBox();
32498         // get the columns that this stamp affects
32499         var firstX = this.isOriginLeft ? offset.x : offset.right;
32500         var lastX = firstX + stampSize.width;
32501         var firstCol = Math.floor( firstX / this.columnWidth );
32502         firstCol = Math.max( 0, firstCol );
32503         
32504         var lastCol = Math.floor( lastX / this.columnWidth );
32505         // lastCol should not go over if multiple of columnWidth #425
32506         lastCol -= lastX % this.columnWidth ? 0 : 1;
32507         lastCol = Math.min( this.cols - 1, lastCol );
32508         
32509         // set colYs to bottom of the stamp
32510         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32511             stampSize.height;
32512             
32513         for ( var i = firstCol; i <= lastCol; i++ ) {
32514           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32515         }
32516     },
32517     */
32518     
32519     _getContainerSize : function()
32520     {
32521         this.maxY = Math.max.apply( Math, this.colYs );
32522         var size = {
32523             height: this.maxY
32524         };
32525       
32526         if ( this.isFitWidth ) {
32527             size.width = this._getContainerFitWidth();
32528         }
32529       
32530         return size;
32531     },
32532     
32533     _getContainerFitWidth : function()
32534     {
32535         var unusedCols = 0;
32536         // count unused columns
32537         var i = this.cols;
32538         while ( --i ) {
32539           if ( this.colYs[i] !== 0 ) {
32540             break;
32541           }
32542           unusedCols++;
32543         }
32544         // fit container to columns that have been used
32545         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32546     },
32547     
32548     needsResizeLayout : function()
32549     {
32550         var previousWidth = this.containerWidth;
32551         this.getContainerWidth();
32552         return previousWidth !== this.containerWidth;
32553     }
32554  
32555 });
32556
32557  
32558
32559  /*
32560  * - LGPL
32561  *
32562  * element
32563  * 
32564  */
32565
32566 /**
32567  * @class Roo.bootstrap.MasonryBrick
32568  * @extends Roo.bootstrap.Component
32569  * Bootstrap MasonryBrick class
32570  * 
32571  * @constructor
32572  * Create a new MasonryBrick
32573  * @param {Object} config The config object
32574  */
32575
32576 Roo.bootstrap.MasonryBrick = function(config){
32577     
32578     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32579     
32580     Roo.bootstrap.MasonryBrick.register(this);
32581     
32582     this.addEvents({
32583         // raw events
32584         /**
32585          * @event click
32586          * When a MasonryBrick is clcik
32587          * @param {Roo.bootstrap.MasonryBrick} this
32588          * @param {Roo.EventObject} e
32589          */
32590         "click" : true
32591     });
32592 };
32593
32594 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32595     
32596     /**
32597      * @cfg {String} title
32598      */   
32599     title : '',
32600     /**
32601      * @cfg {String} html
32602      */   
32603     html : '',
32604     /**
32605      * @cfg {String} bgimage
32606      */   
32607     bgimage : '',
32608     /**
32609      * @cfg {String} videourl
32610      */   
32611     videourl : '',
32612     /**
32613      * @cfg {String} cls
32614      */   
32615     cls : '',
32616     /**
32617      * @cfg {String} href
32618      */   
32619     href : '',
32620     /**
32621      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32622      */   
32623     size : 'xs',
32624     
32625     /**
32626      * @cfg {String} placetitle (center|bottom)
32627      */   
32628     placetitle : '',
32629     
32630     /**
32631      * @cfg {Boolean} isFitContainer defalut true
32632      */   
32633     isFitContainer : true, 
32634     
32635     /**
32636      * @cfg {Boolean} preventDefault defalut false
32637      */   
32638     preventDefault : false, 
32639     
32640     /**
32641      * @cfg {Boolean} inverse defalut false
32642      */   
32643     maskInverse : false, 
32644     
32645     getAutoCreate : function()
32646     {
32647         if(!this.isFitContainer){
32648             return this.getSplitAutoCreate();
32649         }
32650         
32651         var cls = 'masonry-brick masonry-brick-full';
32652         
32653         if(this.href.length){
32654             cls += ' masonry-brick-link';
32655         }
32656         
32657         if(this.bgimage.length){
32658             cls += ' masonry-brick-image';
32659         }
32660         
32661         if(this.maskInverse){
32662             cls += ' mask-inverse';
32663         }
32664         
32665         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32666             cls += ' enable-mask';
32667         }
32668         
32669         if(this.size){
32670             cls += ' masonry-' + this.size + '-brick';
32671         }
32672         
32673         if(this.placetitle.length){
32674             
32675             switch (this.placetitle) {
32676                 case 'center' :
32677                     cls += ' masonry-center-title';
32678                     break;
32679                 case 'bottom' :
32680                     cls += ' masonry-bottom-title';
32681                     break;
32682                 default:
32683                     break;
32684             }
32685             
32686         } else {
32687             if(!this.html.length && !this.bgimage.length){
32688                 cls += ' masonry-center-title';
32689             }
32690
32691             if(!this.html.length && this.bgimage.length){
32692                 cls += ' masonry-bottom-title';
32693             }
32694         }
32695         
32696         if(this.cls){
32697             cls += ' ' + this.cls;
32698         }
32699         
32700         var cfg = {
32701             tag: (this.href.length) ? 'a' : 'div',
32702             cls: cls,
32703             cn: [
32704                 {
32705                     tag: 'div',
32706                     cls: 'masonry-brick-mask'
32707                 },
32708                 {
32709                     tag: 'div',
32710                     cls: 'masonry-brick-paragraph',
32711                     cn: []
32712                 }
32713             ]
32714         };
32715         
32716         if(this.href.length){
32717             cfg.href = this.href;
32718         }
32719         
32720         var cn = cfg.cn[1].cn;
32721         
32722         if(this.title.length){
32723             cn.push({
32724                 tag: 'h4',
32725                 cls: 'masonry-brick-title',
32726                 html: this.title
32727             });
32728         }
32729         
32730         if(this.html.length){
32731             cn.push({
32732                 tag: 'p',
32733                 cls: 'masonry-brick-text',
32734                 html: this.html
32735             });
32736         }
32737         
32738         if (!this.title.length && !this.html.length) {
32739             cfg.cn[1].cls += ' hide';
32740         }
32741         
32742         if(this.bgimage.length){
32743             cfg.cn.push({
32744                 tag: 'img',
32745                 cls: 'masonry-brick-image-view',
32746                 src: this.bgimage
32747             });
32748         }
32749         
32750         if(this.videourl.length){
32751             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32752             // youtube support only?
32753             cfg.cn.push({
32754                 tag: 'iframe',
32755                 cls: 'masonry-brick-image-view',
32756                 src: vurl,
32757                 frameborder : 0,
32758                 allowfullscreen : true
32759             });
32760         }
32761         
32762         return cfg;
32763         
32764     },
32765     
32766     getSplitAutoCreate : function()
32767     {
32768         var cls = 'masonry-brick masonry-brick-split';
32769         
32770         if(this.href.length){
32771             cls += ' masonry-brick-link';
32772         }
32773         
32774         if(this.bgimage.length){
32775             cls += ' masonry-brick-image';
32776         }
32777         
32778         if(this.size){
32779             cls += ' masonry-' + this.size + '-brick';
32780         }
32781         
32782         switch (this.placetitle) {
32783             case 'center' :
32784                 cls += ' masonry-center-title';
32785                 break;
32786             case 'bottom' :
32787                 cls += ' masonry-bottom-title';
32788                 break;
32789             default:
32790                 if(!this.bgimage.length){
32791                     cls += ' masonry-center-title';
32792                 }
32793
32794                 if(this.bgimage.length){
32795                     cls += ' masonry-bottom-title';
32796                 }
32797                 break;
32798         }
32799         
32800         if(this.cls){
32801             cls += ' ' + this.cls;
32802         }
32803         
32804         var cfg = {
32805             tag: (this.href.length) ? 'a' : 'div',
32806             cls: cls,
32807             cn: [
32808                 {
32809                     tag: 'div',
32810                     cls: 'masonry-brick-split-head',
32811                     cn: [
32812                         {
32813                             tag: 'div',
32814                             cls: 'masonry-brick-paragraph',
32815                             cn: []
32816                         }
32817                     ]
32818                 },
32819                 {
32820                     tag: 'div',
32821                     cls: 'masonry-brick-split-body',
32822                     cn: []
32823                 }
32824             ]
32825         };
32826         
32827         if(this.href.length){
32828             cfg.href = this.href;
32829         }
32830         
32831         if(this.title.length){
32832             cfg.cn[0].cn[0].cn.push({
32833                 tag: 'h4',
32834                 cls: 'masonry-brick-title',
32835                 html: this.title
32836             });
32837         }
32838         
32839         if(this.html.length){
32840             cfg.cn[1].cn.push({
32841                 tag: 'p',
32842                 cls: 'masonry-brick-text',
32843                 html: this.html
32844             });
32845         }
32846
32847         if(this.bgimage.length){
32848             cfg.cn[0].cn.push({
32849                 tag: 'img',
32850                 cls: 'masonry-brick-image-view',
32851                 src: this.bgimage
32852             });
32853         }
32854         
32855         if(this.videourl.length){
32856             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32857             // youtube support only?
32858             cfg.cn[0].cn.cn.push({
32859                 tag: 'iframe',
32860                 cls: 'masonry-brick-image-view',
32861                 src: vurl,
32862                 frameborder : 0,
32863                 allowfullscreen : true
32864             });
32865         }
32866         
32867         return cfg;
32868     },
32869     
32870     initEvents: function() 
32871     {
32872         switch (this.size) {
32873             case 'xs' :
32874                 this.x = 1;
32875                 this.y = 1;
32876                 break;
32877             case 'sm' :
32878                 this.x = 2;
32879                 this.y = 2;
32880                 break;
32881             case 'md' :
32882             case 'md-left' :
32883             case 'md-right' :
32884                 this.x = 3;
32885                 this.y = 3;
32886                 break;
32887             case 'tall' :
32888                 this.x = 2;
32889                 this.y = 3;
32890                 break;
32891             case 'wide' :
32892                 this.x = 3;
32893                 this.y = 2;
32894                 break;
32895             case 'wide-thin' :
32896                 this.x = 3;
32897                 this.y = 1;
32898                 break;
32899                         
32900             default :
32901                 break;
32902         }
32903         
32904         if(Roo.isTouch){
32905             this.el.on('touchstart', this.onTouchStart, this);
32906             this.el.on('touchmove', this.onTouchMove, this);
32907             this.el.on('touchend', this.onTouchEnd, this);
32908             this.el.on('contextmenu', this.onContextMenu, this);
32909         } else {
32910             this.el.on('mouseenter'  ,this.enter, this);
32911             this.el.on('mouseleave', this.leave, this);
32912             this.el.on('click', this.onClick, this);
32913         }
32914         
32915         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32916             this.parent().bricks.push(this);   
32917         }
32918         
32919     },
32920     
32921     onClick: function(e, el)
32922     {
32923         var time = this.endTimer - this.startTimer;
32924         // Roo.log(e.preventDefault());
32925         if(Roo.isTouch){
32926             if(time > 1000){
32927                 e.preventDefault();
32928                 return;
32929             }
32930         }
32931         
32932         if(!this.preventDefault){
32933             return;
32934         }
32935         
32936         e.preventDefault();
32937         
32938         if (this.activeClass != '') {
32939             this.selectBrick();
32940         }
32941         
32942         this.fireEvent('click', this, e);
32943     },
32944     
32945     enter: function(e, el)
32946     {
32947         e.preventDefault();
32948         
32949         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32950             return;
32951         }
32952         
32953         if(this.bgimage.length && this.html.length){
32954             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32955         }
32956     },
32957     
32958     leave: function(e, el)
32959     {
32960         e.preventDefault();
32961         
32962         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32963             return;
32964         }
32965         
32966         if(this.bgimage.length && this.html.length){
32967             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32968         }
32969     },
32970     
32971     onTouchStart: function(e, el)
32972     {
32973 //        e.preventDefault();
32974         
32975         this.touchmoved = false;
32976         
32977         if(!this.isFitContainer){
32978             return;
32979         }
32980         
32981         if(!this.bgimage.length || !this.html.length){
32982             return;
32983         }
32984         
32985         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32986         
32987         this.timer = new Date().getTime();
32988         
32989     },
32990     
32991     onTouchMove: function(e, el)
32992     {
32993         this.touchmoved = true;
32994     },
32995     
32996     onContextMenu : function(e,el)
32997     {
32998         e.preventDefault();
32999         e.stopPropagation();
33000         return false;
33001     },
33002     
33003     onTouchEnd: function(e, el)
33004     {
33005 //        e.preventDefault();
33006         
33007         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33008         
33009             this.leave(e,el);
33010             
33011             return;
33012         }
33013         
33014         if(!this.bgimage.length || !this.html.length){
33015             
33016             if(this.href.length){
33017                 window.location.href = this.href;
33018             }
33019             
33020             return;
33021         }
33022         
33023         if(!this.isFitContainer){
33024             return;
33025         }
33026         
33027         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33028         
33029         window.location.href = this.href;
33030     },
33031     
33032     //selection on single brick only
33033     selectBrick : function() {
33034         
33035         if (!this.parentId) {
33036             return;
33037         }
33038         
33039         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33040         var index = m.selectedBrick.indexOf(this.id);
33041         
33042         if ( index > -1) {
33043             m.selectedBrick.splice(index,1);
33044             this.el.removeClass(this.activeClass);
33045             return;
33046         }
33047         
33048         for(var i = 0; i < m.selectedBrick.length; i++) {
33049             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33050             b.el.removeClass(b.activeClass);
33051         }
33052         
33053         m.selectedBrick = [];
33054         
33055         m.selectedBrick.push(this.id);
33056         this.el.addClass(this.activeClass);
33057         return;
33058     },
33059     
33060     isSelected : function(){
33061         return this.el.hasClass(this.activeClass);
33062         
33063     }
33064 });
33065
33066 Roo.apply(Roo.bootstrap.MasonryBrick, {
33067     
33068     //groups: {},
33069     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33070      /**
33071     * register a Masonry Brick
33072     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33073     */
33074     
33075     register : function(brick)
33076     {
33077         //this.groups[brick.id] = brick;
33078         this.groups.add(brick.id, brick);
33079     },
33080     /**
33081     * fetch a  masonry brick based on the masonry brick ID
33082     * @param {string} the masonry brick to add
33083     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33084     */
33085     
33086     get: function(brick_id) 
33087     {
33088         // if (typeof(this.groups[brick_id]) == 'undefined') {
33089         //     return false;
33090         // }
33091         // return this.groups[brick_id] ;
33092         
33093         if(this.groups.key(brick_id)) {
33094             return this.groups.key(brick_id);
33095         }
33096         
33097         return false;
33098     }
33099     
33100     
33101     
33102 });
33103
33104  /*
33105  * - LGPL
33106  *
33107  * element
33108  * 
33109  */
33110
33111 /**
33112  * @class Roo.bootstrap.Brick
33113  * @extends Roo.bootstrap.Component
33114  * Bootstrap Brick class
33115  * 
33116  * @constructor
33117  * Create a new Brick
33118  * @param {Object} config The config object
33119  */
33120
33121 Roo.bootstrap.Brick = function(config){
33122     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33123     
33124     this.addEvents({
33125         // raw events
33126         /**
33127          * @event click
33128          * When a Brick is click
33129          * @param {Roo.bootstrap.Brick} this
33130          * @param {Roo.EventObject} e
33131          */
33132         "click" : true
33133     });
33134 };
33135
33136 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33137     
33138     /**
33139      * @cfg {String} title
33140      */   
33141     title : '',
33142     /**
33143      * @cfg {String} html
33144      */   
33145     html : '',
33146     /**
33147      * @cfg {String} bgimage
33148      */   
33149     bgimage : '',
33150     /**
33151      * @cfg {String} cls
33152      */   
33153     cls : '',
33154     /**
33155      * @cfg {String} href
33156      */   
33157     href : '',
33158     /**
33159      * @cfg {String} video
33160      */   
33161     video : '',
33162     /**
33163      * @cfg {Boolean} square
33164      */   
33165     square : true,
33166     
33167     getAutoCreate : function()
33168     {
33169         var cls = 'roo-brick';
33170         
33171         if(this.href.length){
33172             cls += ' roo-brick-link';
33173         }
33174         
33175         if(this.bgimage.length){
33176             cls += ' roo-brick-image';
33177         }
33178         
33179         if(!this.html.length && !this.bgimage.length){
33180             cls += ' roo-brick-center-title';
33181         }
33182         
33183         if(!this.html.length && this.bgimage.length){
33184             cls += ' roo-brick-bottom-title';
33185         }
33186         
33187         if(this.cls){
33188             cls += ' ' + this.cls;
33189         }
33190         
33191         var cfg = {
33192             tag: (this.href.length) ? 'a' : 'div',
33193             cls: cls,
33194             cn: [
33195                 {
33196                     tag: 'div',
33197                     cls: 'roo-brick-paragraph',
33198                     cn: []
33199                 }
33200             ]
33201         };
33202         
33203         if(this.href.length){
33204             cfg.href = this.href;
33205         }
33206         
33207         var cn = cfg.cn[0].cn;
33208         
33209         if(this.title.length){
33210             cn.push({
33211                 tag: 'h4',
33212                 cls: 'roo-brick-title',
33213                 html: this.title
33214             });
33215         }
33216         
33217         if(this.html.length){
33218             cn.push({
33219                 tag: 'p',
33220                 cls: 'roo-brick-text',
33221                 html: this.html
33222             });
33223         } else {
33224             cn.cls += ' hide';
33225         }
33226         
33227         if(this.bgimage.length){
33228             cfg.cn.push({
33229                 tag: 'img',
33230                 cls: 'roo-brick-image-view',
33231                 src: this.bgimage
33232             });
33233         }
33234         
33235         return cfg;
33236     },
33237     
33238     initEvents: function() 
33239     {
33240         if(this.title.length || this.html.length){
33241             this.el.on('mouseenter'  ,this.enter, this);
33242             this.el.on('mouseleave', this.leave, this);
33243         }
33244         
33245         Roo.EventManager.onWindowResize(this.resize, this); 
33246         
33247         if(this.bgimage.length){
33248             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33249             this.imageEl.on('load', this.onImageLoad, this);
33250             return;
33251         }
33252         
33253         this.resize();
33254     },
33255     
33256     onImageLoad : function()
33257     {
33258         this.resize();
33259     },
33260     
33261     resize : function()
33262     {
33263         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33264         
33265         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33266         
33267         if(this.bgimage.length){
33268             var image = this.el.select('.roo-brick-image-view', true).first();
33269             
33270             image.setWidth(paragraph.getWidth());
33271             
33272             if(this.square){
33273                 image.setHeight(paragraph.getWidth());
33274             }
33275             
33276             this.el.setHeight(image.getHeight());
33277             paragraph.setHeight(image.getHeight());
33278             
33279         }
33280         
33281     },
33282     
33283     enter: function(e, el)
33284     {
33285         e.preventDefault();
33286         
33287         if(this.bgimage.length){
33288             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33289             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33290         }
33291     },
33292     
33293     leave: function(e, el)
33294     {
33295         e.preventDefault();
33296         
33297         if(this.bgimage.length){
33298             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33299             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33300         }
33301     }
33302     
33303 });
33304
33305  
33306
33307  /*
33308  * - LGPL
33309  *
33310  * Number field 
33311  */
33312
33313 /**
33314  * @class Roo.bootstrap.NumberField
33315  * @extends Roo.bootstrap.Input
33316  * Bootstrap NumberField class
33317  * 
33318  * 
33319  * 
33320  * 
33321  * @constructor
33322  * Create a new NumberField
33323  * @param {Object} config The config object
33324  */
33325
33326 Roo.bootstrap.NumberField = function(config){
33327     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33328 };
33329
33330 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33331     
33332     /**
33333      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33334      */
33335     allowDecimals : true,
33336     /**
33337      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33338      */
33339     decimalSeparator : ".",
33340     /**
33341      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33342      */
33343     decimalPrecision : 2,
33344     /**
33345      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33346      */
33347     allowNegative : true,
33348     
33349     /**
33350      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33351      */
33352     allowZero: true,
33353     /**
33354      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33355      */
33356     minValue : Number.NEGATIVE_INFINITY,
33357     /**
33358      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33359      */
33360     maxValue : Number.MAX_VALUE,
33361     /**
33362      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33363      */
33364     minText : "The minimum value for this field is {0}",
33365     /**
33366      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33367      */
33368     maxText : "The maximum value for this field is {0}",
33369     /**
33370      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33371      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33372      */
33373     nanText : "{0} is not a valid number",
33374     /**
33375      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33376      */
33377     thousandsDelimiter : false,
33378     /**
33379      * @cfg {String} valueAlign alignment of value
33380      */
33381     valueAlign : "left",
33382
33383     getAutoCreate : function()
33384     {
33385         var hiddenInput = {
33386             tag: 'input',
33387             type: 'hidden',
33388             id: Roo.id(),
33389             cls: 'hidden-number-input'
33390         };
33391         
33392         if (this.name) {
33393             hiddenInput.name = this.name;
33394         }
33395         
33396         this.name = '';
33397         
33398         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33399         
33400         this.name = hiddenInput.name;
33401         
33402         if(cfg.cn.length > 0) {
33403             cfg.cn.push(hiddenInput);
33404         }
33405         
33406         return cfg;
33407     },
33408
33409     // private
33410     initEvents : function()
33411     {   
33412         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33413         
33414         var allowed = "0123456789";
33415         
33416         if(this.allowDecimals){
33417             allowed += this.decimalSeparator;
33418         }
33419         
33420         if(this.allowNegative){
33421             allowed += "-";
33422         }
33423         
33424         if(this.thousandsDelimiter) {
33425             allowed += ",";
33426         }
33427         
33428         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33429         
33430         var keyPress = function(e){
33431             
33432             var k = e.getKey();
33433             
33434             var c = e.getCharCode();
33435             
33436             if(
33437                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33438                     allowed.indexOf(String.fromCharCode(c)) === -1
33439             ){
33440                 e.stopEvent();
33441                 return;
33442             }
33443             
33444             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33445                 return;
33446             }
33447             
33448             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33449                 e.stopEvent();
33450             }
33451         };
33452         
33453         this.el.on("keypress", keyPress, this);
33454     },
33455     
33456     validateValue : function(value)
33457     {
33458         
33459         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33460             return false;
33461         }
33462         
33463         var num = this.parseValue(value);
33464         
33465         if(isNaN(num)){
33466             this.markInvalid(String.format(this.nanText, value));
33467             return false;
33468         }
33469         
33470         if(num < this.minValue){
33471             this.markInvalid(String.format(this.minText, this.minValue));
33472             return false;
33473         }
33474         
33475         if(num > this.maxValue){
33476             this.markInvalid(String.format(this.maxText, this.maxValue));
33477             return false;
33478         }
33479         
33480         return true;
33481     },
33482
33483     getValue : function()
33484     {
33485         var v = this.hiddenEl().getValue();
33486         
33487         return this.fixPrecision(this.parseValue(v));
33488     },
33489
33490     parseValue : function(value)
33491     {
33492         if(this.thousandsDelimiter) {
33493             value += "";
33494             r = new RegExp(",", "g");
33495             value = value.replace(r, "");
33496         }
33497         
33498         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33499         return isNaN(value) ? '' : value;
33500     },
33501
33502     fixPrecision : function(value)
33503     {
33504         if(this.thousandsDelimiter) {
33505             value += "";
33506             r = new RegExp(",", "g");
33507             value = value.replace(r, "");
33508         }
33509         
33510         var nan = isNaN(value);
33511         
33512         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33513             return nan ? '' : value;
33514         }
33515         return parseFloat(value).toFixed(this.decimalPrecision);
33516     },
33517
33518     setValue : function(v)
33519     {
33520         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33521         
33522         this.value = v;
33523         
33524         if(this.rendered){
33525             
33526             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33527             
33528             this.inputEl().dom.value = (v == '') ? '' :
33529                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33530             
33531             if(!this.allowZero && v === '0') {
33532                 this.hiddenEl().dom.value = '';
33533                 this.inputEl().dom.value = '';
33534             }
33535             
33536             this.validate();
33537         }
33538     },
33539
33540     decimalPrecisionFcn : function(v)
33541     {
33542         return Math.floor(v);
33543     },
33544
33545     beforeBlur : function()
33546     {
33547         var v = this.parseValue(this.getRawValue());
33548         
33549         if(v || v === 0 || v === ''){
33550             this.setValue(v);
33551         }
33552     },
33553     
33554     hiddenEl : function()
33555     {
33556         return this.el.select('input.hidden-number-input',true).first();
33557     }
33558     
33559 });
33560
33561  
33562
33563 /*
33564 * Licence: LGPL
33565 */
33566
33567 /**
33568  * @class Roo.bootstrap.DocumentSlider
33569  * @extends Roo.bootstrap.Component
33570  * Bootstrap DocumentSlider class
33571  * 
33572  * @constructor
33573  * Create a new DocumentViewer
33574  * @param {Object} config The config object
33575  */
33576
33577 Roo.bootstrap.DocumentSlider = function(config){
33578     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33579     
33580     this.files = [];
33581     
33582     this.addEvents({
33583         /**
33584          * @event initial
33585          * Fire after initEvent
33586          * @param {Roo.bootstrap.DocumentSlider} this
33587          */
33588         "initial" : true,
33589         /**
33590          * @event update
33591          * Fire after update
33592          * @param {Roo.bootstrap.DocumentSlider} this
33593          */
33594         "update" : true,
33595         /**
33596          * @event click
33597          * Fire after click
33598          * @param {Roo.bootstrap.DocumentSlider} this
33599          */
33600         "click" : true
33601     });
33602 };
33603
33604 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33605     
33606     files : false,
33607     
33608     indicator : 0,
33609     
33610     getAutoCreate : function()
33611     {
33612         var cfg = {
33613             tag : 'div',
33614             cls : 'roo-document-slider',
33615             cn : [
33616                 {
33617                     tag : 'div',
33618                     cls : 'roo-document-slider-header',
33619                     cn : [
33620                         {
33621                             tag : 'div',
33622                             cls : 'roo-document-slider-header-title'
33623                         }
33624                     ]
33625                 },
33626                 {
33627                     tag : 'div',
33628                     cls : 'roo-document-slider-body',
33629                     cn : [
33630                         {
33631                             tag : 'div',
33632                             cls : 'roo-document-slider-prev',
33633                             cn : [
33634                                 {
33635                                     tag : 'i',
33636                                     cls : 'fa fa-chevron-left'
33637                                 }
33638                             ]
33639                         },
33640                         {
33641                             tag : 'div',
33642                             cls : 'roo-document-slider-thumb',
33643                             cn : [
33644                                 {
33645                                     tag : 'img',
33646                                     cls : 'roo-document-slider-image'
33647                                 }
33648                             ]
33649                         },
33650                         {
33651                             tag : 'div',
33652                             cls : 'roo-document-slider-next',
33653                             cn : [
33654                                 {
33655                                     tag : 'i',
33656                                     cls : 'fa fa-chevron-right'
33657                                 }
33658                             ]
33659                         }
33660                     ]
33661                 }
33662             ]
33663         };
33664         
33665         return cfg;
33666     },
33667     
33668     initEvents : function()
33669     {
33670         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33671         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33672         
33673         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33674         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33675         
33676         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33677         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33678         
33679         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33680         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33681         
33682         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33683         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33684         
33685         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33686         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33687         
33688         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33689         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33690         
33691         this.thumbEl.on('click', this.onClick, this);
33692         
33693         this.prevIndicator.on('click', this.prev, this);
33694         
33695         this.nextIndicator.on('click', this.next, this);
33696         
33697     },
33698     
33699     initial : function()
33700     {
33701         if(this.files.length){
33702             this.indicator = 1;
33703             this.update()
33704         }
33705         
33706         this.fireEvent('initial', this);
33707     },
33708     
33709     update : function()
33710     {
33711         this.imageEl.attr('src', this.files[this.indicator - 1]);
33712         
33713         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33714         
33715         this.prevIndicator.show();
33716         
33717         if(this.indicator == 1){
33718             this.prevIndicator.hide();
33719         }
33720         
33721         this.nextIndicator.show();
33722         
33723         if(this.indicator == this.files.length){
33724             this.nextIndicator.hide();
33725         }
33726         
33727         this.thumbEl.scrollTo('top');
33728         
33729         this.fireEvent('update', this);
33730     },
33731     
33732     onClick : function(e)
33733     {
33734         e.preventDefault();
33735         
33736         this.fireEvent('click', this);
33737     },
33738     
33739     prev : function(e)
33740     {
33741         e.preventDefault();
33742         
33743         this.indicator = Math.max(1, this.indicator - 1);
33744         
33745         this.update();
33746     },
33747     
33748     next : function(e)
33749     {
33750         e.preventDefault();
33751         
33752         this.indicator = Math.min(this.files.length, this.indicator + 1);
33753         
33754         this.update();
33755     }
33756 });
33757 /*
33758  * - LGPL
33759  *
33760  * RadioSet
33761  *
33762  *
33763  */
33764
33765 /**
33766  * @class Roo.bootstrap.RadioSet
33767  * @extends Roo.bootstrap.Input
33768  * Bootstrap RadioSet class
33769  * @cfg {String} indicatorpos (left|right) default left
33770  * @cfg {Boolean} inline (true|false) inline the element (default true)
33771  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33772  * @constructor
33773  * Create a new RadioSet
33774  * @param {Object} config The config object
33775  */
33776
33777 Roo.bootstrap.RadioSet = function(config){
33778     
33779     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33780     
33781     this.radioes = [];
33782     
33783     Roo.bootstrap.RadioSet.register(this);
33784     
33785     this.addEvents({
33786         /**
33787         * @event check
33788         * Fires when the element is checked or unchecked.
33789         * @param {Roo.bootstrap.RadioSet} this This radio
33790         * @param {Roo.bootstrap.Radio} item The checked item
33791         */
33792        check : true,
33793        /**
33794         * @event click
33795         * Fires when the element is click.
33796         * @param {Roo.bootstrap.RadioSet} this This radio set
33797         * @param {Roo.bootstrap.Radio} item The checked item
33798         * @param {Roo.EventObject} e The event object
33799         */
33800        click : true
33801     });
33802     
33803 };
33804
33805 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33806
33807     radioes : false,
33808     
33809     inline : true,
33810     
33811     weight : '',
33812     
33813     indicatorpos : 'left',
33814     
33815     getAutoCreate : function()
33816     {
33817         var label = {
33818             tag : 'label',
33819             cls : 'roo-radio-set-label',
33820             cn : [
33821                 {
33822                     tag : 'span',
33823                     html : this.fieldLabel
33824                 }
33825             ]
33826         };
33827         
33828         if(this.indicatorpos == 'left'){
33829             label.cn.unshift({
33830                 tag : 'i',
33831                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33832                 tooltip : 'This field is required'
33833             });
33834         } else {
33835             label.cn.push({
33836                 tag : 'i',
33837                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33838                 tooltip : 'This field is required'
33839             });
33840         }
33841         
33842         var items = {
33843             tag : 'div',
33844             cls : 'roo-radio-set-items'
33845         };
33846         
33847         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33848         
33849         if (align === 'left' && this.fieldLabel.length) {
33850             
33851             items = {
33852                 cls : "roo-radio-set-right", 
33853                 cn: [
33854                     items
33855                 ]
33856             };
33857             
33858             if(this.labelWidth > 12){
33859                 label.style = "width: " + this.labelWidth + 'px';
33860             }
33861             
33862             if(this.labelWidth < 13 && this.labelmd == 0){
33863                 this.labelmd = this.labelWidth;
33864             }
33865             
33866             if(this.labellg > 0){
33867                 label.cls += ' col-lg-' + this.labellg;
33868                 items.cls += ' col-lg-' + (12 - this.labellg);
33869             }
33870             
33871             if(this.labelmd > 0){
33872                 label.cls += ' col-md-' + this.labelmd;
33873                 items.cls += ' col-md-' + (12 - this.labelmd);
33874             }
33875             
33876             if(this.labelsm > 0){
33877                 label.cls += ' col-sm-' + this.labelsm;
33878                 items.cls += ' col-sm-' + (12 - this.labelsm);
33879             }
33880             
33881             if(this.labelxs > 0){
33882                 label.cls += ' col-xs-' + this.labelxs;
33883                 items.cls += ' col-xs-' + (12 - this.labelxs);
33884             }
33885         }
33886         
33887         var cfg = {
33888             tag : 'div',
33889             cls : 'roo-radio-set',
33890             cn : [
33891                 {
33892                     tag : 'input',
33893                     cls : 'roo-radio-set-input',
33894                     type : 'hidden',
33895                     name : this.name,
33896                     value : this.value ? this.value :  ''
33897                 },
33898                 label,
33899                 items
33900             ]
33901         };
33902         
33903         if(this.weight.length){
33904             cfg.cls += ' roo-radio-' + this.weight;
33905         }
33906         
33907         if(this.inline) {
33908             cfg.cls += ' roo-radio-set-inline';
33909         }
33910         
33911         var settings=this;
33912         ['xs','sm','md','lg'].map(function(size){
33913             if (settings[size]) {
33914                 cfg.cls += ' col-' + size + '-' + settings[size];
33915             }
33916         });
33917         
33918         return cfg;
33919         
33920     },
33921
33922     initEvents : function()
33923     {
33924         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33925         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33926         
33927         if(!this.fieldLabel.length){
33928             this.labelEl.hide();
33929         }
33930         
33931         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33932         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33933         
33934         this.indicator = this.indicatorEl();
33935         
33936         if(this.indicator){
33937             this.indicator.addClass('invisible');
33938         }
33939         
33940         this.originalValue = this.getValue();
33941         
33942     },
33943     
33944     inputEl: function ()
33945     {
33946         return this.el.select('.roo-radio-set-input', true).first();
33947     },
33948     
33949     getChildContainer : function()
33950     {
33951         return this.itemsEl;
33952     },
33953     
33954     register : function(item)
33955     {
33956         this.radioes.push(item);
33957         
33958     },
33959     
33960     validate : function()
33961     {   
33962         if(this.getVisibilityEl().hasClass('hidden')){
33963             return true;
33964         }
33965         
33966         var valid = false;
33967         
33968         Roo.each(this.radioes, function(i){
33969             if(!i.checked){
33970                 return;
33971             }
33972             
33973             valid = true;
33974             return false;
33975         });
33976         
33977         if(this.allowBlank) {
33978             return true;
33979         }
33980         
33981         if(this.disabled || valid){
33982             this.markValid();
33983             return true;
33984         }
33985         
33986         this.markInvalid();
33987         return false;
33988         
33989     },
33990     
33991     markValid : function()
33992     {
33993         if(this.labelEl.isVisible(true)){
33994             this.indicatorEl().removeClass('visible');
33995             this.indicatorEl().addClass('invisible');
33996         }
33997         
33998         this.el.removeClass([this.invalidClass, this.validClass]);
33999         this.el.addClass(this.validClass);
34000         
34001         this.fireEvent('valid', this);
34002     },
34003     
34004     markInvalid : function(msg)
34005     {
34006         if(this.allowBlank || this.disabled){
34007             return;
34008         }
34009         
34010         if(this.labelEl.isVisible(true)){
34011             this.indicatorEl().removeClass('invisible');
34012             this.indicatorEl().addClass('visible');
34013         }
34014         
34015         this.el.removeClass([this.invalidClass, this.validClass]);
34016         this.el.addClass(this.invalidClass);
34017         
34018         this.fireEvent('invalid', this, msg);
34019         
34020     },
34021     
34022     setValue : function(v, suppressEvent)
34023     {   
34024         if(this.value === v){
34025             return;
34026         }
34027         
34028         this.value = v;
34029         
34030         if(this.rendered){
34031             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34032         }
34033         
34034         Roo.each(this.radioes, function(i){
34035             i.checked = false;
34036             i.el.removeClass('checked');
34037         });
34038         
34039         Roo.each(this.radioes, function(i){
34040             
34041             if(i.value === v || i.value.toString() === v.toString()){
34042                 i.checked = true;
34043                 i.el.addClass('checked');
34044                 
34045                 if(suppressEvent !== true){
34046                     this.fireEvent('check', this, i);
34047                 }
34048                 
34049                 return false;
34050             }
34051             
34052         }, this);
34053         
34054         this.validate();
34055     },
34056     
34057     clearInvalid : function(){
34058         
34059         if(!this.el || this.preventMark){
34060             return;
34061         }
34062         
34063         this.el.removeClass([this.invalidClass]);
34064         
34065         this.fireEvent('valid', this);
34066     }
34067     
34068 });
34069
34070 Roo.apply(Roo.bootstrap.RadioSet, {
34071     
34072     groups: {},
34073     
34074     register : function(set)
34075     {
34076         this.groups[set.name] = set;
34077     },
34078     
34079     get: function(name) 
34080     {
34081         if (typeof(this.groups[name]) == 'undefined') {
34082             return false;
34083         }
34084         
34085         return this.groups[name] ;
34086     }
34087     
34088 });
34089 /*
34090  * Based on:
34091  * Ext JS Library 1.1.1
34092  * Copyright(c) 2006-2007, Ext JS, LLC.
34093  *
34094  * Originally Released Under LGPL - original licence link has changed is not relivant.
34095  *
34096  * Fork - LGPL
34097  * <script type="text/javascript">
34098  */
34099
34100
34101 /**
34102  * @class Roo.bootstrap.SplitBar
34103  * @extends Roo.util.Observable
34104  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34105  * <br><br>
34106  * Usage:
34107  * <pre><code>
34108 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34109                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34110 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34111 split.minSize = 100;
34112 split.maxSize = 600;
34113 split.animate = true;
34114 split.on('moved', splitterMoved);
34115 </code></pre>
34116  * @constructor
34117  * Create a new SplitBar
34118  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34119  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34120  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34121  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34122                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34123                         position of the SplitBar).
34124  */
34125 Roo.bootstrap.SplitBar = function(cfg){
34126     
34127     /** @private */
34128     
34129     //{
34130     //  dragElement : elm
34131     //  resizingElement: el,
34132         // optional..
34133     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34134     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34135         // existingProxy ???
34136     //}
34137     
34138     this.el = Roo.get(cfg.dragElement, true);
34139     this.el.dom.unselectable = "on";
34140     /** @private */
34141     this.resizingEl = Roo.get(cfg.resizingElement, true);
34142
34143     /**
34144      * @private
34145      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34146      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34147      * @type Number
34148      */
34149     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34150     
34151     /**
34152      * The minimum size of the resizing element. (Defaults to 0)
34153      * @type Number
34154      */
34155     this.minSize = 0;
34156     
34157     /**
34158      * The maximum size of the resizing element. (Defaults to 2000)
34159      * @type Number
34160      */
34161     this.maxSize = 2000;
34162     
34163     /**
34164      * Whether to animate the transition to the new size
34165      * @type Boolean
34166      */
34167     this.animate = false;
34168     
34169     /**
34170      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34171      * @type Boolean
34172      */
34173     this.useShim = false;
34174     
34175     /** @private */
34176     this.shim = null;
34177     
34178     if(!cfg.existingProxy){
34179         /** @private */
34180         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34181     }else{
34182         this.proxy = Roo.get(cfg.existingProxy).dom;
34183     }
34184     /** @private */
34185     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34186     
34187     /** @private */
34188     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34189     
34190     /** @private */
34191     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34192     
34193     /** @private */
34194     this.dragSpecs = {};
34195     
34196     /**
34197      * @private The adapter to use to positon and resize elements
34198      */
34199     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34200     this.adapter.init(this);
34201     
34202     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34203         /** @private */
34204         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34205         this.el.addClass("roo-splitbar-h");
34206     }else{
34207         /** @private */
34208         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34209         this.el.addClass("roo-splitbar-v");
34210     }
34211     
34212     this.addEvents({
34213         /**
34214          * @event resize
34215          * Fires when the splitter is moved (alias for {@link #event-moved})
34216          * @param {Roo.bootstrap.SplitBar} this
34217          * @param {Number} newSize the new width or height
34218          */
34219         "resize" : true,
34220         /**
34221          * @event moved
34222          * Fires when the splitter is moved
34223          * @param {Roo.bootstrap.SplitBar} this
34224          * @param {Number} newSize the new width or height
34225          */
34226         "moved" : true,
34227         /**
34228          * @event beforeresize
34229          * Fires before the splitter is dragged
34230          * @param {Roo.bootstrap.SplitBar} this
34231          */
34232         "beforeresize" : true,
34233
34234         "beforeapply" : true
34235     });
34236
34237     Roo.util.Observable.call(this);
34238 };
34239
34240 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34241     onStartProxyDrag : function(x, y){
34242         this.fireEvent("beforeresize", this);
34243         if(!this.overlay){
34244             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34245             o.unselectable();
34246             o.enableDisplayMode("block");
34247             // all splitbars share the same overlay
34248             Roo.bootstrap.SplitBar.prototype.overlay = o;
34249         }
34250         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34251         this.overlay.show();
34252         Roo.get(this.proxy).setDisplayed("block");
34253         var size = this.adapter.getElementSize(this);
34254         this.activeMinSize = this.getMinimumSize();;
34255         this.activeMaxSize = this.getMaximumSize();;
34256         var c1 = size - this.activeMinSize;
34257         var c2 = Math.max(this.activeMaxSize - size, 0);
34258         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34259             this.dd.resetConstraints();
34260             this.dd.setXConstraint(
34261                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34262                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34263             );
34264             this.dd.setYConstraint(0, 0);
34265         }else{
34266             this.dd.resetConstraints();
34267             this.dd.setXConstraint(0, 0);
34268             this.dd.setYConstraint(
34269                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34270                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34271             );
34272          }
34273         this.dragSpecs.startSize = size;
34274         this.dragSpecs.startPoint = [x, y];
34275         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34276     },
34277     
34278     /** 
34279      * @private Called after the drag operation by the DDProxy
34280      */
34281     onEndProxyDrag : function(e){
34282         Roo.get(this.proxy).setDisplayed(false);
34283         var endPoint = Roo.lib.Event.getXY(e);
34284         if(this.overlay){
34285             this.overlay.hide();
34286         }
34287         var newSize;
34288         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34289             newSize = this.dragSpecs.startSize + 
34290                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34291                     endPoint[0] - this.dragSpecs.startPoint[0] :
34292                     this.dragSpecs.startPoint[0] - endPoint[0]
34293                 );
34294         }else{
34295             newSize = this.dragSpecs.startSize + 
34296                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34297                     endPoint[1] - this.dragSpecs.startPoint[1] :
34298                     this.dragSpecs.startPoint[1] - endPoint[1]
34299                 );
34300         }
34301         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34302         if(newSize != this.dragSpecs.startSize){
34303             if(this.fireEvent('beforeapply', this, newSize) !== false){
34304                 this.adapter.setElementSize(this, newSize);
34305                 this.fireEvent("moved", this, newSize);
34306                 this.fireEvent("resize", this, newSize);
34307             }
34308         }
34309     },
34310     
34311     /**
34312      * Get the adapter this SplitBar uses
34313      * @return The adapter object
34314      */
34315     getAdapter : function(){
34316         return this.adapter;
34317     },
34318     
34319     /**
34320      * Set the adapter this SplitBar uses
34321      * @param {Object} adapter A SplitBar adapter object
34322      */
34323     setAdapter : function(adapter){
34324         this.adapter = adapter;
34325         this.adapter.init(this);
34326     },
34327     
34328     /**
34329      * Gets the minimum size for the resizing element
34330      * @return {Number} The minimum size
34331      */
34332     getMinimumSize : function(){
34333         return this.minSize;
34334     },
34335     
34336     /**
34337      * Sets the minimum size for the resizing element
34338      * @param {Number} minSize The minimum size
34339      */
34340     setMinimumSize : function(minSize){
34341         this.minSize = minSize;
34342     },
34343     
34344     /**
34345      * Gets the maximum size for the resizing element
34346      * @return {Number} The maximum size
34347      */
34348     getMaximumSize : function(){
34349         return this.maxSize;
34350     },
34351     
34352     /**
34353      * Sets the maximum size for the resizing element
34354      * @param {Number} maxSize The maximum size
34355      */
34356     setMaximumSize : function(maxSize){
34357         this.maxSize = maxSize;
34358     },
34359     
34360     /**
34361      * Sets the initialize size for the resizing element
34362      * @param {Number} size The initial size
34363      */
34364     setCurrentSize : function(size){
34365         var oldAnimate = this.animate;
34366         this.animate = false;
34367         this.adapter.setElementSize(this, size);
34368         this.animate = oldAnimate;
34369     },
34370     
34371     /**
34372      * Destroy this splitbar. 
34373      * @param {Boolean} removeEl True to remove the element
34374      */
34375     destroy : function(removeEl){
34376         if(this.shim){
34377             this.shim.remove();
34378         }
34379         this.dd.unreg();
34380         this.proxy.parentNode.removeChild(this.proxy);
34381         if(removeEl){
34382             this.el.remove();
34383         }
34384     }
34385 });
34386
34387 /**
34388  * @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.
34389  */
34390 Roo.bootstrap.SplitBar.createProxy = function(dir){
34391     var proxy = new Roo.Element(document.createElement("div"));
34392     proxy.unselectable();
34393     var cls = 'roo-splitbar-proxy';
34394     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34395     document.body.appendChild(proxy.dom);
34396     return proxy.dom;
34397 };
34398
34399 /** 
34400  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34401  * Default Adapter. It assumes the splitter and resizing element are not positioned
34402  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34403  */
34404 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34405 };
34406
34407 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34408     // do nothing for now
34409     init : function(s){
34410     
34411     },
34412     /**
34413      * Called before drag operations to get the current size of the resizing element. 
34414      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34415      */
34416      getElementSize : function(s){
34417         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34418             return s.resizingEl.getWidth();
34419         }else{
34420             return s.resizingEl.getHeight();
34421         }
34422     },
34423     
34424     /**
34425      * Called after drag operations to set the size of the resizing element.
34426      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34427      * @param {Number} newSize The new size to set
34428      * @param {Function} onComplete A function to be invoked when resizing is complete
34429      */
34430     setElementSize : function(s, newSize, onComplete){
34431         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34432             if(!s.animate){
34433                 s.resizingEl.setWidth(newSize);
34434                 if(onComplete){
34435                     onComplete(s, newSize);
34436                 }
34437             }else{
34438                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34439             }
34440         }else{
34441             
34442             if(!s.animate){
34443                 s.resizingEl.setHeight(newSize);
34444                 if(onComplete){
34445                     onComplete(s, newSize);
34446                 }
34447             }else{
34448                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34449             }
34450         }
34451     }
34452 };
34453
34454 /** 
34455  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34456  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34457  * Adapter that  moves the splitter element to align with the resized sizing element. 
34458  * Used with an absolute positioned SplitBar.
34459  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34460  * document.body, make sure you assign an id to the body element.
34461  */
34462 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34463     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34464     this.container = Roo.get(container);
34465 };
34466
34467 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34468     init : function(s){
34469         this.basic.init(s);
34470     },
34471     
34472     getElementSize : function(s){
34473         return this.basic.getElementSize(s);
34474     },
34475     
34476     setElementSize : function(s, newSize, onComplete){
34477         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34478     },
34479     
34480     moveSplitter : function(s){
34481         var yes = Roo.bootstrap.SplitBar;
34482         switch(s.placement){
34483             case yes.LEFT:
34484                 s.el.setX(s.resizingEl.getRight());
34485                 break;
34486             case yes.RIGHT:
34487                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34488                 break;
34489             case yes.TOP:
34490                 s.el.setY(s.resizingEl.getBottom());
34491                 break;
34492             case yes.BOTTOM:
34493                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34494                 break;
34495         }
34496     }
34497 };
34498
34499 /**
34500  * Orientation constant - Create a vertical SplitBar
34501  * @static
34502  * @type Number
34503  */
34504 Roo.bootstrap.SplitBar.VERTICAL = 1;
34505
34506 /**
34507  * Orientation constant - Create a horizontal SplitBar
34508  * @static
34509  * @type Number
34510  */
34511 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34512
34513 /**
34514  * Placement constant - The resizing element is to the left of the splitter element
34515  * @static
34516  * @type Number
34517  */
34518 Roo.bootstrap.SplitBar.LEFT = 1;
34519
34520 /**
34521  * Placement constant - The resizing element is to the right of the splitter element
34522  * @static
34523  * @type Number
34524  */
34525 Roo.bootstrap.SplitBar.RIGHT = 2;
34526
34527 /**
34528  * Placement constant - The resizing element is positioned above the splitter element
34529  * @static
34530  * @type Number
34531  */
34532 Roo.bootstrap.SplitBar.TOP = 3;
34533
34534 /**
34535  * Placement constant - The resizing element is positioned under splitter element
34536  * @static
34537  * @type Number
34538  */
34539 Roo.bootstrap.SplitBar.BOTTOM = 4;
34540 Roo.namespace("Roo.bootstrap.layout");/*
34541  * Based on:
34542  * Ext JS Library 1.1.1
34543  * Copyright(c) 2006-2007, Ext JS, LLC.
34544  *
34545  * Originally Released Under LGPL - original licence link has changed is not relivant.
34546  *
34547  * Fork - LGPL
34548  * <script type="text/javascript">
34549  */
34550
34551 /**
34552  * @class Roo.bootstrap.layout.Manager
34553  * @extends Roo.bootstrap.Component
34554  * Base class for layout managers.
34555  */
34556 Roo.bootstrap.layout.Manager = function(config)
34557 {
34558     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34559
34560
34561
34562
34563
34564     /** false to disable window resize monitoring @type Boolean */
34565     this.monitorWindowResize = true;
34566     this.regions = {};
34567     this.addEvents({
34568         /**
34569          * @event layout
34570          * Fires when a layout is performed.
34571          * @param {Roo.LayoutManager} this
34572          */
34573         "layout" : true,
34574         /**
34575          * @event regionresized
34576          * Fires when the user resizes a region.
34577          * @param {Roo.LayoutRegion} region The resized region
34578          * @param {Number} newSize The new size (width for east/west, height for north/south)
34579          */
34580         "regionresized" : true,
34581         /**
34582          * @event regioncollapsed
34583          * Fires when a region is collapsed.
34584          * @param {Roo.LayoutRegion} region The collapsed region
34585          */
34586         "regioncollapsed" : true,
34587         /**
34588          * @event regionexpanded
34589          * Fires when a region is expanded.
34590          * @param {Roo.LayoutRegion} region The expanded region
34591          */
34592         "regionexpanded" : true
34593     });
34594     this.updating = false;
34595
34596     if (config.el) {
34597         this.el = Roo.get(config.el);
34598         this.initEvents();
34599     }
34600
34601 };
34602
34603 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34604
34605
34606     regions : null,
34607
34608     monitorWindowResize : true,
34609
34610
34611     updating : false,
34612
34613
34614     onRender : function(ct, position)
34615     {
34616         if(!this.el){
34617             this.el = Roo.get(ct);
34618             this.initEvents();
34619         }
34620         //this.fireEvent('render',this);
34621     },
34622
34623
34624     initEvents: function()
34625     {
34626
34627
34628         // ie scrollbar fix
34629         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34630             document.body.scroll = "no";
34631         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34632             this.el.position('relative');
34633         }
34634         this.id = this.el.id;
34635         this.el.addClass("roo-layout-container");
34636         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34637         if(this.el.dom != document.body ) {
34638             this.el.on('resize', this.layout,this);
34639             this.el.on('show', this.layout,this);
34640         }
34641
34642     },
34643
34644     /**
34645      * Returns true if this layout is currently being updated
34646      * @return {Boolean}
34647      */
34648     isUpdating : function(){
34649         return this.updating;
34650     },
34651
34652     /**
34653      * Suspend the LayoutManager from doing auto-layouts while
34654      * making multiple add or remove calls
34655      */
34656     beginUpdate : function(){
34657         this.updating = true;
34658     },
34659
34660     /**
34661      * Restore auto-layouts and optionally disable the manager from performing a layout
34662      * @param {Boolean} noLayout true to disable a layout update
34663      */
34664     endUpdate : function(noLayout){
34665         this.updating = false;
34666         if(!noLayout){
34667             this.layout();
34668         }
34669     },
34670
34671     layout: function(){
34672         // abstract...
34673     },
34674
34675     onRegionResized : function(region, newSize){
34676         this.fireEvent("regionresized", region, newSize);
34677         this.layout();
34678     },
34679
34680     onRegionCollapsed : function(region){
34681         this.fireEvent("regioncollapsed", region);
34682     },
34683
34684     onRegionExpanded : function(region){
34685         this.fireEvent("regionexpanded", region);
34686     },
34687
34688     /**
34689      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34690      * performs box-model adjustments.
34691      * @return {Object} The size as an object {width: (the width), height: (the height)}
34692      */
34693     getViewSize : function()
34694     {
34695         var size;
34696         if(this.el.dom != document.body){
34697             size = this.el.getSize();
34698         }else{
34699             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34700         }
34701         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34702         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34703         return size;
34704     },
34705
34706     /**
34707      * Returns the Element this layout is bound to.
34708      * @return {Roo.Element}
34709      */
34710     getEl : function(){
34711         return this.el;
34712     },
34713
34714     /**
34715      * Returns the specified region.
34716      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34717      * @return {Roo.LayoutRegion}
34718      */
34719     getRegion : function(target){
34720         return this.regions[target.toLowerCase()];
34721     },
34722
34723     onWindowResize : function(){
34724         if(this.monitorWindowResize){
34725             this.layout();
34726         }
34727     }
34728 });
34729 /*
34730  * Based on:
34731  * Ext JS Library 1.1.1
34732  * Copyright(c) 2006-2007, Ext JS, LLC.
34733  *
34734  * Originally Released Under LGPL - original licence link has changed is not relivant.
34735  *
34736  * Fork - LGPL
34737  * <script type="text/javascript">
34738  */
34739 /**
34740  * @class Roo.bootstrap.layout.Border
34741  * @extends Roo.bootstrap.layout.Manager
34742  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34743  * please see: examples/bootstrap/nested.html<br><br>
34744  
34745 <b>The container the layout is rendered into can be either the body element or any other element.
34746 If it is not the body element, the container needs to either be an absolute positioned element,
34747 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34748 the container size if it is not the body element.</b>
34749
34750 * @constructor
34751 * Create a new Border
34752 * @param {Object} config Configuration options
34753  */
34754 Roo.bootstrap.layout.Border = function(config){
34755     config = config || {};
34756     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34757     
34758     
34759     
34760     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34761         if(config[region]){
34762             config[region].region = region;
34763             this.addRegion(config[region]);
34764         }
34765     },this);
34766     
34767 };
34768
34769 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34770
34771 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34772     /**
34773      * Creates and adds a new region if it doesn't already exist.
34774      * @param {String} target The target region key (north, south, east, west or center).
34775      * @param {Object} config The regions config object
34776      * @return {BorderLayoutRegion} The new region
34777      */
34778     addRegion : function(config)
34779     {
34780         if(!this.regions[config.region]){
34781             var r = this.factory(config);
34782             this.bindRegion(r);
34783         }
34784         return this.regions[config.region];
34785     },
34786
34787     // private (kinda)
34788     bindRegion : function(r){
34789         this.regions[r.config.region] = r;
34790         
34791         r.on("visibilitychange",    this.layout, this);
34792         r.on("paneladded",          this.layout, this);
34793         r.on("panelremoved",        this.layout, this);
34794         r.on("invalidated",         this.layout, this);
34795         r.on("resized",             this.onRegionResized, this);
34796         r.on("collapsed",           this.onRegionCollapsed, this);
34797         r.on("expanded",            this.onRegionExpanded, this);
34798     },
34799
34800     /**
34801      * Performs a layout update.
34802      */
34803     layout : function()
34804     {
34805         if(this.updating) {
34806             return;
34807         }
34808         
34809         // render all the rebions if they have not been done alreayd?
34810         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34811             if(this.regions[region] && !this.regions[region].bodyEl){
34812                 this.regions[region].onRender(this.el)
34813             }
34814         },this);
34815         
34816         var size = this.getViewSize();
34817         var w = size.width;
34818         var h = size.height;
34819         var centerW = w;
34820         var centerH = h;
34821         var centerY = 0;
34822         var centerX = 0;
34823         //var x = 0, y = 0;
34824
34825         var rs = this.regions;
34826         var north = rs["north"];
34827         var south = rs["south"]; 
34828         var west = rs["west"];
34829         var east = rs["east"];
34830         var center = rs["center"];
34831         //if(this.hideOnLayout){ // not supported anymore
34832             //c.el.setStyle("display", "none");
34833         //}
34834         if(north && north.isVisible()){
34835             var b = north.getBox();
34836             var m = north.getMargins();
34837             b.width = w - (m.left+m.right);
34838             b.x = m.left;
34839             b.y = m.top;
34840             centerY = b.height + b.y + m.bottom;
34841             centerH -= centerY;
34842             north.updateBox(this.safeBox(b));
34843         }
34844         if(south && south.isVisible()){
34845             var b = south.getBox();
34846             var m = south.getMargins();
34847             b.width = w - (m.left+m.right);
34848             b.x = m.left;
34849             var totalHeight = (b.height + m.top + m.bottom);
34850             b.y = h - totalHeight + m.top;
34851             centerH -= totalHeight;
34852             south.updateBox(this.safeBox(b));
34853         }
34854         if(west && west.isVisible()){
34855             var b = west.getBox();
34856             var m = west.getMargins();
34857             b.height = centerH - (m.top+m.bottom);
34858             b.x = m.left;
34859             b.y = centerY + m.top;
34860             var totalWidth = (b.width + m.left + m.right);
34861             centerX += totalWidth;
34862             centerW -= totalWidth;
34863             west.updateBox(this.safeBox(b));
34864         }
34865         if(east && east.isVisible()){
34866             var b = east.getBox();
34867             var m = east.getMargins();
34868             b.height = centerH - (m.top+m.bottom);
34869             var totalWidth = (b.width + m.left + m.right);
34870             b.x = w - totalWidth + m.left;
34871             b.y = centerY + m.top;
34872             centerW -= totalWidth;
34873             east.updateBox(this.safeBox(b));
34874         }
34875         if(center){
34876             var m = center.getMargins();
34877             var centerBox = {
34878                 x: centerX + m.left,
34879                 y: centerY + m.top,
34880                 width: centerW - (m.left+m.right),
34881                 height: centerH - (m.top+m.bottom)
34882             };
34883             //if(this.hideOnLayout){
34884                 //center.el.setStyle("display", "block");
34885             //}
34886             center.updateBox(this.safeBox(centerBox));
34887         }
34888         this.el.repaint();
34889         this.fireEvent("layout", this);
34890     },
34891
34892     // private
34893     safeBox : function(box){
34894         box.width = Math.max(0, box.width);
34895         box.height = Math.max(0, box.height);
34896         return box;
34897     },
34898
34899     /**
34900      * Adds a ContentPanel (or subclass) to this layout.
34901      * @param {String} target The target region key (north, south, east, west or center).
34902      * @param {Roo.ContentPanel} panel The panel to add
34903      * @return {Roo.ContentPanel} The added panel
34904      */
34905     add : function(target, panel){
34906          
34907         target = target.toLowerCase();
34908         return this.regions[target].add(panel);
34909     },
34910
34911     /**
34912      * Remove a ContentPanel (or subclass) to this layout.
34913      * @param {String} target The target region key (north, south, east, west or center).
34914      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34915      * @return {Roo.ContentPanel} The removed panel
34916      */
34917     remove : function(target, panel){
34918         target = target.toLowerCase();
34919         return this.regions[target].remove(panel);
34920     },
34921
34922     /**
34923      * Searches all regions for a panel with the specified id
34924      * @param {String} panelId
34925      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34926      */
34927     findPanel : function(panelId){
34928         var rs = this.regions;
34929         for(var target in rs){
34930             if(typeof rs[target] != "function"){
34931                 var p = rs[target].getPanel(panelId);
34932                 if(p){
34933                     return p;
34934                 }
34935             }
34936         }
34937         return null;
34938     },
34939
34940     /**
34941      * Searches all regions for a panel with the specified id and activates (shows) it.
34942      * @param {String/ContentPanel} panelId The panels id or the panel itself
34943      * @return {Roo.ContentPanel} The shown panel or null
34944      */
34945     showPanel : function(panelId) {
34946       var rs = this.regions;
34947       for(var target in rs){
34948          var r = rs[target];
34949          if(typeof r != "function"){
34950             if(r.hasPanel(panelId)){
34951                return r.showPanel(panelId);
34952             }
34953          }
34954       }
34955       return null;
34956    },
34957
34958    /**
34959      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34960      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34961      */
34962    /*
34963     restoreState : function(provider){
34964         if(!provider){
34965             provider = Roo.state.Manager;
34966         }
34967         var sm = new Roo.LayoutStateManager();
34968         sm.init(this, provider);
34969     },
34970 */
34971  
34972  
34973     /**
34974      * Adds a xtype elements to the layout.
34975      * <pre><code>
34976
34977 layout.addxtype({
34978        xtype : 'ContentPanel',
34979        region: 'west',
34980        items: [ .... ]
34981    }
34982 );
34983
34984 layout.addxtype({
34985         xtype : 'NestedLayoutPanel',
34986         region: 'west',
34987         layout: {
34988            center: { },
34989            west: { }   
34990         },
34991         items : [ ... list of content panels or nested layout panels.. ]
34992    }
34993 );
34994 </code></pre>
34995      * @param {Object} cfg Xtype definition of item to add.
34996      */
34997     addxtype : function(cfg)
34998     {
34999         // basically accepts a pannel...
35000         // can accept a layout region..!?!?
35001         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35002         
35003         
35004         // theory?  children can only be panels??
35005         
35006         //if (!cfg.xtype.match(/Panel$/)) {
35007         //    return false;
35008         //}
35009         var ret = false;
35010         
35011         if (typeof(cfg.region) == 'undefined') {
35012             Roo.log("Failed to add Panel, region was not set");
35013             Roo.log(cfg);
35014             return false;
35015         }
35016         var region = cfg.region;
35017         delete cfg.region;
35018         
35019           
35020         var xitems = [];
35021         if (cfg.items) {
35022             xitems = cfg.items;
35023             delete cfg.items;
35024         }
35025         var nb = false;
35026         
35027         switch(cfg.xtype) 
35028         {
35029             case 'Content':  // ContentPanel (el, cfg)
35030             case 'Scroll':  // ContentPanel (el, cfg)
35031             case 'View': 
35032                 cfg.autoCreate = true;
35033                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35034                 //} else {
35035                 //    var el = this.el.createChild();
35036                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35037                 //}
35038                 
35039                 this.add(region, ret);
35040                 break;
35041             
35042             /*
35043             case 'TreePanel': // our new panel!
35044                 cfg.el = this.el.createChild();
35045                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35046                 this.add(region, ret);
35047                 break;
35048             */
35049             
35050             case 'Nest': 
35051                 // create a new Layout (which is  a Border Layout...
35052                 
35053                 var clayout = cfg.layout;
35054                 clayout.el  = this.el.createChild();
35055                 clayout.items   = clayout.items  || [];
35056                 
35057                 delete cfg.layout;
35058                 
35059                 // replace this exitems with the clayout ones..
35060                 xitems = clayout.items;
35061                  
35062                 // force background off if it's in center...
35063                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35064                     cfg.background = false;
35065                 }
35066                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35067                 
35068                 
35069                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35070                 //console.log('adding nested layout panel '  + cfg.toSource());
35071                 this.add(region, ret);
35072                 nb = {}; /// find first...
35073                 break;
35074             
35075             case 'Grid':
35076                 
35077                 // needs grid and region
35078                 
35079                 //var el = this.getRegion(region).el.createChild();
35080                 /*
35081                  *var el = this.el.createChild();
35082                 // create the grid first...
35083                 cfg.grid.container = el;
35084                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35085                 */
35086                 
35087                 if (region == 'center' && this.active ) {
35088                     cfg.background = false;
35089                 }
35090                 
35091                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35092                 
35093                 this.add(region, ret);
35094                 /*
35095                 if (cfg.background) {
35096                     // render grid on panel activation (if panel background)
35097                     ret.on('activate', function(gp) {
35098                         if (!gp.grid.rendered) {
35099                     //        gp.grid.render(el);
35100                         }
35101                     });
35102                 } else {
35103                   //  cfg.grid.render(el);
35104                 }
35105                 */
35106                 break;
35107            
35108            
35109             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35110                 // it was the old xcomponent building that caused this before.
35111                 // espeically if border is the top element in the tree.
35112                 ret = this;
35113                 break; 
35114                 
35115                     
35116                 
35117                 
35118                 
35119             default:
35120                 /*
35121                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35122                     
35123                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35124                     this.add(region, ret);
35125                 } else {
35126                 */
35127                     Roo.log(cfg);
35128                     throw "Can not add '" + cfg.xtype + "' to Border";
35129                     return null;
35130              
35131                                 
35132              
35133         }
35134         this.beginUpdate();
35135         // add children..
35136         var region = '';
35137         var abn = {};
35138         Roo.each(xitems, function(i)  {
35139             region = nb && i.region ? i.region : false;
35140             
35141             var add = ret.addxtype(i);
35142            
35143             if (region) {
35144                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35145                 if (!i.background) {
35146                     abn[region] = nb[region] ;
35147                 }
35148             }
35149             
35150         });
35151         this.endUpdate();
35152
35153         // make the last non-background panel active..
35154         //if (nb) { Roo.log(abn); }
35155         if (nb) {
35156             
35157             for(var r in abn) {
35158                 region = this.getRegion(r);
35159                 if (region) {
35160                     // tried using nb[r], but it does not work..
35161                      
35162                     region.showPanel(abn[r]);
35163                    
35164                 }
35165             }
35166         }
35167         return ret;
35168         
35169     },
35170     
35171     
35172 // private
35173     factory : function(cfg)
35174     {
35175         
35176         var validRegions = Roo.bootstrap.layout.Border.regions;
35177
35178         var target = cfg.region;
35179         cfg.mgr = this;
35180         
35181         var r = Roo.bootstrap.layout;
35182         Roo.log(target);
35183         switch(target){
35184             case "north":
35185                 return new r.North(cfg);
35186             case "south":
35187                 return new r.South(cfg);
35188             case "east":
35189                 return new r.East(cfg);
35190             case "west":
35191                 return new r.West(cfg);
35192             case "center":
35193                 return new r.Center(cfg);
35194         }
35195         throw 'Layout region "'+target+'" not supported.';
35196     }
35197     
35198     
35199 });
35200  /*
35201  * Based on:
35202  * Ext JS Library 1.1.1
35203  * Copyright(c) 2006-2007, Ext JS, LLC.
35204  *
35205  * Originally Released Under LGPL - original licence link has changed is not relivant.
35206  *
35207  * Fork - LGPL
35208  * <script type="text/javascript">
35209  */
35210  
35211 /**
35212  * @class Roo.bootstrap.layout.Basic
35213  * @extends Roo.util.Observable
35214  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35215  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35216  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35217  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35218  * @cfg {string}   region  the region that it inhabits..
35219  * @cfg {bool}   skipConfig skip config?
35220  * 
35221
35222  */
35223 Roo.bootstrap.layout.Basic = function(config){
35224     
35225     this.mgr = config.mgr;
35226     
35227     this.position = config.region;
35228     
35229     var skipConfig = config.skipConfig;
35230     
35231     this.events = {
35232         /**
35233          * @scope Roo.BasicLayoutRegion
35234          */
35235         
35236         /**
35237          * @event beforeremove
35238          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35239          * @param {Roo.LayoutRegion} this
35240          * @param {Roo.ContentPanel} panel The panel
35241          * @param {Object} e The cancel event object
35242          */
35243         "beforeremove" : true,
35244         /**
35245          * @event invalidated
35246          * Fires when the layout for this region is changed.
35247          * @param {Roo.LayoutRegion} this
35248          */
35249         "invalidated" : true,
35250         /**
35251          * @event visibilitychange
35252          * Fires when this region is shown or hidden 
35253          * @param {Roo.LayoutRegion} this
35254          * @param {Boolean} visibility true or false
35255          */
35256         "visibilitychange" : true,
35257         /**
35258          * @event paneladded
35259          * Fires when a panel is added. 
35260          * @param {Roo.LayoutRegion} this
35261          * @param {Roo.ContentPanel} panel The panel
35262          */
35263         "paneladded" : true,
35264         /**
35265          * @event panelremoved
35266          * Fires when a panel is removed. 
35267          * @param {Roo.LayoutRegion} this
35268          * @param {Roo.ContentPanel} panel The panel
35269          */
35270         "panelremoved" : true,
35271         /**
35272          * @event beforecollapse
35273          * Fires when this region before collapse.
35274          * @param {Roo.LayoutRegion} this
35275          */
35276         "beforecollapse" : true,
35277         /**
35278          * @event collapsed
35279          * Fires when this region is collapsed.
35280          * @param {Roo.LayoutRegion} this
35281          */
35282         "collapsed" : true,
35283         /**
35284          * @event expanded
35285          * Fires when this region is expanded.
35286          * @param {Roo.LayoutRegion} this
35287          */
35288         "expanded" : true,
35289         /**
35290          * @event slideshow
35291          * Fires when this region is slid into view.
35292          * @param {Roo.LayoutRegion} this
35293          */
35294         "slideshow" : true,
35295         /**
35296          * @event slidehide
35297          * Fires when this region slides out of view. 
35298          * @param {Roo.LayoutRegion} this
35299          */
35300         "slidehide" : true,
35301         /**
35302          * @event panelactivated
35303          * Fires when a panel is activated. 
35304          * @param {Roo.LayoutRegion} this
35305          * @param {Roo.ContentPanel} panel The activated panel
35306          */
35307         "panelactivated" : true,
35308         /**
35309          * @event resized
35310          * Fires when the user resizes this region. 
35311          * @param {Roo.LayoutRegion} this
35312          * @param {Number} newSize The new size (width for east/west, height for north/south)
35313          */
35314         "resized" : true
35315     };
35316     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35317     this.panels = new Roo.util.MixedCollection();
35318     this.panels.getKey = this.getPanelId.createDelegate(this);
35319     this.box = null;
35320     this.activePanel = null;
35321     // ensure listeners are added...
35322     
35323     if (config.listeners || config.events) {
35324         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35325             listeners : config.listeners || {},
35326             events : config.events || {}
35327         });
35328     }
35329     
35330     if(skipConfig !== true){
35331         this.applyConfig(config);
35332     }
35333 };
35334
35335 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35336 {
35337     getPanelId : function(p){
35338         return p.getId();
35339     },
35340     
35341     applyConfig : function(config){
35342         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35343         this.config = config;
35344         
35345     },
35346     
35347     /**
35348      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35349      * the width, for horizontal (north, south) the height.
35350      * @param {Number} newSize The new width or height
35351      */
35352     resizeTo : function(newSize){
35353         var el = this.el ? this.el :
35354                  (this.activePanel ? this.activePanel.getEl() : null);
35355         if(el){
35356             switch(this.position){
35357                 case "east":
35358                 case "west":
35359                     el.setWidth(newSize);
35360                     this.fireEvent("resized", this, newSize);
35361                 break;
35362                 case "north":
35363                 case "south":
35364                     el.setHeight(newSize);
35365                     this.fireEvent("resized", this, newSize);
35366                 break;                
35367             }
35368         }
35369     },
35370     
35371     getBox : function(){
35372         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35373     },
35374     
35375     getMargins : function(){
35376         return this.margins;
35377     },
35378     
35379     updateBox : function(box){
35380         this.box = box;
35381         var el = this.activePanel.getEl();
35382         el.dom.style.left = box.x + "px";
35383         el.dom.style.top = box.y + "px";
35384         this.activePanel.setSize(box.width, box.height);
35385     },
35386     
35387     /**
35388      * Returns the container element for this region.
35389      * @return {Roo.Element}
35390      */
35391     getEl : function(){
35392         return this.activePanel;
35393     },
35394     
35395     /**
35396      * Returns true if this region is currently visible.
35397      * @return {Boolean}
35398      */
35399     isVisible : function(){
35400         return this.activePanel ? true : false;
35401     },
35402     
35403     setActivePanel : function(panel){
35404         panel = this.getPanel(panel);
35405         if(this.activePanel && this.activePanel != panel){
35406             this.activePanel.setActiveState(false);
35407             this.activePanel.getEl().setLeftTop(-10000,-10000);
35408         }
35409         this.activePanel = panel;
35410         panel.setActiveState(true);
35411         if(this.box){
35412             panel.setSize(this.box.width, this.box.height);
35413         }
35414         this.fireEvent("panelactivated", this, panel);
35415         this.fireEvent("invalidated");
35416     },
35417     
35418     /**
35419      * Show the specified panel.
35420      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35421      * @return {Roo.ContentPanel} The shown panel or null
35422      */
35423     showPanel : function(panel){
35424         panel = this.getPanel(panel);
35425         if(panel){
35426             this.setActivePanel(panel);
35427         }
35428         return panel;
35429     },
35430     
35431     /**
35432      * Get the active panel for this region.
35433      * @return {Roo.ContentPanel} The active panel or null
35434      */
35435     getActivePanel : function(){
35436         return this.activePanel;
35437     },
35438     
35439     /**
35440      * Add the passed ContentPanel(s)
35441      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35442      * @return {Roo.ContentPanel} The panel added (if only one was added)
35443      */
35444     add : function(panel){
35445         if(arguments.length > 1){
35446             for(var i = 0, len = arguments.length; i < len; i++) {
35447                 this.add(arguments[i]);
35448             }
35449             return null;
35450         }
35451         if(this.hasPanel(panel)){
35452             this.showPanel(panel);
35453             return panel;
35454         }
35455         var el = panel.getEl();
35456         if(el.dom.parentNode != this.mgr.el.dom){
35457             this.mgr.el.dom.appendChild(el.dom);
35458         }
35459         if(panel.setRegion){
35460             panel.setRegion(this);
35461         }
35462         this.panels.add(panel);
35463         el.setStyle("position", "absolute");
35464         if(!panel.background){
35465             this.setActivePanel(panel);
35466             if(this.config.initialSize && this.panels.getCount()==1){
35467                 this.resizeTo(this.config.initialSize);
35468             }
35469         }
35470         this.fireEvent("paneladded", this, panel);
35471         return panel;
35472     },
35473     
35474     /**
35475      * Returns true if the panel is in this region.
35476      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35477      * @return {Boolean}
35478      */
35479     hasPanel : function(panel){
35480         if(typeof panel == "object"){ // must be panel obj
35481             panel = panel.getId();
35482         }
35483         return this.getPanel(panel) ? true : false;
35484     },
35485     
35486     /**
35487      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35488      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35489      * @param {Boolean} preservePanel Overrides the config preservePanel option
35490      * @return {Roo.ContentPanel} The panel that was removed
35491      */
35492     remove : function(panel, preservePanel){
35493         panel = this.getPanel(panel);
35494         if(!panel){
35495             return null;
35496         }
35497         var e = {};
35498         this.fireEvent("beforeremove", this, panel, e);
35499         if(e.cancel === true){
35500             return null;
35501         }
35502         var panelId = panel.getId();
35503         this.panels.removeKey(panelId);
35504         return panel;
35505     },
35506     
35507     /**
35508      * Returns the panel specified or null if it's not in this region.
35509      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35510      * @return {Roo.ContentPanel}
35511      */
35512     getPanel : function(id){
35513         if(typeof id == "object"){ // must be panel obj
35514             return id;
35515         }
35516         return this.panels.get(id);
35517     },
35518     
35519     /**
35520      * Returns this regions position (north/south/east/west/center).
35521      * @return {String} 
35522      */
35523     getPosition: function(){
35524         return this.position;    
35525     }
35526 });/*
35527  * Based on:
35528  * Ext JS Library 1.1.1
35529  * Copyright(c) 2006-2007, Ext JS, LLC.
35530  *
35531  * Originally Released Under LGPL - original licence link has changed is not relivant.
35532  *
35533  * Fork - LGPL
35534  * <script type="text/javascript">
35535  */
35536  
35537 /**
35538  * @class Roo.bootstrap.layout.Region
35539  * @extends Roo.bootstrap.layout.Basic
35540  * This class represents a region in a layout manager.
35541  
35542  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35543  * @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})
35544  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35545  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35546  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35547  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35548  * @cfg {String}    title           The title for the region (overrides panel titles)
35549  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35550  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35551  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35552  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35553  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35554  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35555  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35556  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35557  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35558  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35559
35560  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35561  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35562  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35563  * @cfg {Number}    width           For East/West panels
35564  * @cfg {Number}    height          For North/South panels
35565  * @cfg {Boolean}   split           To show the splitter
35566  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35567  * 
35568  * @cfg {string}   cls             Extra CSS classes to add to region
35569  * 
35570  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35571  * @cfg {string}   region  the region that it inhabits..
35572  *
35573
35574  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35575  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35576
35577  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35578  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35579  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35580  */
35581 Roo.bootstrap.layout.Region = function(config)
35582 {
35583     this.applyConfig(config);
35584
35585     var mgr = config.mgr;
35586     var pos = config.region;
35587     config.skipConfig = true;
35588     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35589     
35590     if (mgr.el) {
35591         this.onRender(mgr.el);   
35592     }
35593      
35594     this.visible = true;
35595     this.collapsed = false;
35596     this.unrendered_panels = [];
35597 };
35598
35599 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35600
35601     position: '', // set by wrapper (eg. north/south etc..)
35602     unrendered_panels : null,  // unrendered panels.
35603     createBody : function(){
35604         /** This region's body element 
35605         * @type Roo.Element */
35606         this.bodyEl = this.el.createChild({
35607                 tag: "div",
35608                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35609         });
35610     },
35611
35612     onRender: function(ctr, pos)
35613     {
35614         var dh = Roo.DomHelper;
35615         /** This region's container element 
35616         * @type Roo.Element */
35617         this.el = dh.append(ctr.dom, {
35618                 tag: "div",
35619                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35620             }, true);
35621         /** This region's title element 
35622         * @type Roo.Element */
35623     
35624         this.titleEl = dh.append(this.el.dom,
35625             {
35626                     tag: "div",
35627                     unselectable: "on",
35628                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35629                     children:[
35630                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35631                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35632                     ]}, true);
35633         
35634         this.titleEl.enableDisplayMode();
35635         /** This region's title text element 
35636         * @type HTMLElement */
35637         this.titleTextEl = this.titleEl.dom.firstChild;
35638         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35639         /*
35640         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35641         this.closeBtn.enableDisplayMode();
35642         this.closeBtn.on("click", this.closeClicked, this);
35643         this.closeBtn.hide();
35644     */
35645         this.createBody(this.config);
35646         if(this.config.hideWhenEmpty){
35647             this.hide();
35648             this.on("paneladded", this.validateVisibility, this);
35649             this.on("panelremoved", this.validateVisibility, this);
35650         }
35651         if(this.autoScroll){
35652             this.bodyEl.setStyle("overflow", "auto");
35653         }else{
35654             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35655         }
35656         //if(c.titlebar !== false){
35657             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35658                 this.titleEl.hide();
35659             }else{
35660                 this.titleEl.show();
35661                 if(this.config.title){
35662                     this.titleTextEl.innerHTML = this.config.title;
35663                 }
35664             }
35665         //}
35666         if(this.config.collapsed){
35667             this.collapse(true);
35668         }
35669         if(this.config.hidden){
35670             this.hide();
35671         }
35672         
35673         if (this.unrendered_panels && this.unrendered_panels.length) {
35674             for (var i =0;i< this.unrendered_panels.length; i++) {
35675                 this.add(this.unrendered_panels[i]);
35676             }
35677             this.unrendered_panels = null;
35678             
35679         }
35680         
35681     },
35682     
35683     applyConfig : function(c)
35684     {
35685         /*
35686          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35687             var dh = Roo.DomHelper;
35688             if(c.titlebar !== false){
35689                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35690                 this.collapseBtn.on("click", this.collapse, this);
35691                 this.collapseBtn.enableDisplayMode();
35692                 /*
35693                 if(c.showPin === true || this.showPin){
35694                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35695                     this.stickBtn.enableDisplayMode();
35696                     this.stickBtn.on("click", this.expand, this);
35697                     this.stickBtn.hide();
35698                 }
35699                 
35700             }
35701             */
35702             /** This region's collapsed element
35703             * @type Roo.Element */
35704             /*
35705              *
35706             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35707                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35708             ]}, true);
35709             
35710             if(c.floatable !== false){
35711                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35712                this.collapsedEl.on("click", this.collapseClick, this);
35713             }
35714
35715             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35716                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35717                    id: "message", unselectable: "on", style:{"float":"left"}});
35718                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35719              }
35720             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35721             this.expandBtn.on("click", this.expand, this);
35722             
35723         }
35724         
35725         if(this.collapseBtn){
35726             this.collapseBtn.setVisible(c.collapsible == true);
35727         }
35728         
35729         this.cmargins = c.cmargins || this.cmargins ||
35730                          (this.position == "west" || this.position == "east" ?
35731                              {top: 0, left: 2, right:2, bottom: 0} :
35732                              {top: 2, left: 0, right:0, bottom: 2});
35733         */
35734         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35735         
35736         
35737         this.bottomTabs = c.tabPosition != "top";
35738         
35739         this.autoScroll = c.autoScroll || false;
35740         
35741         
35742        
35743         
35744         this.duration = c.duration || .30;
35745         this.slideDuration = c.slideDuration || .45;
35746         this.config = c;
35747        
35748     },
35749     /**
35750      * Returns true if this region is currently visible.
35751      * @return {Boolean}
35752      */
35753     isVisible : function(){
35754         return this.visible;
35755     },
35756
35757     /**
35758      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35759      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35760      */
35761     //setCollapsedTitle : function(title){
35762     //    title = title || "&#160;";
35763      //   if(this.collapsedTitleTextEl){
35764       //      this.collapsedTitleTextEl.innerHTML = title;
35765        // }
35766     //},
35767
35768     getBox : function(){
35769         var b;
35770       //  if(!this.collapsed){
35771             b = this.el.getBox(false, true);
35772        // }else{
35773           //  b = this.collapsedEl.getBox(false, true);
35774         //}
35775         return b;
35776     },
35777
35778     getMargins : function(){
35779         return this.margins;
35780         //return this.collapsed ? this.cmargins : this.margins;
35781     },
35782 /*
35783     highlight : function(){
35784         this.el.addClass("x-layout-panel-dragover");
35785     },
35786
35787     unhighlight : function(){
35788         this.el.removeClass("x-layout-panel-dragover");
35789     },
35790 */
35791     updateBox : function(box)
35792     {
35793         if (!this.bodyEl) {
35794             return; // not rendered yet..
35795         }
35796         
35797         this.box = box;
35798         if(!this.collapsed){
35799             this.el.dom.style.left = box.x + "px";
35800             this.el.dom.style.top = box.y + "px";
35801             this.updateBody(box.width, box.height);
35802         }else{
35803             this.collapsedEl.dom.style.left = box.x + "px";
35804             this.collapsedEl.dom.style.top = box.y + "px";
35805             this.collapsedEl.setSize(box.width, box.height);
35806         }
35807         if(this.tabs){
35808             this.tabs.autoSizeTabs();
35809         }
35810     },
35811
35812     updateBody : function(w, h)
35813     {
35814         if(w !== null){
35815             this.el.setWidth(w);
35816             w -= this.el.getBorderWidth("rl");
35817             if(this.config.adjustments){
35818                 w += this.config.adjustments[0];
35819             }
35820         }
35821         if(h !== null && h > 0){
35822             this.el.setHeight(h);
35823             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35824             h -= this.el.getBorderWidth("tb");
35825             if(this.config.adjustments){
35826                 h += this.config.adjustments[1];
35827             }
35828             this.bodyEl.setHeight(h);
35829             if(this.tabs){
35830                 h = this.tabs.syncHeight(h);
35831             }
35832         }
35833         if(this.panelSize){
35834             w = w !== null ? w : this.panelSize.width;
35835             h = h !== null ? h : this.panelSize.height;
35836         }
35837         if(this.activePanel){
35838             var el = this.activePanel.getEl();
35839             w = w !== null ? w : el.getWidth();
35840             h = h !== null ? h : el.getHeight();
35841             this.panelSize = {width: w, height: h};
35842             this.activePanel.setSize(w, h);
35843         }
35844         if(Roo.isIE && this.tabs){
35845             this.tabs.el.repaint();
35846         }
35847     },
35848
35849     /**
35850      * Returns the container element for this region.
35851      * @return {Roo.Element}
35852      */
35853     getEl : function(){
35854         return this.el;
35855     },
35856
35857     /**
35858      * Hides this region.
35859      */
35860     hide : function(){
35861         //if(!this.collapsed){
35862             this.el.dom.style.left = "-2000px";
35863             this.el.hide();
35864         //}else{
35865          //   this.collapsedEl.dom.style.left = "-2000px";
35866          //   this.collapsedEl.hide();
35867        // }
35868         this.visible = false;
35869         this.fireEvent("visibilitychange", this, false);
35870     },
35871
35872     /**
35873      * Shows this region if it was previously hidden.
35874      */
35875     show : function(){
35876         //if(!this.collapsed){
35877             this.el.show();
35878         //}else{
35879         //    this.collapsedEl.show();
35880        // }
35881         this.visible = true;
35882         this.fireEvent("visibilitychange", this, true);
35883     },
35884 /*
35885     closeClicked : function(){
35886         if(this.activePanel){
35887             this.remove(this.activePanel);
35888         }
35889     },
35890
35891     collapseClick : function(e){
35892         if(this.isSlid){
35893            e.stopPropagation();
35894            this.slideIn();
35895         }else{
35896            e.stopPropagation();
35897            this.slideOut();
35898         }
35899     },
35900 */
35901     /**
35902      * Collapses this region.
35903      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35904      */
35905     /*
35906     collapse : function(skipAnim, skipCheck = false){
35907         if(this.collapsed) {
35908             return;
35909         }
35910         
35911         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35912             
35913             this.collapsed = true;
35914             if(this.split){
35915                 this.split.el.hide();
35916             }
35917             if(this.config.animate && skipAnim !== true){
35918                 this.fireEvent("invalidated", this);
35919                 this.animateCollapse();
35920             }else{
35921                 this.el.setLocation(-20000,-20000);
35922                 this.el.hide();
35923                 this.collapsedEl.show();
35924                 this.fireEvent("collapsed", this);
35925                 this.fireEvent("invalidated", this);
35926             }
35927         }
35928         
35929     },
35930 */
35931     animateCollapse : function(){
35932         // overridden
35933     },
35934
35935     /**
35936      * Expands this region if it was previously collapsed.
35937      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35938      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35939      */
35940     /*
35941     expand : function(e, skipAnim){
35942         if(e) {
35943             e.stopPropagation();
35944         }
35945         if(!this.collapsed || this.el.hasActiveFx()) {
35946             return;
35947         }
35948         if(this.isSlid){
35949             this.afterSlideIn();
35950             skipAnim = true;
35951         }
35952         this.collapsed = false;
35953         if(this.config.animate && skipAnim !== true){
35954             this.animateExpand();
35955         }else{
35956             this.el.show();
35957             if(this.split){
35958                 this.split.el.show();
35959             }
35960             this.collapsedEl.setLocation(-2000,-2000);
35961             this.collapsedEl.hide();
35962             this.fireEvent("invalidated", this);
35963             this.fireEvent("expanded", this);
35964         }
35965     },
35966 */
35967     animateExpand : function(){
35968         // overridden
35969     },
35970
35971     initTabs : function()
35972     {
35973         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35974         
35975         var ts = new Roo.bootstrap.panel.Tabs({
35976                 el: this.bodyEl.dom,
35977                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35978                 disableTooltips: this.config.disableTabTips,
35979                 toolbar : this.config.toolbar
35980             });
35981         
35982         if(this.config.hideTabs){
35983             ts.stripWrap.setDisplayed(false);
35984         }
35985         this.tabs = ts;
35986         ts.resizeTabs = this.config.resizeTabs === true;
35987         ts.minTabWidth = this.config.minTabWidth || 40;
35988         ts.maxTabWidth = this.config.maxTabWidth || 250;
35989         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35990         ts.monitorResize = false;
35991         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35992         ts.bodyEl.addClass('roo-layout-tabs-body');
35993         this.panels.each(this.initPanelAsTab, this);
35994     },
35995
35996     initPanelAsTab : function(panel){
35997         var ti = this.tabs.addTab(
35998             panel.getEl().id,
35999             panel.getTitle(),
36000             null,
36001             this.config.closeOnTab && panel.isClosable(),
36002             panel.tpl
36003         );
36004         if(panel.tabTip !== undefined){
36005             ti.setTooltip(panel.tabTip);
36006         }
36007         ti.on("activate", function(){
36008               this.setActivePanel(panel);
36009         }, this);
36010         
36011         if(this.config.closeOnTab){
36012             ti.on("beforeclose", function(t, e){
36013                 e.cancel = true;
36014                 this.remove(panel);
36015             }, this);
36016         }
36017         
36018         panel.tabItem = ti;
36019         
36020         return ti;
36021     },
36022
36023     updatePanelTitle : function(panel, title)
36024     {
36025         if(this.activePanel == panel){
36026             this.updateTitle(title);
36027         }
36028         if(this.tabs){
36029             var ti = this.tabs.getTab(panel.getEl().id);
36030             ti.setText(title);
36031             if(panel.tabTip !== undefined){
36032                 ti.setTooltip(panel.tabTip);
36033             }
36034         }
36035     },
36036
36037     updateTitle : function(title){
36038         if(this.titleTextEl && !this.config.title){
36039             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36040         }
36041     },
36042
36043     setActivePanel : function(panel)
36044     {
36045         panel = this.getPanel(panel);
36046         if(this.activePanel && this.activePanel != panel){
36047             if(this.activePanel.setActiveState(false) === false){
36048                 return;
36049             }
36050         }
36051         this.activePanel = panel;
36052         panel.setActiveState(true);
36053         if(this.panelSize){
36054             panel.setSize(this.panelSize.width, this.panelSize.height);
36055         }
36056         if(this.closeBtn){
36057             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36058         }
36059         this.updateTitle(panel.getTitle());
36060         if(this.tabs){
36061             this.fireEvent("invalidated", this);
36062         }
36063         this.fireEvent("panelactivated", this, panel);
36064     },
36065
36066     /**
36067      * Shows the specified panel.
36068      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36069      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36070      */
36071     showPanel : function(panel)
36072     {
36073         panel = this.getPanel(panel);
36074         if(panel){
36075             if(this.tabs){
36076                 var tab = this.tabs.getTab(panel.getEl().id);
36077                 if(tab.isHidden()){
36078                     this.tabs.unhideTab(tab.id);
36079                 }
36080                 tab.activate();
36081             }else{
36082                 this.setActivePanel(panel);
36083             }
36084         }
36085         return panel;
36086     },
36087
36088     /**
36089      * Get the active panel for this region.
36090      * @return {Roo.ContentPanel} The active panel or null
36091      */
36092     getActivePanel : function(){
36093         return this.activePanel;
36094     },
36095
36096     validateVisibility : function(){
36097         if(this.panels.getCount() < 1){
36098             this.updateTitle("&#160;");
36099             this.closeBtn.hide();
36100             this.hide();
36101         }else{
36102             if(!this.isVisible()){
36103                 this.show();
36104             }
36105         }
36106     },
36107
36108     /**
36109      * Adds the passed ContentPanel(s) to this region.
36110      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36111      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36112      */
36113     add : function(panel)
36114     {
36115         if(arguments.length > 1){
36116             for(var i = 0, len = arguments.length; i < len; i++) {
36117                 this.add(arguments[i]);
36118             }
36119             return null;
36120         }
36121         
36122         // if we have not been rendered yet, then we can not really do much of this..
36123         if (!this.bodyEl) {
36124             this.unrendered_panels.push(panel);
36125             return panel;
36126         }
36127         
36128         
36129         
36130         
36131         if(this.hasPanel(panel)){
36132             this.showPanel(panel);
36133             return panel;
36134         }
36135         panel.setRegion(this);
36136         this.panels.add(panel);
36137        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36138             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36139             // and hide them... ???
36140             this.bodyEl.dom.appendChild(panel.getEl().dom);
36141             if(panel.background !== true){
36142                 this.setActivePanel(panel);
36143             }
36144             this.fireEvent("paneladded", this, panel);
36145             return panel;
36146         }
36147         */
36148         if(!this.tabs){
36149             this.initTabs();
36150         }else{
36151             this.initPanelAsTab(panel);
36152         }
36153         
36154         
36155         if(panel.background !== true){
36156             this.tabs.activate(panel.getEl().id);
36157         }
36158         this.fireEvent("paneladded", this, panel);
36159         return panel;
36160     },
36161
36162     /**
36163      * Hides the tab for the specified panel.
36164      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36165      */
36166     hidePanel : function(panel){
36167         if(this.tabs && (panel = this.getPanel(panel))){
36168             this.tabs.hideTab(panel.getEl().id);
36169         }
36170     },
36171
36172     /**
36173      * Unhides the tab for a previously hidden panel.
36174      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36175      */
36176     unhidePanel : function(panel){
36177         if(this.tabs && (panel = this.getPanel(panel))){
36178             this.tabs.unhideTab(panel.getEl().id);
36179         }
36180     },
36181
36182     clearPanels : function(){
36183         while(this.panels.getCount() > 0){
36184              this.remove(this.panels.first());
36185         }
36186     },
36187
36188     /**
36189      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36190      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36191      * @param {Boolean} preservePanel Overrides the config preservePanel option
36192      * @return {Roo.ContentPanel} The panel that was removed
36193      */
36194     remove : function(panel, preservePanel)
36195     {
36196         panel = this.getPanel(panel);
36197         if(!panel){
36198             return null;
36199         }
36200         var e = {};
36201         this.fireEvent("beforeremove", this, panel, e);
36202         if(e.cancel === true){
36203             return null;
36204         }
36205         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36206         var panelId = panel.getId();
36207         this.panels.removeKey(panelId);
36208         if(preservePanel){
36209             document.body.appendChild(panel.getEl().dom);
36210         }
36211         if(this.tabs){
36212             this.tabs.removeTab(panel.getEl().id);
36213         }else if (!preservePanel){
36214             this.bodyEl.dom.removeChild(panel.getEl().dom);
36215         }
36216         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36217             var p = this.panels.first();
36218             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36219             tempEl.appendChild(p.getEl().dom);
36220             this.bodyEl.update("");
36221             this.bodyEl.dom.appendChild(p.getEl().dom);
36222             tempEl = null;
36223             this.updateTitle(p.getTitle());
36224             this.tabs = null;
36225             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36226             this.setActivePanel(p);
36227         }
36228         panel.setRegion(null);
36229         if(this.activePanel == panel){
36230             this.activePanel = null;
36231         }
36232         if(this.config.autoDestroy !== false && preservePanel !== true){
36233             try{panel.destroy();}catch(e){}
36234         }
36235         this.fireEvent("panelremoved", this, panel);
36236         return panel;
36237     },
36238
36239     /**
36240      * Returns the TabPanel component used by this region
36241      * @return {Roo.TabPanel}
36242      */
36243     getTabs : function(){
36244         return this.tabs;
36245     },
36246
36247     createTool : function(parentEl, className){
36248         var btn = Roo.DomHelper.append(parentEl, {
36249             tag: "div",
36250             cls: "x-layout-tools-button",
36251             children: [ {
36252                 tag: "div",
36253                 cls: "roo-layout-tools-button-inner " + className,
36254                 html: "&#160;"
36255             }]
36256         }, true);
36257         btn.addClassOnOver("roo-layout-tools-button-over");
36258         return btn;
36259     }
36260 });/*
36261  * Based on:
36262  * Ext JS Library 1.1.1
36263  * Copyright(c) 2006-2007, Ext JS, LLC.
36264  *
36265  * Originally Released Under LGPL - original licence link has changed is not relivant.
36266  *
36267  * Fork - LGPL
36268  * <script type="text/javascript">
36269  */
36270  
36271
36272
36273 /**
36274  * @class Roo.SplitLayoutRegion
36275  * @extends Roo.LayoutRegion
36276  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36277  */
36278 Roo.bootstrap.layout.Split = function(config){
36279     this.cursor = config.cursor;
36280     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36281 };
36282
36283 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36284 {
36285     splitTip : "Drag to resize.",
36286     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36287     useSplitTips : false,
36288
36289     applyConfig : function(config){
36290         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36291     },
36292     
36293     onRender : function(ctr,pos) {
36294         
36295         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36296         if(!this.config.split){
36297             return;
36298         }
36299         if(!this.split){
36300             
36301             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36302                             tag: "div",
36303                             id: this.el.id + "-split",
36304                             cls: "roo-layout-split roo-layout-split-"+this.position,
36305                             html: "&#160;"
36306             });
36307             /** The SplitBar for this region 
36308             * @type Roo.SplitBar */
36309             // does not exist yet...
36310             Roo.log([this.position, this.orientation]);
36311             
36312             this.split = new Roo.bootstrap.SplitBar({
36313                 dragElement : splitEl,
36314                 resizingElement: this.el,
36315                 orientation : this.orientation
36316             });
36317             
36318             this.split.on("moved", this.onSplitMove, this);
36319             this.split.useShim = this.config.useShim === true;
36320             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36321             if(this.useSplitTips){
36322                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36323             }
36324             //if(config.collapsible){
36325             //    this.split.el.on("dblclick", this.collapse,  this);
36326             //}
36327         }
36328         if(typeof this.config.minSize != "undefined"){
36329             this.split.minSize = this.config.minSize;
36330         }
36331         if(typeof this.config.maxSize != "undefined"){
36332             this.split.maxSize = this.config.maxSize;
36333         }
36334         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36335             this.hideSplitter();
36336         }
36337         
36338     },
36339
36340     getHMaxSize : function(){
36341          var cmax = this.config.maxSize || 10000;
36342          var center = this.mgr.getRegion("center");
36343          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36344     },
36345
36346     getVMaxSize : function(){
36347          var cmax = this.config.maxSize || 10000;
36348          var center = this.mgr.getRegion("center");
36349          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36350     },
36351
36352     onSplitMove : function(split, newSize){
36353         this.fireEvent("resized", this, newSize);
36354     },
36355     
36356     /** 
36357      * Returns the {@link Roo.SplitBar} for this region.
36358      * @return {Roo.SplitBar}
36359      */
36360     getSplitBar : function(){
36361         return this.split;
36362     },
36363     
36364     hide : function(){
36365         this.hideSplitter();
36366         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36367     },
36368
36369     hideSplitter : function(){
36370         if(this.split){
36371             this.split.el.setLocation(-2000,-2000);
36372             this.split.el.hide();
36373         }
36374     },
36375
36376     show : function(){
36377         if(this.split){
36378             this.split.el.show();
36379         }
36380         Roo.bootstrap.layout.Split.superclass.show.call(this);
36381     },
36382     
36383     beforeSlide: function(){
36384         if(Roo.isGecko){// firefox overflow auto bug workaround
36385             this.bodyEl.clip();
36386             if(this.tabs) {
36387                 this.tabs.bodyEl.clip();
36388             }
36389             if(this.activePanel){
36390                 this.activePanel.getEl().clip();
36391                 
36392                 if(this.activePanel.beforeSlide){
36393                     this.activePanel.beforeSlide();
36394                 }
36395             }
36396         }
36397     },
36398     
36399     afterSlide : function(){
36400         if(Roo.isGecko){// firefox overflow auto bug workaround
36401             this.bodyEl.unclip();
36402             if(this.tabs) {
36403                 this.tabs.bodyEl.unclip();
36404             }
36405             if(this.activePanel){
36406                 this.activePanel.getEl().unclip();
36407                 if(this.activePanel.afterSlide){
36408                     this.activePanel.afterSlide();
36409                 }
36410             }
36411         }
36412     },
36413
36414     initAutoHide : function(){
36415         if(this.autoHide !== false){
36416             if(!this.autoHideHd){
36417                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36418                 this.autoHideHd = {
36419                     "mouseout": function(e){
36420                         if(!e.within(this.el, true)){
36421                             st.delay(500);
36422                         }
36423                     },
36424                     "mouseover" : function(e){
36425                         st.cancel();
36426                     },
36427                     scope : this
36428                 };
36429             }
36430             this.el.on(this.autoHideHd);
36431         }
36432     },
36433
36434     clearAutoHide : function(){
36435         if(this.autoHide !== false){
36436             this.el.un("mouseout", this.autoHideHd.mouseout);
36437             this.el.un("mouseover", this.autoHideHd.mouseover);
36438         }
36439     },
36440
36441     clearMonitor : function(){
36442         Roo.get(document).un("click", this.slideInIf, this);
36443     },
36444
36445     // these names are backwards but not changed for compat
36446     slideOut : function(){
36447         if(this.isSlid || this.el.hasActiveFx()){
36448             return;
36449         }
36450         this.isSlid = true;
36451         if(this.collapseBtn){
36452             this.collapseBtn.hide();
36453         }
36454         this.closeBtnState = this.closeBtn.getStyle('display');
36455         this.closeBtn.hide();
36456         if(this.stickBtn){
36457             this.stickBtn.show();
36458         }
36459         this.el.show();
36460         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36461         this.beforeSlide();
36462         this.el.setStyle("z-index", 10001);
36463         this.el.slideIn(this.getSlideAnchor(), {
36464             callback: function(){
36465                 this.afterSlide();
36466                 this.initAutoHide();
36467                 Roo.get(document).on("click", this.slideInIf, this);
36468                 this.fireEvent("slideshow", this);
36469             },
36470             scope: this,
36471             block: true
36472         });
36473     },
36474
36475     afterSlideIn : function(){
36476         this.clearAutoHide();
36477         this.isSlid = false;
36478         this.clearMonitor();
36479         this.el.setStyle("z-index", "");
36480         if(this.collapseBtn){
36481             this.collapseBtn.show();
36482         }
36483         this.closeBtn.setStyle('display', this.closeBtnState);
36484         if(this.stickBtn){
36485             this.stickBtn.hide();
36486         }
36487         this.fireEvent("slidehide", this);
36488     },
36489
36490     slideIn : function(cb){
36491         if(!this.isSlid || this.el.hasActiveFx()){
36492             Roo.callback(cb);
36493             return;
36494         }
36495         this.isSlid = false;
36496         this.beforeSlide();
36497         this.el.slideOut(this.getSlideAnchor(), {
36498             callback: function(){
36499                 this.el.setLeftTop(-10000, -10000);
36500                 this.afterSlide();
36501                 this.afterSlideIn();
36502                 Roo.callback(cb);
36503             },
36504             scope: this,
36505             block: true
36506         });
36507     },
36508     
36509     slideInIf : function(e){
36510         if(!e.within(this.el)){
36511             this.slideIn();
36512         }
36513     },
36514
36515     animateCollapse : function(){
36516         this.beforeSlide();
36517         this.el.setStyle("z-index", 20000);
36518         var anchor = this.getSlideAnchor();
36519         this.el.slideOut(anchor, {
36520             callback : function(){
36521                 this.el.setStyle("z-index", "");
36522                 this.collapsedEl.slideIn(anchor, {duration:.3});
36523                 this.afterSlide();
36524                 this.el.setLocation(-10000,-10000);
36525                 this.el.hide();
36526                 this.fireEvent("collapsed", this);
36527             },
36528             scope: this,
36529             block: true
36530         });
36531     },
36532
36533     animateExpand : function(){
36534         this.beforeSlide();
36535         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36536         this.el.setStyle("z-index", 20000);
36537         this.collapsedEl.hide({
36538             duration:.1
36539         });
36540         this.el.slideIn(this.getSlideAnchor(), {
36541             callback : function(){
36542                 this.el.setStyle("z-index", "");
36543                 this.afterSlide();
36544                 if(this.split){
36545                     this.split.el.show();
36546                 }
36547                 this.fireEvent("invalidated", this);
36548                 this.fireEvent("expanded", this);
36549             },
36550             scope: this,
36551             block: true
36552         });
36553     },
36554
36555     anchors : {
36556         "west" : "left",
36557         "east" : "right",
36558         "north" : "top",
36559         "south" : "bottom"
36560     },
36561
36562     sanchors : {
36563         "west" : "l",
36564         "east" : "r",
36565         "north" : "t",
36566         "south" : "b"
36567     },
36568
36569     canchors : {
36570         "west" : "tl-tr",
36571         "east" : "tr-tl",
36572         "north" : "tl-bl",
36573         "south" : "bl-tl"
36574     },
36575
36576     getAnchor : function(){
36577         return this.anchors[this.position];
36578     },
36579
36580     getCollapseAnchor : function(){
36581         return this.canchors[this.position];
36582     },
36583
36584     getSlideAnchor : function(){
36585         return this.sanchors[this.position];
36586     },
36587
36588     getAlignAdj : function(){
36589         var cm = this.cmargins;
36590         switch(this.position){
36591             case "west":
36592                 return [0, 0];
36593             break;
36594             case "east":
36595                 return [0, 0];
36596             break;
36597             case "north":
36598                 return [0, 0];
36599             break;
36600             case "south":
36601                 return [0, 0];
36602             break;
36603         }
36604     },
36605
36606     getExpandAdj : function(){
36607         var c = this.collapsedEl, cm = this.cmargins;
36608         switch(this.position){
36609             case "west":
36610                 return [-(cm.right+c.getWidth()+cm.left), 0];
36611             break;
36612             case "east":
36613                 return [cm.right+c.getWidth()+cm.left, 0];
36614             break;
36615             case "north":
36616                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36617             break;
36618             case "south":
36619                 return [0, cm.top+cm.bottom+c.getHeight()];
36620             break;
36621         }
36622     }
36623 });/*
36624  * Based on:
36625  * Ext JS Library 1.1.1
36626  * Copyright(c) 2006-2007, Ext JS, LLC.
36627  *
36628  * Originally Released Under LGPL - original licence link has changed is not relivant.
36629  *
36630  * Fork - LGPL
36631  * <script type="text/javascript">
36632  */
36633 /*
36634  * These classes are private internal classes
36635  */
36636 Roo.bootstrap.layout.Center = function(config){
36637     config.region = "center";
36638     Roo.bootstrap.layout.Region.call(this, config);
36639     this.visible = true;
36640     this.minWidth = config.minWidth || 20;
36641     this.minHeight = config.minHeight || 20;
36642 };
36643
36644 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36645     hide : function(){
36646         // center panel can't be hidden
36647     },
36648     
36649     show : function(){
36650         // center panel can't be hidden
36651     },
36652     
36653     getMinWidth: function(){
36654         return this.minWidth;
36655     },
36656     
36657     getMinHeight: function(){
36658         return this.minHeight;
36659     }
36660 });
36661
36662
36663
36664
36665  
36666
36667
36668
36669
36670
36671 Roo.bootstrap.layout.North = function(config)
36672 {
36673     config.region = 'north';
36674     config.cursor = 'n-resize';
36675     
36676     Roo.bootstrap.layout.Split.call(this, config);
36677     
36678     
36679     if(this.split){
36680         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36681         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36682         this.split.el.addClass("roo-layout-split-v");
36683     }
36684     var size = config.initialSize || config.height;
36685     if(typeof size != "undefined"){
36686         this.el.setHeight(size);
36687     }
36688 };
36689 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36690 {
36691     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36692     
36693     
36694     
36695     getBox : function(){
36696         if(this.collapsed){
36697             return this.collapsedEl.getBox();
36698         }
36699         var box = this.el.getBox();
36700         if(this.split){
36701             box.height += this.split.el.getHeight();
36702         }
36703         return box;
36704     },
36705     
36706     updateBox : function(box){
36707         if(this.split && !this.collapsed){
36708             box.height -= this.split.el.getHeight();
36709             this.split.el.setLeft(box.x);
36710             this.split.el.setTop(box.y+box.height);
36711             this.split.el.setWidth(box.width);
36712         }
36713         if(this.collapsed){
36714             this.updateBody(box.width, null);
36715         }
36716         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36717     }
36718 });
36719
36720
36721
36722
36723
36724 Roo.bootstrap.layout.South = function(config){
36725     config.region = 'south';
36726     config.cursor = 's-resize';
36727     Roo.bootstrap.layout.Split.call(this, config);
36728     if(this.split){
36729         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36730         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36731         this.split.el.addClass("roo-layout-split-v");
36732     }
36733     var size = config.initialSize || config.height;
36734     if(typeof size != "undefined"){
36735         this.el.setHeight(size);
36736     }
36737 };
36738
36739 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36740     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36741     getBox : function(){
36742         if(this.collapsed){
36743             return this.collapsedEl.getBox();
36744         }
36745         var box = this.el.getBox();
36746         if(this.split){
36747             var sh = this.split.el.getHeight();
36748             box.height += sh;
36749             box.y -= sh;
36750         }
36751         return box;
36752     },
36753     
36754     updateBox : function(box){
36755         if(this.split && !this.collapsed){
36756             var sh = this.split.el.getHeight();
36757             box.height -= sh;
36758             box.y += sh;
36759             this.split.el.setLeft(box.x);
36760             this.split.el.setTop(box.y-sh);
36761             this.split.el.setWidth(box.width);
36762         }
36763         if(this.collapsed){
36764             this.updateBody(box.width, null);
36765         }
36766         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36767     }
36768 });
36769
36770 Roo.bootstrap.layout.East = function(config){
36771     config.region = "east";
36772     config.cursor = "e-resize";
36773     Roo.bootstrap.layout.Split.call(this, config);
36774     if(this.split){
36775         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36776         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36777         this.split.el.addClass("roo-layout-split-h");
36778     }
36779     var size = config.initialSize || config.width;
36780     if(typeof size != "undefined"){
36781         this.el.setWidth(size);
36782     }
36783 };
36784 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36785     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36786     getBox : function(){
36787         if(this.collapsed){
36788             return this.collapsedEl.getBox();
36789         }
36790         var box = this.el.getBox();
36791         if(this.split){
36792             var sw = this.split.el.getWidth();
36793             box.width += sw;
36794             box.x -= sw;
36795         }
36796         return box;
36797     },
36798
36799     updateBox : function(box){
36800         if(this.split && !this.collapsed){
36801             var sw = this.split.el.getWidth();
36802             box.width -= sw;
36803             this.split.el.setLeft(box.x);
36804             this.split.el.setTop(box.y);
36805             this.split.el.setHeight(box.height);
36806             box.x += sw;
36807         }
36808         if(this.collapsed){
36809             this.updateBody(null, box.height);
36810         }
36811         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36812     }
36813 });
36814
36815 Roo.bootstrap.layout.West = function(config){
36816     config.region = "west";
36817     config.cursor = "w-resize";
36818     
36819     Roo.bootstrap.layout.Split.call(this, config);
36820     if(this.split){
36821         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36822         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36823         this.split.el.addClass("roo-layout-split-h");
36824     }
36825     
36826 };
36827 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36828     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36829     
36830     onRender: function(ctr, pos)
36831     {
36832         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36833         var size = this.config.initialSize || this.config.width;
36834         if(typeof size != "undefined"){
36835             this.el.setWidth(size);
36836         }
36837     },
36838     
36839     getBox : function(){
36840         if(this.collapsed){
36841             return this.collapsedEl.getBox();
36842         }
36843         var box = this.el.getBox();
36844         if(this.split){
36845             box.width += this.split.el.getWidth();
36846         }
36847         return box;
36848     },
36849     
36850     updateBox : function(box){
36851         if(this.split && !this.collapsed){
36852             var sw = this.split.el.getWidth();
36853             box.width -= sw;
36854             this.split.el.setLeft(box.x+box.width);
36855             this.split.el.setTop(box.y);
36856             this.split.el.setHeight(box.height);
36857         }
36858         if(this.collapsed){
36859             this.updateBody(null, box.height);
36860         }
36861         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36862     }
36863 });
36864 Roo.namespace("Roo.bootstrap.panel");/*
36865  * Based on:
36866  * Ext JS Library 1.1.1
36867  * Copyright(c) 2006-2007, Ext JS, LLC.
36868  *
36869  * Originally Released Under LGPL - original licence link has changed is not relivant.
36870  *
36871  * Fork - LGPL
36872  * <script type="text/javascript">
36873  */
36874 /**
36875  * @class Roo.ContentPanel
36876  * @extends Roo.util.Observable
36877  * A basic ContentPanel element.
36878  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36879  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36880  * @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
36881  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36882  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36883  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36884  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36885  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36886  * @cfg {String} title          The title for this panel
36887  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36888  * @cfg {String} url            Calls {@link #setUrl} with this value
36889  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36890  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36891  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36892  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36893  * @cfg {Boolean} badges render the badges
36894
36895  * @constructor
36896  * Create a new ContentPanel.
36897  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36898  * @param {String/Object} config A string to set only the title or a config object
36899  * @param {String} content (optional) Set the HTML content for this panel
36900  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36901  */
36902 Roo.bootstrap.panel.Content = function( config){
36903     
36904     this.tpl = config.tpl || false;
36905     
36906     var el = config.el;
36907     var content = config.content;
36908
36909     if(config.autoCreate){ // xtype is available if this is called from factory
36910         el = Roo.id();
36911     }
36912     this.el = Roo.get(el);
36913     if(!this.el && config && config.autoCreate){
36914         if(typeof config.autoCreate == "object"){
36915             if(!config.autoCreate.id){
36916                 config.autoCreate.id = config.id||el;
36917             }
36918             this.el = Roo.DomHelper.append(document.body,
36919                         config.autoCreate, true);
36920         }else{
36921             var elcfg =  {   tag: "div",
36922                             cls: "roo-layout-inactive-content",
36923                             id: config.id||el
36924                             };
36925             if (config.html) {
36926                 elcfg.html = config.html;
36927                 
36928             }
36929                         
36930             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36931         }
36932     } 
36933     this.closable = false;
36934     this.loaded = false;
36935     this.active = false;
36936    
36937       
36938     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36939         
36940         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36941         
36942         this.wrapEl = this.el; //this.el.wrap();
36943         var ti = [];
36944         if (config.toolbar.items) {
36945             ti = config.toolbar.items ;
36946             delete config.toolbar.items ;
36947         }
36948         
36949         var nitems = [];
36950         this.toolbar.render(this.wrapEl, 'before');
36951         for(var i =0;i < ti.length;i++) {
36952           //  Roo.log(['add child', items[i]]);
36953             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36954         }
36955         this.toolbar.items = nitems;
36956         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36957         delete config.toolbar;
36958         
36959     }
36960     /*
36961     // xtype created footer. - not sure if will work as we normally have to render first..
36962     if (this.footer && !this.footer.el && this.footer.xtype) {
36963         if (!this.wrapEl) {
36964             this.wrapEl = this.el.wrap();
36965         }
36966     
36967         this.footer.container = this.wrapEl.createChild();
36968          
36969         this.footer = Roo.factory(this.footer, Roo);
36970         
36971     }
36972     */
36973     
36974      if(typeof config == "string"){
36975         this.title = config;
36976     }else{
36977         Roo.apply(this, config);
36978     }
36979     
36980     if(this.resizeEl){
36981         this.resizeEl = Roo.get(this.resizeEl, true);
36982     }else{
36983         this.resizeEl = this.el;
36984     }
36985     // handle view.xtype
36986     
36987  
36988     
36989     
36990     this.addEvents({
36991         /**
36992          * @event activate
36993          * Fires when this panel is activated. 
36994          * @param {Roo.ContentPanel} this
36995          */
36996         "activate" : true,
36997         /**
36998          * @event deactivate
36999          * Fires when this panel is activated. 
37000          * @param {Roo.ContentPanel} this
37001          */
37002         "deactivate" : true,
37003
37004         /**
37005          * @event resize
37006          * Fires when this panel is resized if fitToFrame is true.
37007          * @param {Roo.ContentPanel} this
37008          * @param {Number} width The width after any component adjustments
37009          * @param {Number} height The height after any component adjustments
37010          */
37011         "resize" : true,
37012         
37013          /**
37014          * @event render
37015          * Fires when this tab is created
37016          * @param {Roo.ContentPanel} this
37017          */
37018         "render" : true
37019         
37020         
37021         
37022     });
37023     
37024
37025     
37026     
37027     if(this.autoScroll){
37028         this.resizeEl.setStyle("overflow", "auto");
37029     } else {
37030         // fix randome scrolling
37031         //this.el.on('scroll', function() {
37032         //    Roo.log('fix random scolling');
37033         //    this.scrollTo('top',0); 
37034         //});
37035     }
37036     content = content || this.content;
37037     if(content){
37038         this.setContent(content);
37039     }
37040     if(config && config.url){
37041         this.setUrl(this.url, this.params, this.loadOnce);
37042     }
37043     
37044     
37045     
37046     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37047     
37048     if (this.view && typeof(this.view.xtype) != 'undefined') {
37049         this.view.el = this.el.appendChild(document.createElement("div"));
37050         this.view = Roo.factory(this.view); 
37051         this.view.render  &&  this.view.render(false, '');  
37052     }
37053     
37054     
37055     this.fireEvent('render', this);
37056 };
37057
37058 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37059     
37060     tabTip : '',
37061     
37062     setRegion : function(region){
37063         this.region = region;
37064         this.setActiveClass(region && !this.background);
37065     },
37066     
37067     
37068     setActiveClass: function(state)
37069     {
37070         if(state){
37071            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37072            this.el.setStyle('position','relative');
37073         }else{
37074            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37075            this.el.setStyle('position', 'absolute');
37076         } 
37077     },
37078     
37079     /**
37080      * Returns the toolbar for this Panel if one was configured. 
37081      * @return {Roo.Toolbar} 
37082      */
37083     getToolbar : function(){
37084         return this.toolbar;
37085     },
37086     
37087     setActiveState : function(active)
37088     {
37089         this.active = active;
37090         this.setActiveClass(active);
37091         if(!active){
37092             if(this.fireEvent("deactivate", this) === false){
37093                 return false;
37094             }
37095             return true;
37096         }
37097         this.fireEvent("activate", this);
37098         return true;
37099     },
37100     /**
37101      * Updates this panel's element
37102      * @param {String} content The new content
37103      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37104     */
37105     setContent : function(content, loadScripts){
37106         this.el.update(content, loadScripts);
37107     },
37108
37109     ignoreResize : function(w, h){
37110         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37111             return true;
37112         }else{
37113             this.lastSize = {width: w, height: h};
37114             return false;
37115         }
37116     },
37117     /**
37118      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37119      * @return {Roo.UpdateManager} The UpdateManager
37120      */
37121     getUpdateManager : function(){
37122         return this.el.getUpdateManager();
37123     },
37124      /**
37125      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37126      * @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:
37127 <pre><code>
37128 panel.load({
37129     url: "your-url.php",
37130     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37131     callback: yourFunction,
37132     scope: yourObject, //(optional scope)
37133     discardUrl: false,
37134     nocache: false,
37135     text: "Loading...",
37136     timeout: 30,
37137     scripts: false
37138 });
37139 </code></pre>
37140      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37141      * 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.
37142      * @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}
37143      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37144      * @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.
37145      * @return {Roo.ContentPanel} this
37146      */
37147     load : function(){
37148         var um = this.el.getUpdateManager();
37149         um.update.apply(um, arguments);
37150         return this;
37151     },
37152
37153
37154     /**
37155      * 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.
37156      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37157      * @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)
37158      * @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)
37159      * @return {Roo.UpdateManager} The UpdateManager
37160      */
37161     setUrl : function(url, params, loadOnce){
37162         if(this.refreshDelegate){
37163             this.removeListener("activate", this.refreshDelegate);
37164         }
37165         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37166         this.on("activate", this.refreshDelegate);
37167         return this.el.getUpdateManager();
37168     },
37169     
37170     _handleRefresh : function(url, params, loadOnce){
37171         if(!loadOnce || !this.loaded){
37172             var updater = this.el.getUpdateManager();
37173             updater.update(url, params, this._setLoaded.createDelegate(this));
37174         }
37175     },
37176     
37177     _setLoaded : function(){
37178         this.loaded = true;
37179     }, 
37180     
37181     /**
37182      * Returns this panel's id
37183      * @return {String} 
37184      */
37185     getId : function(){
37186         return this.el.id;
37187     },
37188     
37189     /** 
37190      * Returns this panel's element - used by regiosn to add.
37191      * @return {Roo.Element} 
37192      */
37193     getEl : function(){
37194         return this.wrapEl || this.el;
37195     },
37196     
37197    
37198     
37199     adjustForComponents : function(width, height)
37200     {
37201         //Roo.log('adjustForComponents ');
37202         if(this.resizeEl != this.el){
37203             width -= this.el.getFrameWidth('lr');
37204             height -= this.el.getFrameWidth('tb');
37205         }
37206         if(this.toolbar){
37207             var te = this.toolbar.getEl();
37208             te.setWidth(width);
37209             height -= te.getHeight();
37210         }
37211         if(this.footer){
37212             var te = this.footer.getEl();
37213             te.setWidth(width);
37214             height -= te.getHeight();
37215         }
37216         
37217         
37218         if(this.adjustments){
37219             width += this.adjustments[0];
37220             height += this.adjustments[1];
37221         }
37222         return {"width": width, "height": height};
37223     },
37224     
37225     setSize : function(width, height){
37226         if(this.fitToFrame && !this.ignoreResize(width, height)){
37227             if(this.fitContainer && this.resizeEl != this.el){
37228                 this.el.setSize(width, height);
37229             }
37230             var size = this.adjustForComponents(width, height);
37231             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37232             this.fireEvent('resize', this, size.width, size.height);
37233         }
37234     },
37235     
37236     /**
37237      * Returns this panel's title
37238      * @return {String} 
37239      */
37240     getTitle : function(){
37241         
37242         if (typeof(this.title) != 'object') {
37243             return this.title;
37244         }
37245         
37246         var t = '';
37247         for (var k in this.title) {
37248             if (!this.title.hasOwnProperty(k)) {
37249                 continue;
37250             }
37251             
37252             if (k.indexOf('-') >= 0) {
37253                 var s = k.split('-');
37254                 for (var i = 0; i<s.length; i++) {
37255                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37256                 }
37257             } else {
37258                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37259             }
37260         }
37261         return t;
37262     },
37263     
37264     /**
37265      * Set this panel's title
37266      * @param {String} title
37267      */
37268     setTitle : function(title){
37269         this.title = title;
37270         if(this.region){
37271             this.region.updatePanelTitle(this, title);
37272         }
37273     },
37274     
37275     /**
37276      * Returns true is this panel was configured to be closable
37277      * @return {Boolean} 
37278      */
37279     isClosable : function(){
37280         return this.closable;
37281     },
37282     
37283     beforeSlide : function(){
37284         this.el.clip();
37285         this.resizeEl.clip();
37286     },
37287     
37288     afterSlide : function(){
37289         this.el.unclip();
37290         this.resizeEl.unclip();
37291     },
37292     
37293     /**
37294      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37295      *   Will fail silently if the {@link #setUrl} method has not been called.
37296      *   This does not activate the panel, just updates its content.
37297      */
37298     refresh : function(){
37299         if(this.refreshDelegate){
37300            this.loaded = false;
37301            this.refreshDelegate();
37302         }
37303     },
37304     
37305     /**
37306      * Destroys this panel
37307      */
37308     destroy : function(){
37309         this.el.removeAllListeners();
37310         var tempEl = document.createElement("span");
37311         tempEl.appendChild(this.el.dom);
37312         tempEl.innerHTML = "";
37313         this.el.remove();
37314         this.el = null;
37315     },
37316     
37317     /**
37318      * form - if the content panel contains a form - this is a reference to it.
37319      * @type {Roo.form.Form}
37320      */
37321     form : false,
37322     /**
37323      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37324      *    This contains a reference to it.
37325      * @type {Roo.View}
37326      */
37327     view : false,
37328     
37329       /**
37330      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37331      * <pre><code>
37332
37333 layout.addxtype({
37334        xtype : 'Form',
37335        items: [ .... ]
37336    }
37337 );
37338
37339 </code></pre>
37340      * @param {Object} cfg Xtype definition of item to add.
37341      */
37342     
37343     
37344     getChildContainer: function () {
37345         return this.getEl();
37346     }
37347     
37348     
37349     /*
37350         var  ret = new Roo.factory(cfg);
37351         return ret;
37352         
37353         
37354         // add form..
37355         if (cfg.xtype.match(/^Form$/)) {
37356             
37357             var el;
37358             //if (this.footer) {
37359             //    el = this.footer.container.insertSibling(false, 'before');
37360             //} else {
37361                 el = this.el.createChild();
37362             //}
37363
37364             this.form = new  Roo.form.Form(cfg);
37365             
37366             
37367             if ( this.form.allItems.length) {
37368                 this.form.render(el.dom);
37369             }
37370             return this.form;
37371         }
37372         // should only have one of theses..
37373         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37374             // views.. should not be just added - used named prop 'view''
37375             
37376             cfg.el = this.el.appendChild(document.createElement("div"));
37377             // factory?
37378             
37379             var ret = new Roo.factory(cfg);
37380              
37381              ret.render && ret.render(false, ''); // render blank..
37382             this.view = ret;
37383             return ret;
37384         }
37385         return false;
37386     }
37387     \*/
37388 });
37389  
37390 /**
37391  * @class Roo.bootstrap.panel.Grid
37392  * @extends Roo.bootstrap.panel.Content
37393  * @constructor
37394  * Create a new GridPanel.
37395  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37396  * @param {Object} config A the config object
37397   
37398  */
37399
37400
37401
37402 Roo.bootstrap.panel.Grid = function(config)
37403 {
37404     
37405       
37406     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37407         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37408
37409     config.el = this.wrapper;
37410     //this.el = this.wrapper;
37411     
37412       if (config.container) {
37413         // ctor'ed from a Border/panel.grid
37414         
37415         
37416         this.wrapper.setStyle("overflow", "hidden");
37417         this.wrapper.addClass('roo-grid-container');
37418
37419     }
37420     
37421     
37422     if(config.toolbar){
37423         var tool_el = this.wrapper.createChild();    
37424         this.toolbar = Roo.factory(config.toolbar);
37425         var ti = [];
37426         if (config.toolbar.items) {
37427             ti = config.toolbar.items ;
37428             delete config.toolbar.items ;
37429         }
37430         
37431         var nitems = [];
37432         this.toolbar.render(tool_el);
37433         for(var i =0;i < ti.length;i++) {
37434           //  Roo.log(['add child', items[i]]);
37435             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37436         }
37437         this.toolbar.items = nitems;
37438         
37439         delete config.toolbar;
37440     }
37441     
37442     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37443     config.grid.scrollBody = true;;
37444     config.grid.monitorWindowResize = false; // turn off autosizing
37445     config.grid.autoHeight = false;
37446     config.grid.autoWidth = false;
37447     
37448     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37449     
37450     if (config.background) {
37451         // render grid on panel activation (if panel background)
37452         this.on('activate', function(gp) {
37453             if (!gp.grid.rendered) {
37454                 gp.grid.render(this.wrapper);
37455                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37456             }
37457         });
37458             
37459     } else {
37460         this.grid.render(this.wrapper);
37461         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37462
37463     }
37464     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37465     // ??? needed ??? config.el = this.wrapper;
37466     
37467     
37468     
37469   
37470     // xtype created footer. - not sure if will work as we normally have to render first..
37471     if (this.footer && !this.footer.el && this.footer.xtype) {
37472         
37473         var ctr = this.grid.getView().getFooterPanel(true);
37474         this.footer.dataSource = this.grid.dataSource;
37475         this.footer = Roo.factory(this.footer, Roo);
37476         this.footer.render(ctr);
37477         
37478     }
37479     
37480     
37481     
37482     
37483      
37484 };
37485
37486 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37487     getId : function(){
37488         return this.grid.id;
37489     },
37490     
37491     /**
37492      * Returns the grid for this panel
37493      * @return {Roo.bootstrap.Table} 
37494      */
37495     getGrid : function(){
37496         return this.grid;    
37497     },
37498     
37499     setSize : function(width, height){
37500         if(!this.ignoreResize(width, height)){
37501             var grid = this.grid;
37502             var size = this.adjustForComponents(width, height);
37503             var gridel = grid.getGridEl();
37504             gridel.setSize(size.width, size.height);
37505             /*
37506             var thd = grid.getGridEl().select('thead',true).first();
37507             var tbd = grid.getGridEl().select('tbody', true).first();
37508             if (tbd) {
37509                 tbd.setSize(width, height - thd.getHeight());
37510             }
37511             */
37512             grid.autoSize();
37513         }
37514     },
37515      
37516     
37517     
37518     beforeSlide : function(){
37519         this.grid.getView().scroller.clip();
37520     },
37521     
37522     afterSlide : function(){
37523         this.grid.getView().scroller.unclip();
37524     },
37525     
37526     destroy : function(){
37527         this.grid.destroy();
37528         delete this.grid;
37529         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37530     }
37531 });
37532
37533 /**
37534  * @class Roo.bootstrap.panel.Nest
37535  * @extends Roo.bootstrap.panel.Content
37536  * @constructor
37537  * Create a new Panel, that can contain a layout.Border.
37538  * 
37539  * 
37540  * @param {Roo.BorderLayout} layout The layout for this panel
37541  * @param {String/Object} config A string to set only the title or a config object
37542  */
37543 Roo.bootstrap.panel.Nest = function(config)
37544 {
37545     // construct with only one argument..
37546     /* FIXME - implement nicer consturctors
37547     if (layout.layout) {
37548         config = layout;
37549         layout = config.layout;
37550         delete config.layout;
37551     }
37552     if (layout.xtype && !layout.getEl) {
37553         // then layout needs constructing..
37554         layout = Roo.factory(layout, Roo);
37555     }
37556     */
37557     
37558     config.el =  config.layout.getEl();
37559     
37560     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37561     
37562     config.layout.monitorWindowResize = false; // turn off autosizing
37563     this.layout = config.layout;
37564     this.layout.getEl().addClass("roo-layout-nested-layout");
37565     
37566     
37567     
37568     
37569 };
37570
37571 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37572
37573     setSize : function(width, height){
37574         if(!this.ignoreResize(width, height)){
37575             var size = this.adjustForComponents(width, height);
37576             var el = this.layout.getEl();
37577             if (size.height < 1) {
37578                 el.setWidth(size.width);   
37579             } else {
37580                 el.setSize(size.width, size.height);
37581             }
37582             var touch = el.dom.offsetWidth;
37583             this.layout.layout();
37584             // ie requires a double layout on the first pass
37585             if(Roo.isIE && !this.initialized){
37586                 this.initialized = true;
37587                 this.layout.layout();
37588             }
37589         }
37590     },
37591     
37592     // activate all subpanels if not currently active..
37593     
37594     setActiveState : function(active){
37595         this.active = active;
37596         this.setActiveClass(active);
37597         
37598         if(!active){
37599             this.fireEvent("deactivate", this);
37600             return;
37601         }
37602         
37603         this.fireEvent("activate", this);
37604         // not sure if this should happen before or after..
37605         if (!this.layout) {
37606             return; // should not happen..
37607         }
37608         var reg = false;
37609         for (var r in this.layout.regions) {
37610             reg = this.layout.getRegion(r);
37611             if (reg.getActivePanel()) {
37612                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37613                 reg.setActivePanel(reg.getActivePanel());
37614                 continue;
37615             }
37616             if (!reg.panels.length) {
37617                 continue;
37618             }
37619             reg.showPanel(reg.getPanel(0));
37620         }
37621         
37622         
37623         
37624         
37625     },
37626     
37627     /**
37628      * Returns the nested BorderLayout for this panel
37629      * @return {Roo.BorderLayout} 
37630      */
37631     getLayout : function(){
37632         return this.layout;
37633     },
37634     
37635      /**
37636      * Adds a xtype elements to the layout of the nested panel
37637      * <pre><code>
37638
37639 panel.addxtype({
37640        xtype : 'ContentPanel',
37641        region: 'west',
37642        items: [ .... ]
37643    }
37644 );
37645
37646 panel.addxtype({
37647         xtype : 'NestedLayoutPanel',
37648         region: 'west',
37649         layout: {
37650            center: { },
37651            west: { }   
37652         },
37653         items : [ ... list of content panels or nested layout panels.. ]
37654    }
37655 );
37656 </code></pre>
37657      * @param {Object} cfg Xtype definition of item to add.
37658      */
37659     addxtype : function(cfg) {
37660         return this.layout.addxtype(cfg);
37661     
37662     }
37663 });        /*
37664  * Based on:
37665  * Ext JS Library 1.1.1
37666  * Copyright(c) 2006-2007, Ext JS, LLC.
37667  *
37668  * Originally Released Under LGPL - original licence link has changed is not relivant.
37669  *
37670  * Fork - LGPL
37671  * <script type="text/javascript">
37672  */
37673 /**
37674  * @class Roo.TabPanel
37675  * @extends Roo.util.Observable
37676  * A lightweight tab container.
37677  * <br><br>
37678  * Usage:
37679  * <pre><code>
37680 // basic tabs 1, built from existing content
37681 var tabs = new Roo.TabPanel("tabs1");
37682 tabs.addTab("script", "View Script");
37683 tabs.addTab("markup", "View Markup");
37684 tabs.activate("script");
37685
37686 // more advanced tabs, built from javascript
37687 var jtabs = new Roo.TabPanel("jtabs");
37688 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37689
37690 // set up the UpdateManager
37691 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37692 var updater = tab2.getUpdateManager();
37693 updater.setDefaultUrl("ajax1.htm");
37694 tab2.on('activate', updater.refresh, updater, true);
37695
37696 // Use setUrl for Ajax loading
37697 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37698 tab3.setUrl("ajax2.htm", null, true);
37699
37700 // Disabled tab
37701 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37702 tab4.disable();
37703
37704 jtabs.activate("jtabs-1");
37705  * </code></pre>
37706  * @constructor
37707  * Create a new TabPanel.
37708  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37709  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37710  */
37711 Roo.bootstrap.panel.Tabs = function(config){
37712     /**
37713     * The container element for this TabPanel.
37714     * @type Roo.Element
37715     */
37716     this.el = Roo.get(config.el);
37717     delete config.el;
37718     if(config){
37719         if(typeof config == "boolean"){
37720             this.tabPosition = config ? "bottom" : "top";
37721         }else{
37722             Roo.apply(this, config);
37723         }
37724     }
37725     
37726     if(this.tabPosition == "bottom"){
37727         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37728         this.el.addClass("roo-tabs-bottom");
37729     }
37730     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37731     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37732     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37733     if(Roo.isIE){
37734         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37735     }
37736     if(this.tabPosition != "bottom"){
37737         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37738          * @type Roo.Element
37739          */
37740         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37741         this.el.addClass("roo-tabs-top");
37742     }
37743     this.items = [];
37744
37745     this.bodyEl.setStyle("position", "relative");
37746
37747     this.active = null;
37748     this.activateDelegate = this.activate.createDelegate(this);
37749
37750     this.addEvents({
37751         /**
37752          * @event tabchange
37753          * Fires when the active tab changes
37754          * @param {Roo.TabPanel} this
37755          * @param {Roo.TabPanelItem} activePanel The new active tab
37756          */
37757         "tabchange": true,
37758         /**
37759          * @event beforetabchange
37760          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37761          * @param {Roo.TabPanel} this
37762          * @param {Object} e Set cancel to true on this object to cancel the tab change
37763          * @param {Roo.TabPanelItem} tab The tab being changed to
37764          */
37765         "beforetabchange" : true
37766     });
37767
37768     Roo.EventManager.onWindowResize(this.onResize, this);
37769     this.cpad = this.el.getPadding("lr");
37770     this.hiddenCount = 0;
37771
37772
37773     // toolbar on the tabbar support...
37774     if (this.toolbar) {
37775         alert("no toolbar support yet");
37776         this.toolbar  = false;
37777         /*
37778         var tcfg = this.toolbar;
37779         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37780         this.toolbar = new Roo.Toolbar(tcfg);
37781         if (Roo.isSafari) {
37782             var tbl = tcfg.container.child('table', true);
37783             tbl.setAttribute('width', '100%');
37784         }
37785         */
37786         
37787     }
37788    
37789
37790
37791     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37792 };
37793
37794 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37795     /*
37796      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37797      */
37798     tabPosition : "top",
37799     /*
37800      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37801      */
37802     currentTabWidth : 0,
37803     /*
37804      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37805      */
37806     minTabWidth : 40,
37807     /*
37808      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37809      */
37810     maxTabWidth : 250,
37811     /*
37812      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37813      */
37814     preferredTabWidth : 175,
37815     /*
37816      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37817      */
37818     resizeTabs : false,
37819     /*
37820      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37821      */
37822     monitorResize : true,
37823     /*
37824      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37825      */
37826     toolbar : false,
37827
37828     /**
37829      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37830      * @param {String} id The id of the div to use <b>or create</b>
37831      * @param {String} text The text for the tab
37832      * @param {String} content (optional) Content to put in the TabPanelItem body
37833      * @param {Boolean} closable (optional) True to create a close icon on the tab
37834      * @return {Roo.TabPanelItem} The created TabPanelItem
37835      */
37836     addTab : function(id, text, content, closable, tpl)
37837     {
37838         var item = new Roo.bootstrap.panel.TabItem({
37839             panel: this,
37840             id : id,
37841             text : text,
37842             closable : closable,
37843             tpl : tpl
37844         });
37845         this.addTabItem(item);
37846         if(content){
37847             item.setContent(content);
37848         }
37849         return item;
37850     },
37851
37852     /**
37853      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37854      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37855      * @return {Roo.TabPanelItem}
37856      */
37857     getTab : function(id){
37858         return this.items[id];
37859     },
37860
37861     /**
37862      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37863      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37864      */
37865     hideTab : function(id){
37866         var t = this.items[id];
37867         if(!t.isHidden()){
37868            t.setHidden(true);
37869            this.hiddenCount++;
37870            this.autoSizeTabs();
37871         }
37872     },
37873
37874     /**
37875      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37876      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37877      */
37878     unhideTab : function(id){
37879         var t = this.items[id];
37880         if(t.isHidden()){
37881            t.setHidden(false);
37882            this.hiddenCount--;
37883            this.autoSizeTabs();
37884         }
37885     },
37886
37887     /**
37888      * Adds an existing {@link Roo.TabPanelItem}.
37889      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37890      */
37891     addTabItem : function(item){
37892         this.items[item.id] = item;
37893         this.items.push(item);
37894       //  if(this.resizeTabs){
37895     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37896   //         this.autoSizeTabs();
37897 //        }else{
37898 //            item.autoSize();
37899        // }
37900     },
37901
37902     /**
37903      * Removes a {@link Roo.TabPanelItem}.
37904      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37905      */
37906     removeTab : function(id){
37907         var items = this.items;
37908         var tab = items[id];
37909         if(!tab) { return; }
37910         var index = items.indexOf(tab);
37911         if(this.active == tab && items.length > 1){
37912             var newTab = this.getNextAvailable(index);
37913             if(newTab) {
37914                 newTab.activate();
37915             }
37916         }
37917         this.stripEl.dom.removeChild(tab.pnode.dom);
37918         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37919             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37920         }
37921         items.splice(index, 1);
37922         delete this.items[tab.id];
37923         tab.fireEvent("close", tab);
37924         tab.purgeListeners();
37925         this.autoSizeTabs();
37926     },
37927
37928     getNextAvailable : function(start){
37929         var items = this.items;
37930         var index = start;
37931         // look for a next tab that will slide over to
37932         // replace the one being removed
37933         while(index < items.length){
37934             var item = items[++index];
37935             if(item && !item.isHidden()){
37936                 return item;
37937             }
37938         }
37939         // if one isn't found select the previous tab (on the left)
37940         index = start;
37941         while(index >= 0){
37942             var item = items[--index];
37943             if(item && !item.isHidden()){
37944                 return item;
37945             }
37946         }
37947         return null;
37948     },
37949
37950     /**
37951      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37952      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37953      */
37954     disableTab : function(id){
37955         var tab = this.items[id];
37956         if(tab && this.active != tab){
37957             tab.disable();
37958         }
37959     },
37960
37961     /**
37962      * Enables a {@link Roo.TabPanelItem} that is disabled.
37963      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37964      */
37965     enableTab : function(id){
37966         var tab = this.items[id];
37967         tab.enable();
37968     },
37969
37970     /**
37971      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37972      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37973      * @return {Roo.TabPanelItem} The TabPanelItem.
37974      */
37975     activate : function(id){
37976         var tab = this.items[id];
37977         if(!tab){
37978             return null;
37979         }
37980         if(tab == this.active || tab.disabled){
37981             return tab;
37982         }
37983         var e = {};
37984         this.fireEvent("beforetabchange", this, e, tab);
37985         if(e.cancel !== true && !tab.disabled){
37986             if(this.active){
37987                 this.active.hide();
37988             }
37989             this.active = this.items[id];
37990             this.active.show();
37991             this.fireEvent("tabchange", this, this.active);
37992         }
37993         return tab;
37994     },
37995
37996     /**
37997      * Gets the active {@link Roo.TabPanelItem}.
37998      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37999      */
38000     getActiveTab : function(){
38001         return this.active;
38002     },
38003
38004     /**
38005      * Updates the tab body element to fit the height of the container element
38006      * for overflow scrolling
38007      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38008      */
38009     syncHeight : function(targetHeight){
38010         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38011         var bm = this.bodyEl.getMargins();
38012         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38013         this.bodyEl.setHeight(newHeight);
38014         return newHeight;
38015     },
38016
38017     onResize : function(){
38018         if(this.monitorResize){
38019             this.autoSizeTabs();
38020         }
38021     },
38022
38023     /**
38024      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38025      */
38026     beginUpdate : function(){
38027         this.updating = true;
38028     },
38029
38030     /**
38031      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38032      */
38033     endUpdate : function(){
38034         this.updating = false;
38035         this.autoSizeTabs();
38036     },
38037
38038     /**
38039      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38040      */
38041     autoSizeTabs : function(){
38042         var count = this.items.length;
38043         var vcount = count - this.hiddenCount;
38044         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38045             return;
38046         }
38047         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38048         var availWidth = Math.floor(w / vcount);
38049         var b = this.stripBody;
38050         if(b.getWidth() > w){
38051             var tabs = this.items;
38052             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38053             if(availWidth < this.minTabWidth){
38054                 /*if(!this.sleft){    // incomplete scrolling code
38055                     this.createScrollButtons();
38056                 }
38057                 this.showScroll();
38058                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38059             }
38060         }else{
38061             if(this.currentTabWidth < this.preferredTabWidth){
38062                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38063             }
38064         }
38065     },
38066
38067     /**
38068      * Returns the number of tabs in this TabPanel.
38069      * @return {Number}
38070      */
38071      getCount : function(){
38072          return this.items.length;
38073      },
38074
38075     /**
38076      * Resizes all the tabs to the passed width
38077      * @param {Number} The new width
38078      */
38079     setTabWidth : function(width){
38080         this.currentTabWidth = width;
38081         for(var i = 0, len = this.items.length; i < len; i++) {
38082                 if(!this.items[i].isHidden()) {
38083                 this.items[i].setWidth(width);
38084             }
38085         }
38086     },
38087
38088     /**
38089      * Destroys this TabPanel
38090      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38091      */
38092     destroy : function(removeEl){
38093         Roo.EventManager.removeResizeListener(this.onResize, this);
38094         for(var i = 0, len = this.items.length; i < len; i++){
38095             this.items[i].purgeListeners();
38096         }
38097         if(removeEl === true){
38098             this.el.update("");
38099             this.el.remove();
38100         }
38101     },
38102     
38103     createStrip : function(container)
38104     {
38105         var strip = document.createElement("nav");
38106         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38107         container.appendChild(strip);
38108         return strip;
38109     },
38110     
38111     createStripList : function(strip)
38112     {
38113         // div wrapper for retard IE
38114         // returns the "tr" element.
38115         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38116         //'<div class="x-tabs-strip-wrap">'+
38117           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38118           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38119         return strip.firstChild; //.firstChild.firstChild.firstChild;
38120     },
38121     createBody : function(container)
38122     {
38123         var body = document.createElement("div");
38124         Roo.id(body, "tab-body");
38125         //Roo.fly(body).addClass("x-tabs-body");
38126         Roo.fly(body).addClass("tab-content");
38127         container.appendChild(body);
38128         return body;
38129     },
38130     createItemBody :function(bodyEl, id){
38131         var body = Roo.getDom(id);
38132         if(!body){
38133             body = document.createElement("div");
38134             body.id = id;
38135         }
38136         //Roo.fly(body).addClass("x-tabs-item-body");
38137         Roo.fly(body).addClass("tab-pane");
38138          bodyEl.insertBefore(body, bodyEl.firstChild);
38139         return body;
38140     },
38141     /** @private */
38142     createStripElements :  function(stripEl, text, closable, tpl)
38143     {
38144         var td = document.createElement("li"); // was td..
38145         
38146         
38147         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38148         
38149         
38150         stripEl.appendChild(td);
38151         /*if(closable){
38152             td.className = "x-tabs-closable";
38153             if(!this.closeTpl){
38154                 this.closeTpl = 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>' +
38157                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38158                 );
38159             }
38160             var el = this.closeTpl.overwrite(td, {"text": text});
38161             var close = el.getElementsByTagName("div")[0];
38162             var inner = el.getElementsByTagName("em")[0];
38163             return {"el": el, "close": close, "inner": inner};
38164         } else {
38165         */
38166         // not sure what this is..
38167 //            if(!this.tabTpl){
38168                 //this.tabTpl = new Roo.Template(
38169                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38170                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38171                 //);
38172 //                this.tabTpl = new Roo.Template(
38173 //                   '<a href="#">' +
38174 //                   '<span unselectable="on"' +
38175 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38176 //                            ' >{text}</span></a>'
38177 //                );
38178 //                
38179 //            }
38180
38181
38182             var template = tpl || this.tabTpl || false;
38183             
38184             if(!template){
38185                 
38186                 template = new Roo.Template(
38187                    '<a href="#">' +
38188                    '<span unselectable="on"' +
38189                             (this.disableTooltips ? '' : ' title="{text}"') +
38190                             ' >{text}</span></a>'
38191                 );
38192             }
38193             
38194             switch (typeof(template)) {
38195                 case 'object' :
38196                     break;
38197                 case 'string' :
38198                     template = new Roo.Template(template);
38199                     break;
38200                 default :
38201                     break;
38202             }
38203             
38204             var el = template.overwrite(td, {"text": text});
38205             
38206             var inner = el.getElementsByTagName("span")[0];
38207             
38208             return {"el": el, "inner": inner};
38209             
38210     }
38211         
38212     
38213 });
38214
38215 /**
38216  * @class Roo.TabPanelItem
38217  * @extends Roo.util.Observable
38218  * Represents an individual item (tab plus body) in a TabPanel.
38219  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38220  * @param {String} id The id of this TabPanelItem
38221  * @param {String} text The text for the tab of this TabPanelItem
38222  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38223  */
38224 Roo.bootstrap.panel.TabItem = function(config){
38225     /**
38226      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38227      * @type Roo.TabPanel
38228      */
38229     this.tabPanel = config.panel;
38230     /**
38231      * The id for this TabPanelItem
38232      * @type String
38233      */
38234     this.id = config.id;
38235     /** @private */
38236     this.disabled = false;
38237     /** @private */
38238     this.text = config.text;
38239     /** @private */
38240     this.loaded = false;
38241     this.closable = config.closable;
38242
38243     /**
38244      * The body element for this TabPanelItem.
38245      * @type Roo.Element
38246      */
38247     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38248     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38249     this.bodyEl.setStyle("display", "block");
38250     this.bodyEl.setStyle("zoom", "1");
38251     //this.hideAction();
38252
38253     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38254     /** @private */
38255     this.el = Roo.get(els.el);
38256     this.inner = Roo.get(els.inner, true);
38257     this.textEl = Roo.get(this.el.dom.firstChild, true);
38258     this.pnode = Roo.get(els.el.parentNode, true);
38259 //    this.el.on("mousedown", this.onTabMouseDown, this);
38260     this.el.on("click", this.onTabClick, this);
38261     /** @private */
38262     if(config.closable){
38263         var c = Roo.get(els.close, true);
38264         c.dom.title = this.closeText;
38265         c.addClassOnOver("close-over");
38266         c.on("click", this.closeClick, this);
38267      }
38268
38269     this.addEvents({
38270          /**
38271          * @event activate
38272          * Fires when this tab becomes the active tab.
38273          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38274          * @param {Roo.TabPanelItem} this
38275          */
38276         "activate": true,
38277         /**
38278          * @event beforeclose
38279          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38280          * @param {Roo.TabPanelItem} this
38281          * @param {Object} e Set cancel to true on this object to cancel the close.
38282          */
38283         "beforeclose": true,
38284         /**
38285          * @event close
38286          * Fires when this tab is closed.
38287          * @param {Roo.TabPanelItem} this
38288          */
38289          "close": true,
38290         /**
38291          * @event deactivate
38292          * Fires when this tab is no longer the active tab.
38293          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38294          * @param {Roo.TabPanelItem} this
38295          */
38296          "deactivate" : true
38297     });
38298     this.hidden = false;
38299
38300     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38301 };
38302
38303 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38304            {
38305     purgeListeners : function(){
38306        Roo.util.Observable.prototype.purgeListeners.call(this);
38307        this.el.removeAllListeners();
38308     },
38309     /**
38310      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38311      */
38312     show : function(){
38313         this.pnode.addClass("active");
38314         this.showAction();
38315         if(Roo.isOpera){
38316             this.tabPanel.stripWrap.repaint();
38317         }
38318         this.fireEvent("activate", this.tabPanel, this);
38319     },
38320
38321     /**
38322      * Returns true if this tab is the active tab.
38323      * @return {Boolean}
38324      */
38325     isActive : function(){
38326         return this.tabPanel.getActiveTab() == this;
38327     },
38328
38329     /**
38330      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38331      */
38332     hide : function(){
38333         this.pnode.removeClass("active");
38334         this.hideAction();
38335         this.fireEvent("deactivate", this.tabPanel, this);
38336     },
38337
38338     hideAction : function(){
38339         this.bodyEl.hide();
38340         this.bodyEl.setStyle("position", "absolute");
38341         this.bodyEl.setLeft("-20000px");
38342         this.bodyEl.setTop("-20000px");
38343     },
38344
38345     showAction : function(){
38346         this.bodyEl.setStyle("position", "relative");
38347         this.bodyEl.setTop("");
38348         this.bodyEl.setLeft("");
38349         this.bodyEl.show();
38350     },
38351
38352     /**
38353      * Set the tooltip for the tab.
38354      * @param {String} tooltip The tab's tooltip
38355      */
38356     setTooltip : function(text){
38357         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38358             this.textEl.dom.qtip = text;
38359             this.textEl.dom.removeAttribute('title');
38360         }else{
38361             this.textEl.dom.title = text;
38362         }
38363     },
38364
38365     onTabClick : function(e){
38366         e.preventDefault();
38367         this.tabPanel.activate(this.id);
38368     },
38369
38370     onTabMouseDown : function(e){
38371         e.preventDefault();
38372         this.tabPanel.activate(this.id);
38373     },
38374 /*
38375     getWidth : function(){
38376         return this.inner.getWidth();
38377     },
38378
38379     setWidth : function(width){
38380         var iwidth = width - this.pnode.getPadding("lr");
38381         this.inner.setWidth(iwidth);
38382         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38383         this.pnode.setWidth(width);
38384     },
38385 */
38386     /**
38387      * Show or hide the tab
38388      * @param {Boolean} hidden True to hide or false to show.
38389      */
38390     setHidden : function(hidden){
38391         this.hidden = hidden;
38392         this.pnode.setStyle("display", hidden ? "none" : "");
38393     },
38394
38395     /**
38396      * Returns true if this tab is "hidden"
38397      * @return {Boolean}
38398      */
38399     isHidden : function(){
38400         return this.hidden;
38401     },
38402
38403     /**
38404      * Returns the text for this tab
38405      * @return {String}
38406      */
38407     getText : function(){
38408         return this.text;
38409     },
38410     /*
38411     autoSize : function(){
38412         //this.el.beginMeasure();
38413         this.textEl.setWidth(1);
38414         /*
38415          *  #2804 [new] Tabs in Roojs
38416          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38417          */
38418         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38419         //this.el.endMeasure();
38420     //},
38421
38422     /**
38423      * Sets the text for the tab (Note: this also sets the tooltip text)
38424      * @param {String} text The tab's text and tooltip
38425      */
38426     setText : function(text){
38427         this.text = text;
38428         this.textEl.update(text);
38429         this.setTooltip(text);
38430         //if(!this.tabPanel.resizeTabs){
38431         //    this.autoSize();
38432         //}
38433     },
38434     /**
38435      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38436      */
38437     activate : function(){
38438         this.tabPanel.activate(this.id);
38439     },
38440
38441     /**
38442      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38443      */
38444     disable : function(){
38445         if(this.tabPanel.active != this){
38446             this.disabled = true;
38447             this.pnode.addClass("disabled");
38448         }
38449     },
38450
38451     /**
38452      * Enables this TabPanelItem if it was previously disabled.
38453      */
38454     enable : function(){
38455         this.disabled = false;
38456         this.pnode.removeClass("disabled");
38457     },
38458
38459     /**
38460      * Sets the content for this TabPanelItem.
38461      * @param {String} content The content
38462      * @param {Boolean} loadScripts true to look for and load scripts
38463      */
38464     setContent : function(content, loadScripts){
38465         this.bodyEl.update(content, loadScripts);
38466     },
38467
38468     /**
38469      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38470      * @return {Roo.UpdateManager} The UpdateManager
38471      */
38472     getUpdateManager : function(){
38473         return this.bodyEl.getUpdateManager();
38474     },
38475
38476     /**
38477      * Set a URL to be used to load the content for this TabPanelItem.
38478      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38479      * @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)
38480      * @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)
38481      * @return {Roo.UpdateManager} The UpdateManager
38482      */
38483     setUrl : function(url, params, loadOnce){
38484         if(this.refreshDelegate){
38485             this.un('activate', this.refreshDelegate);
38486         }
38487         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38488         this.on("activate", this.refreshDelegate);
38489         return this.bodyEl.getUpdateManager();
38490     },
38491
38492     /** @private */
38493     _handleRefresh : function(url, params, loadOnce){
38494         if(!loadOnce || !this.loaded){
38495             var updater = this.bodyEl.getUpdateManager();
38496             updater.update(url, params, this._setLoaded.createDelegate(this));
38497         }
38498     },
38499
38500     /**
38501      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38502      *   Will fail silently if the setUrl method has not been called.
38503      *   This does not activate the panel, just updates its content.
38504      */
38505     refresh : function(){
38506         if(this.refreshDelegate){
38507            this.loaded = false;
38508            this.refreshDelegate();
38509         }
38510     },
38511
38512     /** @private */
38513     _setLoaded : function(){
38514         this.loaded = true;
38515     },
38516
38517     /** @private */
38518     closeClick : function(e){
38519         var o = {};
38520         e.stopEvent();
38521         this.fireEvent("beforeclose", this, o);
38522         if(o.cancel !== true){
38523             this.tabPanel.removeTab(this.id);
38524         }
38525     },
38526     /**
38527      * The text displayed in the tooltip for the close icon.
38528      * @type String
38529      */
38530     closeText : "Close this tab"
38531 });
38532 /**
38533 *    This script refer to:
38534 *    Title: International Telephone Input
38535 *    Author: Jack O'Connor
38536 *    Code version:  v12.1.12
38537 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38538 **/
38539
38540 Roo.bootstrap.PhoneInputData = function() {
38541     var d = [
38542       [
38543         "Afghanistan (‫افغانستان‬‎)",
38544         "af",
38545         "93"
38546       ],
38547       [
38548         "Albania (Shqipëri)",
38549         "al",
38550         "355"
38551       ],
38552       [
38553         "Algeria (‫الجزائر‬‎)",
38554         "dz",
38555         "213"
38556       ],
38557       [
38558         "American Samoa",
38559         "as",
38560         "1684"
38561       ],
38562       [
38563         "Andorra",
38564         "ad",
38565         "376"
38566       ],
38567       [
38568         "Angola",
38569         "ao",
38570         "244"
38571       ],
38572       [
38573         "Anguilla",
38574         "ai",
38575         "1264"
38576       ],
38577       [
38578         "Antigua and Barbuda",
38579         "ag",
38580         "1268"
38581       ],
38582       [
38583         "Argentina",
38584         "ar",
38585         "54"
38586       ],
38587       [
38588         "Armenia (Հայաստան)",
38589         "am",
38590         "374"
38591       ],
38592       [
38593         "Aruba",
38594         "aw",
38595         "297"
38596       ],
38597       [
38598         "Australia",
38599         "au",
38600         "61",
38601         0
38602       ],
38603       [
38604         "Austria (Österreich)",
38605         "at",
38606         "43"
38607       ],
38608       [
38609         "Azerbaijan (Azərbaycan)",
38610         "az",
38611         "994"
38612       ],
38613       [
38614         "Bahamas",
38615         "bs",
38616         "1242"
38617       ],
38618       [
38619         "Bahrain (‫البحرين‬‎)",
38620         "bh",
38621         "973"
38622       ],
38623       [
38624         "Bangladesh (বাংলাদেশ)",
38625         "bd",
38626         "880"
38627       ],
38628       [
38629         "Barbados",
38630         "bb",
38631         "1246"
38632       ],
38633       [
38634         "Belarus (Беларусь)",
38635         "by",
38636         "375"
38637       ],
38638       [
38639         "Belgium (België)",
38640         "be",
38641         "32"
38642       ],
38643       [
38644         "Belize",
38645         "bz",
38646         "501"
38647       ],
38648       [
38649         "Benin (Bénin)",
38650         "bj",
38651         "229"
38652       ],
38653       [
38654         "Bermuda",
38655         "bm",
38656         "1441"
38657       ],
38658       [
38659         "Bhutan (འབྲུག)",
38660         "bt",
38661         "975"
38662       ],
38663       [
38664         "Bolivia",
38665         "bo",
38666         "591"
38667       ],
38668       [
38669         "Bosnia and Herzegovina (Босна и Херцеговина)",
38670         "ba",
38671         "387"
38672       ],
38673       [
38674         "Botswana",
38675         "bw",
38676         "267"
38677       ],
38678       [
38679         "Brazil (Brasil)",
38680         "br",
38681         "55"
38682       ],
38683       [
38684         "British Indian Ocean Territory",
38685         "io",
38686         "246"
38687       ],
38688       [
38689         "British Virgin Islands",
38690         "vg",
38691         "1284"
38692       ],
38693       [
38694         "Brunei",
38695         "bn",
38696         "673"
38697       ],
38698       [
38699         "Bulgaria (България)",
38700         "bg",
38701         "359"
38702       ],
38703       [
38704         "Burkina Faso",
38705         "bf",
38706         "226"
38707       ],
38708       [
38709         "Burundi (Uburundi)",
38710         "bi",
38711         "257"
38712       ],
38713       [
38714         "Cambodia (កម្ពុជា)",
38715         "kh",
38716         "855"
38717       ],
38718       [
38719         "Cameroon (Cameroun)",
38720         "cm",
38721         "237"
38722       ],
38723       [
38724         "Canada",
38725         "ca",
38726         "1",
38727         1,
38728         ["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"]
38729       ],
38730       [
38731         "Cape Verde (Kabu Verdi)",
38732         "cv",
38733         "238"
38734       ],
38735       [
38736         "Caribbean Netherlands",
38737         "bq",
38738         "599",
38739         1
38740       ],
38741       [
38742         "Cayman Islands",
38743         "ky",
38744         "1345"
38745       ],
38746       [
38747         "Central African Republic (République centrafricaine)",
38748         "cf",
38749         "236"
38750       ],
38751       [
38752         "Chad (Tchad)",
38753         "td",
38754         "235"
38755       ],
38756       [
38757         "Chile",
38758         "cl",
38759         "56"
38760       ],
38761       [
38762         "China (中国)",
38763         "cn",
38764         "86"
38765       ],
38766       [
38767         "Christmas Island",
38768         "cx",
38769         "61",
38770         2
38771       ],
38772       [
38773         "Cocos (Keeling) Islands",
38774         "cc",
38775         "61",
38776         1
38777       ],
38778       [
38779         "Colombia",
38780         "co",
38781         "57"
38782       ],
38783       [
38784         "Comoros (‫جزر القمر‬‎)",
38785         "km",
38786         "269"
38787       ],
38788       [
38789         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38790         "cd",
38791         "243"
38792       ],
38793       [
38794         "Congo (Republic) (Congo-Brazzaville)",
38795         "cg",
38796         "242"
38797       ],
38798       [
38799         "Cook Islands",
38800         "ck",
38801         "682"
38802       ],
38803       [
38804         "Costa Rica",
38805         "cr",
38806         "506"
38807       ],
38808       [
38809         "Côte d’Ivoire",
38810         "ci",
38811         "225"
38812       ],
38813       [
38814         "Croatia (Hrvatska)",
38815         "hr",
38816         "385"
38817       ],
38818       [
38819         "Cuba",
38820         "cu",
38821         "53"
38822       ],
38823       [
38824         "Curaçao",
38825         "cw",
38826         "599",
38827         0
38828       ],
38829       [
38830         "Cyprus (Κύπρος)",
38831         "cy",
38832         "357"
38833       ],
38834       [
38835         "Czech Republic (Česká republika)",
38836         "cz",
38837         "420"
38838       ],
38839       [
38840         "Denmark (Danmark)",
38841         "dk",
38842         "45"
38843       ],
38844       [
38845         "Djibouti",
38846         "dj",
38847         "253"
38848       ],
38849       [
38850         "Dominica",
38851         "dm",
38852         "1767"
38853       ],
38854       [
38855         "Dominican Republic (República Dominicana)",
38856         "do",
38857         "1",
38858         2,
38859         ["809", "829", "849"]
38860       ],
38861       [
38862         "Ecuador",
38863         "ec",
38864         "593"
38865       ],
38866       [
38867         "Egypt (‫مصر‬‎)",
38868         "eg",
38869         "20"
38870       ],
38871       [
38872         "El Salvador",
38873         "sv",
38874         "503"
38875       ],
38876       [
38877         "Equatorial Guinea (Guinea Ecuatorial)",
38878         "gq",
38879         "240"
38880       ],
38881       [
38882         "Eritrea",
38883         "er",
38884         "291"
38885       ],
38886       [
38887         "Estonia (Eesti)",
38888         "ee",
38889         "372"
38890       ],
38891       [
38892         "Ethiopia",
38893         "et",
38894         "251"
38895       ],
38896       [
38897         "Falkland Islands (Islas Malvinas)",
38898         "fk",
38899         "500"
38900       ],
38901       [
38902         "Faroe Islands (Føroyar)",
38903         "fo",
38904         "298"
38905       ],
38906       [
38907         "Fiji",
38908         "fj",
38909         "679"
38910       ],
38911       [
38912         "Finland (Suomi)",
38913         "fi",
38914         "358",
38915         0
38916       ],
38917       [
38918         "France",
38919         "fr",
38920         "33"
38921       ],
38922       [
38923         "French Guiana (Guyane française)",
38924         "gf",
38925         "594"
38926       ],
38927       [
38928         "French Polynesia (Polynésie française)",
38929         "pf",
38930         "689"
38931       ],
38932       [
38933         "Gabon",
38934         "ga",
38935         "241"
38936       ],
38937       [
38938         "Gambia",
38939         "gm",
38940         "220"
38941       ],
38942       [
38943         "Georgia (საქართველო)",
38944         "ge",
38945         "995"
38946       ],
38947       [
38948         "Germany (Deutschland)",
38949         "de",
38950         "49"
38951       ],
38952       [
38953         "Ghana (Gaana)",
38954         "gh",
38955         "233"
38956       ],
38957       [
38958         "Gibraltar",
38959         "gi",
38960         "350"
38961       ],
38962       [
38963         "Greece (Ελλάδα)",
38964         "gr",
38965         "30"
38966       ],
38967       [
38968         "Greenland (Kalaallit Nunaat)",
38969         "gl",
38970         "299"
38971       ],
38972       [
38973         "Grenada",
38974         "gd",
38975         "1473"
38976       ],
38977       [
38978         "Guadeloupe",
38979         "gp",
38980         "590",
38981         0
38982       ],
38983       [
38984         "Guam",
38985         "gu",
38986         "1671"
38987       ],
38988       [
38989         "Guatemala",
38990         "gt",
38991         "502"
38992       ],
38993       [
38994         "Guernsey",
38995         "gg",
38996         "44",
38997         1
38998       ],
38999       [
39000         "Guinea (Guinée)",
39001         "gn",
39002         "224"
39003       ],
39004       [
39005         "Guinea-Bissau (Guiné Bissau)",
39006         "gw",
39007         "245"
39008       ],
39009       [
39010         "Guyana",
39011         "gy",
39012         "592"
39013       ],
39014       [
39015         "Haiti",
39016         "ht",
39017         "509"
39018       ],
39019       [
39020         "Honduras",
39021         "hn",
39022         "504"
39023       ],
39024       [
39025         "Hong Kong (香港)",
39026         "hk",
39027         "852"
39028       ],
39029       [
39030         "Hungary (Magyarország)",
39031         "hu",
39032         "36"
39033       ],
39034       [
39035         "Iceland (Ísland)",
39036         "is",
39037         "354"
39038       ],
39039       [
39040         "India (भारत)",
39041         "in",
39042         "91"
39043       ],
39044       [
39045         "Indonesia",
39046         "id",
39047         "62"
39048       ],
39049       [
39050         "Iran (‫ایران‬‎)",
39051         "ir",
39052         "98"
39053       ],
39054       [
39055         "Iraq (‫العراق‬‎)",
39056         "iq",
39057         "964"
39058       ],
39059       [
39060         "Ireland",
39061         "ie",
39062         "353"
39063       ],
39064       [
39065         "Isle of Man",
39066         "im",
39067         "44",
39068         2
39069       ],
39070       [
39071         "Israel (‫ישראל‬‎)",
39072         "il",
39073         "972"
39074       ],
39075       [
39076         "Italy (Italia)",
39077         "it",
39078         "39",
39079         0
39080       ],
39081       [
39082         "Jamaica",
39083         "jm",
39084         "1876"
39085       ],
39086       [
39087         "Japan (日本)",
39088         "jp",
39089         "81"
39090       ],
39091       [
39092         "Jersey",
39093         "je",
39094         "44",
39095         3
39096       ],
39097       [
39098         "Jordan (‫الأردن‬‎)",
39099         "jo",
39100         "962"
39101       ],
39102       [
39103         "Kazakhstan (Казахстан)",
39104         "kz",
39105         "7",
39106         1
39107       ],
39108       [
39109         "Kenya",
39110         "ke",
39111         "254"
39112       ],
39113       [
39114         "Kiribati",
39115         "ki",
39116         "686"
39117       ],
39118       [
39119         "Kosovo",
39120         "xk",
39121         "383"
39122       ],
39123       [
39124         "Kuwait (‫الكويت‬‎)",
39125         "kw",
39126         "965"
39127       ],
39128       [
39129         "Kyrgyzstan (Кыргызстан)",
39130         "kg",
39131         "996"
39132       ],
39133       [
39134         "Laos (ລາວ)",
39135         "la",
39136         "856"
39137       ],
39138       [
39139         "Latvia (Latvija)",
39140         "lv",
39141         "371"
39142       ],
39143       [
39144         "Lebanon (‫لبنان‬‎)",
39145         "lb",
39146         "961"
39147       ],
39148       [
39149         "Lesotho",
39150         "ls",
39151         "266"
39152       ],
39153       [
39154         "Liberia",
39155         "lr",
39156         "231"
39157       ],
39158       [
39159         "Libya (‫ليبيا‬‎)",
39160         "ly",
39161         "218"
39162       ],
39163       [
39164         "Liechtenstein",
39165         "li",
39166         "423"
39167       ],
39168       [
39169         "Lithuania (Lietuva)",
39170         "lt",
39171         "370"
39172       ],
39173       [
39174         "Luxembourg",
39175         "lu",
39176         "352"
39177       ],
39178       [
39179         "Macau (澳門)",
39180         "mo",
39181         "853"
39182       ],
39183       [
39184         "Macedonia (FYROM) (Македонија)",
39185         "mk",
39186         "389"
39187       ],
39188       [
39189         "Madagascar (Madagasikara)",
39190         "mg",
39191         "261"
39192       ],
39193       [
39194         "Malawi",
39195         "mw",
39196         "265"
39197       ],
39198       [
39199         "Malaysia",
39200         "my",
39201         "60"
39202       ],
39203       [
39204         "Maldives",
39205         "mv",
39206         "960"
39207       ],
39208       [
39209         "Mali",
39210         "ml",
39211         "223"
39212       ],
39213       [
39214         "Malta",
39215         "mt",
39216         "356"
39217       ],
39218       [
39219         "Marshall Islands",
39220         "mh",
39221         "692"
39222       ],
39223       [
39224         "Martinique",
39225         "mq",
39226         "596"
39227       ],
39228       [
39229         "Mauritania (‫موريتانيا‬‎)",
39230         "mr",
39231         "222"
39232       ],
39233       [
39234         "Mauritius (Moris)",
39235         "mu",
39236         "230"
39237       ],
39238       [
39239         "Mayotte",
39240         "yt",
39241         "262",
39242         1
39243       ],
39244       [
39245         "Mexico (México)",
39246         "mx",
39247         "52"
39248       ],
39249       [
39250         "Micronesia",
39251         "fm",
39252         "691"
39253       ],
39254       [
39255         "Moldova (Republica Moldova)",
39256         "md",
39257         "373"
39258       ],
39259       [
39260         "Monaco",
39261         "mc",
39262         "377"
39263       ],
39264       [
39265         "Mongolia (Монгол)",
39266         "mn",
39267         "976"
39268       ],
39269       [
39270         "Montenegro (Crna Gora)",
39271         "me",
39272         "382"
39273       ],
39274       [
39275         "Montserrat",
39276         "ms",
39277         "1664"
39278       ],
39279       [
39280         "Morocco (‫المغرب‬‎)",
39281         "ma",
39282         "212",
39283         0
39284       ],
39285       [
39286         "Mozambique (Moçambique)",
39287         "mz",
39288         "258"
39289       ],
39290       [
39291         "Myanmar (Burma) (မြန်မာ)",
39292         "mm",
39293         "95"
39294       ],
39295       [
39296         "Namibia (Namibië)",
39297         "na",
39298         "264"
39299       ],
39300       [
39301         "Nauru",
39302         "nr",
39303         "674"
39304       ],
39305       [
39306         "Nepal (नेपाल)",
39307         "np",
39308         "977"
39309       ],
39310       [
39311         "Netherlands (Nederland)",
39312         "nl",
39313         "31"
39314       ],
39315       [
39316         "New Caledonia (Nouvelle-Calédonie)",
39317         "nc",
39318         "687"
39319       ],
39320       [
39321         "New Zealand",
39322         "nz",
39323         "64"
39324       ],
39325       [
39326         "Nicaragua",
39327         "ni",
39328         "505"
39329       ],
39330       [
39331         "Niger (Nijar)",
39332         "ne",
39333         "227"
39334       ],
39335       [
39336         "Nigeria",
39337         "ng",
39338         "234"
39339       ],
39340       [
39341         "Niue",
39342         "nu",
39343         "683"
39344       ],
39345       [
39346         "Norfolk Island",
39347         "nf",
39348         "672"
39349       ],
39350       [
39351         "North Korea (조선 민주주의 인민 공화국)",
39352         "kp",
39353         "850"
39354       ],
39355       [
39356         "Northern Mariana Islands",
39357         "mp",
39358         "1670"
39359       ],
39360       [
39361         "Norway (Norge)",
39362         "no",
39363         "47",
39364         0
39365       ],
39366       [
39367         "Oman (‫عُمان‬‎)",
39368         "om",
39369         "968"
39370       ],
39371       [
39372         "Pakistan (‫پاکستان‬‎)",
39373         "pk",
39374         "92"
39375       ],
39376       [
39377         "Palau",
39378         "pw",
39379         "680"
39380       ],
39381       [
39382         "Palestine (‫فلسطين‬‎)",
39383         "ps",
39384         "970"
39385       ],
39386       [
39387         "Panama (Panamá)",
39388         "pa",
39389         "507"
39390       ],
39391       [
39392         "Papua New Guinea",
39393         "pg",
39394         "675"
39395       ],
39396       [
39397         "Paraguay",
39398         "py",
39399         "595"
39400       ],
39401       [
39402         "Peru (Perú)",
39403         "pe",
39404         "51"
39405       ],
39406       [
39407         "Philippines",
39408         "ph",
39409         "63"
39410       ],
39411       [
39412         "Poland (Polska)",
39413         "pl",
39414         "48"
39415       ],
39416       [
39417         "Portugal",
39418         "pt",
39419         "351"
39420       ],
39421       [
39422         "Puerto Rico",
39423         "pr",
39424         "1",
39425         3,
39426         ["787", "939"]
39427       ],
39428       [
39429         "Qatar (‫قطر‬‎)",
39430         "qa",
39431         "974"
39432       ],
39433       [
39434         "Réunion (La Réunion)",
39435         "re",
39436         "262",
39437         0
39438       ],
39439       [
39440         "Romania (România)",
39441         "ro",
39442         "40"
39443       ],
39444       [
39445         "Russia (Россия)",
39446         "ru",
39447         "7",
39448         0
39449       ],
39450       [
39451         "Rwanda",
39452         "rw",
39453         "250"
39454       ],
39455       [
39456         "Saint Barthélemy",
39457         "bl",
39458         "590",
39459         1
39460       ],
39461       [
39462         "Saint Helena",
39463         "sh",
39464         "290"
39465       ],
39466       [
39467         "Saint Kitts and Nevis",
39468         "kn",
39469         "1869"
39470       ],
39471       [
39472         "Saint Lucia",
39473         "lc",
39474         "1758"
39475       ],
39476       [
39477         "Saint Martin (Saint-Martin (partie française))",
39478         "mf",
39479         "590",
39480         2
39481       ],
39482       [
39483         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39484         "pm",
39485         "508"
39486       ],
39487       [
39488         "Saint Vincent and the Grenadines",
39489         "vc",
39490         "1784"
39491       ],
39492       [
39493         "Samoa",
39494         "ws",
39495         "685"
39496       ],
39497       [
39498         "San Marino",
39499         "sm",
39500         "378"
39501       ],
39502       [
39503         "São Tomé and Príncipe (São Tomé e Príncipe)",
39504         "st",
39505         "239"
39506       ],
39507       [
39508         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39509         "sa",
39510         "966"
39511       ],
39512       [
39513         "Senegal (Sénégal)",
39514         "sn",
39515         "221"
39516       ],
39517       [
39518         "Serbia (Србија)",
39519         "rs",
39520         "381"
39521       ],
39522       [
39523         "Seychelles",
39524         "sc",
39525         "248"
39526       ],
39527       [
39528         "Sierra Leone",
39529         "sl",
39530         "232"
39531       ],
39532       [
39533         "Singapore",
39534         "sg",
39535         "65"
39536       ],
39537       [
39538         "Sint Maarten",
39539         "sx",
39540         "1721"
39541       ],
39542       [
39543         "Slovakia (Slovensko)",
39544         "sk",
39545         "421"
39546       ],
39547       [
39548         "Slovenia (Slovenija)",
39549         "si",
39550         "386"
39551       ],
39552       [
39553         "Solomon Islands",
39554         "sb",
39555         "677"
39556       ],
39557       [
39558         "Somalia (Soomaaliya)",
39559         "so",
39560         "252"
39561       ],
39562       [
39563         "South Africa",
39564         "za",
39565         "27"
39566       ],
39567       [
39568         "South Korea (대한민국)",
39569         "kr",
39570         "82"
39571       ],
39572       [
39573         "South Sudan (‫جنوب السودان‬‎)",
39574         "ss",
39575         "211"
39576       ],
39577       [
39578         "Spain (España)",
39579         "es",
39580         "34"
39581       ],
39582       [
39583         "Sri Lanka (ශ්‍රී ලංකාව)",
39584         "lk",
39585         "94"
39586       ],
39587       [
39588         "Sudan (‫السودان‬‎)",
39589         "sd",
39590         "249"
39591       ],
39592       [
39593         "Suriname",
39594         "sr",
39595         "597"
39596       ],
39597       [
39598         "Svalbard and Jan Mayen",
39599         "sj",
39600         "47",
39601         1
39602       ],
39603       [
39604         "Swaziland",
39605         "sz",
39606         "268"
39607       ],
39608       [
39609         "Sweden (Sverige)",
39610         "se",
39611         "46"
39612       ],
39613       [
39614         "Switzerland (Schweiz)",
39615         "ch",
39616         "41"
39617       ],
39618       [
39619         "Syria (‫سوريا‬‎)",
39620         "sy",
39621         "963"
39622       ],
39623       [
39624         "Taiwan (台灣)",
39625         "tw",
39626         "886"
39627       ],
39628       [
39629         "Tajikistan",
39630         "tj",
39631         "992"
39632       ],
39633       [
39634         "Tanzania",
39635         "tz",
39636         "255"
39637       ],
39638       [
39639         "Thailand (ไทย)",
39640         "th",
39641         "66"
39642       ],
39643       [
39644         "Timor-Leste",
39645         "tl",
39646         "670"
39647       ],
39648       [
39649         "Togo",
39650         "tg",
39651         "228"
39652       ],
39653       [
39654         "Tokelau",
39655         "tk",
39656         "690"
39657       ],
39658       [
39659         "Tonga",
39660         "to",
39661         "676"
39662       ],
39663       [
39664         "Trinidad and Tobago",
39665         "tt",
39666         "1868"
39667       ],
39668       [
39669         "Tunisia (‫تونس‬‎)",
39670         "tn",
39671         "216"
39672       ],
39673       [
39674         "Turkey (Türkiye)",
39675         "tr",
39676         "90"
39677       ],
39678       [
39679         "Turkmenistan",
39680         "tm",
39681         "993"
39682       ],
39683       [
39684         "Turks and Caicos Islands",
39685         "tc",
39686         "1649"
39687       ],
39688       [
39689         "Tuvalu",
39690         "tv",
39691         "688"
39692       ],
39693       [
39694         "U.S. Virgin Islands",
39695         "vi",
39696         "1340"
39697       ],
39698       [
39699         "Uganda",
39700         "ug",
39701         "256"
39702       ],
39703       [
39704         "Ukraine (Україна)",
39705         "ua",
39706         "380"
39707       ],
39708       [
39709         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39710         "ae",
39711         "971"
39712       ],
39713       [
39714         "United Kingdom",
39715         "gb",
39716         "44",
39717         0
39718       ],
39719       [
39720         "United States",
39721         "us",
39722         "1",
39723         0
39724       ],
39725       [
39726         "Uruguay",
39727         "uy",
39728         "598"
39729       ],
39730       [
39731         "Uzbekistan (Oʻzbekiston)",
39732         "uz",
39733         "998"
39734       ],
39735       [
39736         "Vanuatu",
39737         "vu",
39738         "678"
39739       ],
39740       [
39741         "Vatican City (Città del Vaticano)",
39742         "va",
39743         "39",
39744         1
39745       ],
39746       [
39747         "Venezuela",
39748         "ve",
39749         "58"
39750       ],
39751       [
39752         "Vietnam (Việt Nam)",
39753         "vn",
39754         "84"
39755       ],
39756       [
39757         "Wallis and Futuna (Wallis-et-Futuna)",
39758         "wf",
39759         "681"
39760       ],
39761       [
39762         "Western Sahara (‫الصحراء الغربية‬‎)",
39763         "eh",
39764         "212",
39765         1
39766       ],
39767       [
39768         "Yemen (‫اليمن‬‎)",
39769         "ye",
39770         "967"
39771       ],
39772       [
39773         "Zambia",
39774         "zm",
39775         "260"
39776       ],
39777       [
39778         "Zimbabwe",
39779         "zw",
39780         "263"
39781       ],
39782       [
39783         "Åland Islands",
39784         "ax",
39785         "358",
39786         1
39787       ]
39788   ];
39789   
39790   return d;
39791 }/**
39792 *    This script refer to:
39793 *    Title: International Telephone Input
39794 *    Author: Jack O'Connor
39795 *    Code version:  v12.1.12
39796 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39797 **/
39798
39799 /**
39800  * @class Roo.bootstrap.PhoneInput
39801  * @extends Roo.bootstrap.TriggerField
39802  * An input with International dial-code selection
39803  
39804  * @cfg {String} defaultDialCode default '+852'
39805  * @cfg {Array} preferedCountries default []
39806   
39807  * @constructor
39808  * Create a new PhoneInput.
39809  * @param {Object} config Configuration options
39810  */
39811
39812 Roo.bootstrap.PhoneInput = function(config) {
39813     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39814 };
39815
39816 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39817         
39818         listWidth: undefined,
39819         
39820         selectedClass: 'active',
39821         
39822         invalidClass : "has-warning",
39823         
39824         validClass: 'has-success',
39825         
39826         allowed: '0123456789',
39827         
39828         /**
39829          * @cfg {String} defaultDialCode The default dial code when initializing the input
39830          */
39831         defaultDialCode: '+852',
39832         
39833         /**
39834          * @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
39835          */
39836         preferedCountries: false,
39837         
39838         getAutoCreate : function()
39839         {
39840             var data = Roo.bootstrap.PhoneInputData();
39841             var align = this.labelAlign || this.parentLabelAlign();
39842             var id = Roo.id();
39843             
39844             this.allCountries = [];
39845             this.dialCodeMapping = [];
39846             
39847             for (var i = 0; i < data.length; i++) {
39848               var c = data[i];
39849               this.allCountries[i] = {
39850                 name: c[0],
39851                 iso2: c[1],
39852                 dialCode: c[2],
39853                 priority: c[3] || 0,
39854                 areaCodes: c[4] || null
39855               };
39856               this.dialCodeMapping[c[2]] = {
39857                   name: c[0],
39858                   iso2: c[1],
39859                   priority: c[3] || 0,
39860                   areaCodes: c[4] || null
39861               };
39862             }
39863             
39864             var cfg = {
39865                 cls: 'form-group',
39866                 cn: []
39867             };
39868             
39869             var input =  {
39870                 tag: 'input',
39871                 id : id,
39872                 cls : 'form-control tel-input',
39873                 autocomplete: 'new-password'
39874             };
39875             
39876             var hiddenInput = {
39877                 tag: 'input',
39878                 type: 'hidden',
39879                 cls: 'hidden-tel-input'
39880             };
39881             
39882             if (this.name) {
39883                 hiddenInput.name = this.name;
39884             }
39885             
39886             if (this.disabled) {
39887                 input.disabled = true;
39888             }
39889             
39890             var flag_container = {
39891                 tag: 'div',
39892                 cls: 'flag-box',
39893                 cn: [
39894                     {
39895                         tag: 'div',
39896                         cls: 'flag'
39897                     },
39898                     {
39899                         tag: 'div',
39900                         cls: 'caret'
39901                     }
39902                 ]
39903             };
39904             
39905             var box = {
39906                 tag: 'div',
39907                 cls: this.hasFeedback ? 'has-feedback' : '',
39908                 cn: [
39909                     hiddenInput,
39910                     input,
39911                     {
39912                         tag: 'input',
39913                         cls: 'dial-code-holder',
39914                         disabled: true
39915                     }
39916                 ]
39917             };
39918             
39919             var container = {
39920                 cls: 'roo-select2-container input-group',
39921                 cn: [
39922                     flag_container,
39923                     box
39924                 ]
39925             };
39926             
39927             if (this.fieldLabel.length) {
39928                 var indicator = {
39929                     tag: 'i',
39930                     tooltip: 'This field is required'
39931                 };
39932                 
39933                 var label = {
39934                     tag: 'label',
39935                     'for':  id,
39936                     cls: 'control-label',
39937                     cn: []
39938                 };
39939                 
39940                 var label_text = {
39941                     tag: 'span',
39942                     html: this.fieldLabel
39943                 };
39944                 
39945                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39946                 label.cn = [
39947                     indicator,
39948                     label_text
39949                 ];
39950                 
39951                 if(this.indicatorpos == 'right') {
39952                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39953                     label.cn = [
39954                         label_text,
39955                         indicator
39956                     ];
39957                 }
39958                 
39959                 if(align == 'left') {
39960                     container = {
39961                         tag: 'div',
39962                         cn: [
39963                             container
39964                         ]
39965                     };
39966                     
39967                     if(this.labelWidth > 12){
39968                         label.style = "width: " + this.labelWidth + 'px';
39969                     }
39970                     if(this.labelWidth < 13 && this.labelmd == 0){
39971                         this.labelmd = this.labelWidth;
39972                     }
39973                     if(this.labellg > 0){
39974                         label.cls += ' col-lg-' + this.labellg;
39975                         input.cls += ' col-lg-' + (12 - this.labellg);
39976                     }
39977                     if(this.labelmd > 0){
39978                         label.cls += ' col-md-' + this.labelmd;
39979                         container.cls += ' col-md-' + (12 - this.labelmd);
39980                     }
39981                     if(this.labelsm > 0){
39982                         label.cls += ' col-sm-' + this.labelsm;
39983                         container.cls += ' col-sm-' + (12 - this.labelsm);
39984                     }
39985                     if(this.labelxs > 0){
39986                         label.cls += ' col-xs-' + this.labelxs;
39987                         container.cls += ' col-xs-' + (12 - this.labelxs);
39988                     }
39989                 }
39990             }
39991             
39992             cfg.cn = [
39993                 label,
39994                 container
39995             ];
39996             
39997             var settings = this;
39998             
39999             ['xs','sm','md','lg'].map(function(size){
40000                 if (settings[size]) {
40001                     cfg.cls += ' col-' + size + '-' + settings[size];
40002                 }
40003             });
40004             
40005             this.store = new Roo.data.Store({
40006                 proxy : new Roo.data.MemoryProxy({}),
40007                 reader : new Roo.data.JsonReader({
40008                     fields : [
40009                         {
40010                             'name' : 'name',
40011                             'type' : 'string'
40012                         },
40013                         {
40014                             'name' : 'iso2',
40015                             'type' : 'string'
40016                         },
40017                         {
40018                             'name' : 'dialCode',
40019                             'type' : 'string'
40020                         },
40021                         {
40022                             'name' : 'priority',
40023                             'type' : 'string'
40024                         },
40025                         {
40026                             'name' : 'areaCodes',
40027                             'type' : 'string'
40028                         }
40029                     ]
40030                 })
40031             });
40032             
40033             if(!this.preferedCountries) {
40034                 this.preferedCountries = [
40035                     'hk',
40036                     'gb',
40037                     'us'
40038                 ];
40039             }
40040             
40041             var p = this.preferedCountries.reverse();
40042             
40043             if(p) {
40044                 for (var i = 0; i < p.length; i++) {
40045                     for (var j = 0; j < this.allCountries.length; j++) {
40046                         if(this.allCountries[j].iso2 == p[i]) {
40047                             var t = this.allCountries[j];
40048                             this.allCountries.splice(j,1);
40049                             this.allCountries.unshift(t);
40050                         }
40051                     } 
40052                 }
40053             }
40054             
40055             this.store.proxy.data = {
40056                 success: true,
40057                 data: this.allCountries
40058             };
40059             
40060             return cfg;
40061         },
40062         
40063         initEvents : function()
40064         {
40065             this.createList();
40066             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40067             
40068             this.indicator = this.indicatorEl();
40069             this.flag = this.flagEl();
40070             this.dialCodeHolder = this.dialCodeHolderEl();
40071             
40072             this.trigger = this.el.select('div.flag-box',true).first();
40073             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40074             
40075             var _this = this;
40076             
40077             (function(){
40078                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40079                 _this.list.setWidth(lw);
40080             }).defer(100);
40081             
40082             this.list.on('mouseover', this.onViewOver, this);
40083             this.list.on('mousemove', this.onViewMove, this);
40084             this.inputEl().on("keyup", this.onKeyUp, this);
40085             
40086             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40087
40088             this.view = new Roo.View(this.list, this.tpl, {
40089                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40090             });
40091             
40092             this.view.on('click', this.onViewClick, this);
40093             this.setValue(this.defaultDialCode);
40094         },
40095         
40096         onTriggerClick : function(e)
40097         {
40098             Roo.log('trigger click');
40099             if(this.disabled){
40100                 return;
40101             }
40102             
40103             if(this.isExpanded()){
40104                 this.collapse();
40105                 this.hasFocus = false;
40106             }else {
40107                 this.store.load({});
40108                 this.hasFocus = true;
40109                 this.expand();
40110             }
40111         },
40112         
40113         isExpanded : function()
40114         {
40115             return this.list.isVisible();
40116         },
40117         
40118         collapse : function()
40119         {
40120             if(!this.isExpanded()){
40121                 return;
40122             }
40123             this.list.hide();
40124             Roo.get(document).un('mousedown', this.collapseIf, this);
40125             Roo.get(document).un('mousewheel', this.collapseIf, this);
40126             this.fireEvent('collapse', this);
40127             this.validate();
40128         },
40129         
40130         expand : function()
40131         {
40132             Roo.log('expand');
40133
40134             if(this.isExpanded() || !this.hasFocus){
40135                 return;
40136             }
40137             
40138             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40139             this.list.setWidth(lw);
40140             
40141             this.list.show();
40142             this.restrictHeight();
40143             
40144             Roo.get(document).on('mousedown', this.collapseIf, this);
40145             Roo.get(document).on('mousewheel', this.collapseIf, this);
40146             
40147             this.fireEvent('expand', this);
40148         },
40149         
40150         restrictHeight : function()
40151         {
40152             this.list.alignTo(this.inputEl(), this.listAlign);
40153             this.list.alignTo(this.inputEl(), this.listAlign);
40154         },
40155         
40156         onViewOver : function(e, t)
40157         {
40158             if(this.inKeyMode){
40159                 return;
40160             }
40161             var item = this.view.findItemFromChild(t);
40162             
40163             if(item){
40164                 var index = this.view.indexOf(item);
40165                 this.select(index, false);
40166             }
40167         },
40168
40169         // private
40170         onViewClick : function(view, doFocus, el, e)
40171         {
40172             var index = this.view.getSelectedIndexes()[0];
40173             
40174             var r = this.store.getAt(index);
40175             
40176             if(r){
40177                 this.onSelect(r, index);
40178             }
40179             if(doFocus !== false && !this.blockFocus){
40180                 this.inputEl().focus();
40181             }
40182         },
40183         
40184         onViewMove : function(e, t)
40185         {
40186             this.inKeyMode = false;
40187         },
40188         
40189         select : function(index, scrollIntoView)
40190         {
40191             this.selectedIndex = index;
40192             this.view.select(index);
40193             if(scrollIntoView !== false){
40194                 var el = this.view.getNode(index);
40195                 if(el){
40196                     this.list.scrollChildIntoView(el, false);
40197                 }
40198             }
40199         },
40200         
40201         createList : function()
40202         {
40203             this.list = Roo.get(document.body).createChild({
40204                 tag: 'ul',
40205                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40206                 style: 'display:none'
40207             });
40208             
40209             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40210         },
40211         
40212         collapseIf : function(e)
40213         {
40214             var in_combo  = e.within(this.el);
40215             var in_list =  e.within(this.list);
40216             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40217             
40218             if (in_combo || in_list || is_list) {
40219                 return;
40220             }
40221             this.collapse();
40222         },
40223         
40224         onSelect : function(record, index)
40225         {
40226             if(this.fireEvent('beforeselect', this, record, index) !== false){
40227                 
40228                 this.setFlagClass(record.data.iso2);
40229                 this.setDialCode(record.data.dialCode);
40230                 this.hasFocus = false;
40231                 this.collapse();
40232                 this.fireEvent('select', this, record, index);
40233             }
40234         },
40235         
40236         flagEl : function()
40237         {
40238             var flag = this.el.select('div.flag',true).first();
40239             if(!flag){
40240                 return false;
40241             }
40242             return flag;
40243         },
40244         
40245         dialCodeHolderEl : function()
40246         {
40247             var d = this.el.select('input.dial-code-holder',true).first();
40248             if(!d){
40249                 return false;
40250             }
40251             return d;
40252         },
40253         
40254         setDialCode : function(v)
40255         {
40256             this.dialCodeHolder.dom.value = '+'+v;
40257         },
40258         
40259         setFlagClass : function(n)
40260         {
40261             this.flag.dom.className = 'flag '+n;
40262         },
40263         
40264         getValue : function()
40265         {
40266             var v = this.inputEl().getValue();
40267             if(this.dialCodeHolder) {
40268                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40269             }
40270             return v;
40271         },
40272         
40273         setValue : function(v)
40274         {
40275             var d = this.getDialCode(v);
40276             
40277             //invalid dial code
40278             if(v.length == 0 || !d || d.length == 0) {
40279                 if(this.rendered){
40280                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40281                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40282                 }
40283                 return;
40284             }
40285             
40286             //valid dial code
40287             this.setFlagClass(this.dialCodeMapping[d].iso2);
40288             this.setDialCode(d);
40289             this.inputEl().dom.value = v.replace('+'+d,'');
40290             this.hiddenEl().dom.value = this.getValue();
40291             
40292             this.validate();
40293         },
40294         
40295         getDialCode : function(v)
40296         {
40297             v = v ||  '';
40298             
40299             if (v.length == 0) {
40300                 return this.dialCodeHolder.dom.value;
40301             }
40302             
40303             var dialCode = "";
40304             if (v.charAt(0) != "+") {
40305                 return false;
40306             }
40307             var numericChars = "";
40308             for (var i = 1; i < v.length; i++) {
40309               var c = v.charAt(i);
40310               if (!isNaN(c)) {
40311                 numericChars += c;
40312                 if (this.dialCodeMapping[numericChars]) {
40313                   dialCode = v.substr(1, i);
40314                 }
40315                 if (numericChars.length == 4) {
40316                   break;
40317                 }
40318               }
40319             }
40320             return dialCode;
40321         },
40322         
40323         reset : function()
40324         {
40325             this.setValue(this.defaultDialCode);
40326             this.validate();
40327         },
40328         
40329         hiddenEl : function()
40330         {
40331             return this.el.select('input.hidden-tel-input',true).first();
40332         },
40333         
40334         onKeyUp : function(e){
40335             
40336             var k = e.getKey();
40337             var c = e.getCharCode();
40338             
40339             if(
40340                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40341                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40342             ){
40343                 e.stopEvent();
40344             }
40345             
40346             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40347             //     return;
40348             // }
40349             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40350                 e.stopEvent();
40351             }
40352             
40353             this.setValue(this.getValue());
40354         }
40355         
40356 });
40357 /**
40358  * @class Roo.bootstrap.MoneyField
40359  * @extends Roo.bootstrap.ComboBox
40360  * Bootstrap MoneyField class
40361  * 
40362  * @constructor
40363  * Create a new MoneyField.
40364  * @param {Object} config Configuration options
40365  */
40366
40367 Roo.bootstrap.MoneyField = function(config) {
40368     
40369     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40370     
40371 };
40372
40373 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40374     
40375     /**
40376      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40377      */
40378     allowDecimals : true,
40379     /**
40380      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40381      */
40382     decimalSeparator : ".",
40383     /**
40384      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40385      */
40386     decimalPrecision : 0,
40387     /**
40388      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40389      */
40390     allowNegative : true,
40391     /**
40392      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40393      */
40394     allowZero: true,
40395     /**
40396      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40397      */
40398     minValue : Number.NEGATIVE_INFINITY,
40399     /**
40400      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40401      */
40402     maxValue : Number.MAX_VALUE,
40403     /**
40404      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40405      */
40406     minText : "The minimum value for this field is {0}",
40407     /**
40408      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40409      */
40410     maxText : "The maximum value for this field is {0}",
40411     /**
40412      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40413      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40414      */
40415     nanText : "{0} is not a valid number",
40416     /**
40417      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40418      */
40419     castInt : true,
40420     /**
40421      * @cfg {String} defaults currency of the MoneyField
40422      * value should be in lkey
40423      */
40424     defaultCurrency : false,
40425     /**
40426      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40427      */
40428     thousandsDelimiter : false,
40429     
40430     
40431     inputlg : 9,
40432     inputmd : 9,
40433     inputsm : 9,
40434     inputxs : 6,
40435     
40436     store : false,
40437     
40438     getAutoCreate : function()
40439     {
40440         var align = this.labelAlign || this.parentLabelAlign();
40441         
40442         var id = Roo.id();
40443
40444         var cfg = {
40445             cls: 'form-group',
40446             cn: []
40447         };
40448
40449         var input =  {
40450             tag: 'input',
40451             id : id,
40452             cls : 'form-control roo-money-amount-input',
40453             autocomplete: 'new-password'
40454         };
40455         
40456         var hiddenInput = {
40457             tag: 'input',
40458             type: 'hidden',
40459             id: Roo.id(),
40460             cls: 'hidden-number-input'
40461         };
40462         
40463         if (this.name) {
40464             hiddenInput.name = this.name;
40465         }
40466
40467         if (this.disabled) {
40468             input.disabled = true;
40469         }
40470
40471         var clg = 12 - this.inputlg;
40472         var cmd = 12 - this.inputmd;
40473         var csm = 12 - this.inputsm;
40474         var cxs = 12 - this.inputxs;
40475         
40476         var container = {
40477             tag : 'div',
40478             cls : 'row roo-money-field',
40479             cn : [
40480                 {
40481                     tag : 'div',
40482                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40483                     cn : [
40484                         {
40485                             tag : 'div',
40486                             cls: 'roo-select2-container input-group',
40487                             cn: [
40488                                 {
40489                                     tag : 'input',
40490                                     cls : 'form-control roo-money-currency-input',
40491                                     autocomplete: 'new-password',
40492                                     readOnly : 1,
40493                                     name : this.currencyName
40494                                 },
40495                                 {
40496                                     tag :'span',
40497                                     cls : 'input-group-addon',
40498                                     cn : [
40499                                         {
40500                                             tag: 'span',
40501                                             cls: 'caret'
40502                                         }
40503                                     ]
40504                                 }
40505                             ]
40506                         }
40507                     ]
40508                 },
40509                 {
40510                     tag : 'div',
40511                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40512                     cn : [
40513                         {
40514                             tag: 'div',
40515                             cls: this.hasFeedback ? 'has-feedback' : '',
40516                             cn: [
40517                                 input
40518                             ]
40519                         }
40520                     ]
40521                 }
40522             ]
40523             
40524         };
40525         
40526         if (this.fieldLabel.length) {
40527             var indicator = {
40528                 tag: 'i',
40529                 tooltip: 'This field is required'
40530             };
40531
40532             var label = {
40533                 tag: 'label',
40534                 'for':  id,
40535                 cls: 'control-label',
40536                 cn: []
40537             };
40538
40539             var label_text = {
40540                 tag: 'span',
40541                 html: this.fieldLabel
40542             };
40543
40544             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40545             label.cn = [
40546                 indicator,
40547                 label_text
40548             ];
40549
40550             if(this.indicatorpos == 'right') {
40551                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40552                 label.cn = [
40553                     label_text,
40554                     indicator
40555                 ];
40556             }
40557
40558             if(align == 'left') {
40559                 container = {
40560                     tag: 'div',
40561                     cn: [
40562                         container
40563                     ]
40564                 };
40565
40566                 if(this.labelWidth > 12){
40567                     label.style = "width: " + this.labelWidth + 'px';
40568                 }
40569                 if(this.labelWidth < 13 && this.labelmd == 0){
40570                     this.labelmd = this.labelWidth;
40571                 }
40572                 if(this.labellg > 0){
40573                     label.cls += ' col-lg-' + this.labellg;
40574                     input.cls += ' col-lg-' + (12 - this.labellg);
40575                 }
40576                 if(this.labelmd > 0){
40577                     label.cls += ' col-md-' + this.labelmd;
40578                     container.cls += ' col-md-' + (12 - this.labelmd);
40579                 }
40580                 if(this.labelsm > 0){
40581                     label.cls += ' col-sm-' + this.labelsm;
40582                     container.cls += ' col-sm-' + (12 - this.labelsm);
40583                 }
40584                 if(this.labelxs > 0){
40585                     label.cls += ' col-xs-' + this.labelxs;
40586                     container.cls += ' col-xs-' + (12 - this.labelxs);
40587                 }
40588             }
40589         }
40590
40591         cfg.cn = [
40592             label,
40593             container,
40594             hiddenInput
40595         ];
40596         
40597         var settings = this;
40598
40599         ['xs','sm','md','lg'].map(function(size){
40600             if (settings[size]) {
40601                 cfg.cls += ' col-' + size + '-' + settings[size];
40602             }
40603         });
40604         
40605         return cfg;
40606     },
40607     
40608     initEvents : function()
40609     {
40610         this.indicator = this.indicatorEl();
40611         
40612         this.initCurrencyEvent();
40613         
40614         this.initNumberEvent();
40615     },
40616     
40617     initCurrencyEvent : function()
40618     {
40619         if (!this.store) {
40620             throw "can not find store for combo";
40621         }
40622         
40623         this.store = Roo.factory(this.store, Roo.data);
40624         this.store.parent = this;
40625         
40626         this.createList();
40627         
40628         this.triggerEl = this.el.select('.input-group-addon', true).first();
40629         
40630         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40631         
40632         var _this = this;
40633         
40634         (function(){
40635             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40636             _this.list.setWidth(lw);
40637         }).defer(100);
40638         
40639         this.list.on('mouseover', this.onViewOver, this);
40640         this.list.on('mousemove', this.onViewMove, this);
40641         this.list.on('scroll', this.onViewScroll, this);
40642         
40643         if(!this.tpl){
40644             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40645         }
40646         
40647         this.view = new Roo.View(this.list, this.tpl, {
40648             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40649         });
40650         
40651         this.view.on('click', this.onViewClick, this);
40652         
40653         this.store.on('beforeload', this.onBeforeLoad, this);
40654         this.store.on('load', this.onLoad, this);
40655         this.store.on('loadexception', this.onLoadException, this);
40656         
40657         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40658             "up" : function(e){
40659                 this.inKeyMode = true;
40660                 this.selectPrev();
40661             },
40662
40663             "down" : function(e){
40664                 if(!this.isExpanded()){
40665                     this.onTriggerClick();
40666                 }else{
40667                     this.inKeyMode = true;
40668                     this.selectNext();
40669                 }
40670             },
40671
40672             "enter" : 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             "esc" : function(e){
40683                 this.collapse();
40684             },
40685
40686             "tab" : function(e){
40687                 this.collapse();
40688                 
40689                 if(this.fireEvent("specialkey", this, e)){
40690                     this.onViewClick(false);
40691                 }
40692                 
40693                 return true;
40694             },
40695
40696             scope : this,
40697
40698             doRelay : function(foo, bar, hname){
40699                 if(hname == 'down' || this.scope.isExpanded()){
40700                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40701                 }
40702                 return true;
40703             },
40704
40705             forceKeyDown: true
40706         });
40707         
40708         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40709         
40710     },
40711     
40712     initNumberEvent : function(e)
40713     {
40714         this.inputEl().on("keydown" , this.fireKey,  this);
40715         this.inputEl().on("focus", this.onFocus,  this);
40716         this.inputEl().on("blur", this.onBlur,  this);
40717         
40718         this.inputEl().relayEvent('keyup', this);
40719         
40720         if(this.indicator){
40721             this.indicator.addClass('invisible');
40722         }
40723  
40724         this.originalValue = this.getValue();
40725         
40726         if(this.validationEvent == 'keyup'){
40727             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40728             this.inputEl().on('keyup', this.filterValidation, this);
40729         }
40730         else if(this.validationEvent !== false){
40731             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40732         }
40733         
40734         if(this.selectOnFocus){
40735             this.on("focus", this.preFocus, this);
40736             
40737         }
40738         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40739             this.inputEl().on("keypress", this.filterKeys, this);
40740         } else {
40741             this.inputEl().relayEvent('keypress', this);
40742         }
40743         
40744         var allowed = "0123456789";
40745         
40746         if(this.allowDecimals){
40747             allowed += this.decimalSeparator;
40748         }
40749         
40750         if(this.allowNegative){
40751             allowed += "-";
40752         }
40753         
40754         if(this.thousandsDelimiter) {
40755             allowed += ",";
40756         }
40757         
40758         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40759         
40760         var keyPress = function(e){
40761             
40762             var k = e.getKey();
40763             
40764             var c = e.getCharCode();
40765             
40766             if(
40767                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40768                     allowed.indexOf(String.fromCharCode(c)) === -1
40769             ){
40770                 e.stopEvent();
40771                 return;
40772             }
40773             
40774             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40775                 return;
40776             }
40777             
40778             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40779                 e.stopEvent();
40780             }
40781         };
40782         
40783         this.inputEl().on("keypress", keyPress, this);
40784         
40785     },
40786     
40787     onTriggerClick : function(e)
40788     {   
40789         if(this.disabled){
40790             return;
40791         }
40792         
40793         this.page = 0;
40794         this.loadNext = false;
40795         
40796         if(this.isExpanded()){
40797             this.collapse();
40798             return;
40799         }
40800         
40801         this.hasFocus = true;
40802         
40803         if(this.triggerAction == 'all') {
40804             this.doQuery(this.allQuery, true);
40805             return;
40806         }
40807         
40808         this.doQuery(this.getRawValue());
40809     },
40810     
40811     getCurrency : function()
40812     {   
40813         var v = this.currencyEl().getValue();
40814         
40815         return v;
40816     },
40817     
40818     restrictHeight : function()
40819     {
40820         this.list.alignTo(this.currencyEl(), this.listAlign);
40821         this.list.alignTo(this.currencyEl(), this.listAlign);
40822     },
40823     
40824     onViewClick : function(view, doFocus, el, e)
40825     {
40826         var index = this.view.getSelectedIndexes()[0];
40827         
40828         var r = this.store.getAt(index);
40829         
40830         if(r){
40831             this.onSelect(r, index);
40832         }
40833     },
40834     
40835     onSelect : function(record, index){
40836         
40837         if(this.fireEvent('beforeselect', this, record, index) !== false){
40838         
40839             this.setFromCurrencyData(index > -1 ? record.data : false);
40840             
40841             this.collapse();
40842             
40843             this.fireEvent('select', this, record, index);
40844         }
40845     },
40846     
40847     setFromCurrencyData : function(o)
40848     {
40849         var currency = '';
40850         
40851         this.lastCurrency = o;
40852         
40853         if (this.currencyField) {
40854             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40855         } else {
40856             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40857         }
40858         
40859         this.lastSelectionText = currency;
40860         
40861         //setting default currency
40862         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40863             this.setCurrency(this.defaultCurrency);
40864             return;
40865         }
40866         
40867         this.setCurrency(currency);
40868     },
40869     
40870     setFromData : function(o)
40871     {
40872         var c = {};
40873         
40874         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40875         
40876         this.setFromCurrencyData(c);
40877         
40878         var value = '';
40879         
40880         if (this.name) {
40881             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40882         } else {
40883             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40884         }
40885         
40886         this.setValue(value);
40887         
40888     },
40889     
40890     setCurrency : function(v)
40891     {   
40892         this.currencyValue = v;
40893         
40894         if(this.rendered){
40895             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40896             this.validate();
40897         }
40898     },
40899     
40900     setValue : function(v)
40901     {
40902         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40903         
40904         this.value = v;
40905         
40906         if(this.rendered){
40907             
40908             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40909             
40910             this.inputEl().dom.value = (v == '') ? '' :
40911                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40912             
40913             if(!this.allowZero && v === '0') {
40914                 this.hiddenEl().dom.value = '';
40915                 this.inputEl().dom.value = '';
40916             }
40917             
40918             this.validate();
40919         }
40920     },
40921     
40922     getRawValue : function()
40923     {
40924         var v = this.inputEl().getValue();
40925         
40926         return v;
40927     },
40928     
40929     getValue : function()
40930     {
40931         return this.fixPrecision(this.parseValue(this.getRawValue()));
40932     },
40933     
40934     parseValue : function(value)
40935     {
40936         if(this.thousandsDelimiter) {
40937             value += "";
40938             r = new RegExp(",", "g");
40939             value = value.replace(r, "");
40940         }
40941         
40942         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40943         return isNaN(value) ? '' : value;
40944         
40945     },
40946     
40947     fixPrecision : function(value)
40948     {
40949         if(this.thousandsDelimiter) {
40950             value += "";
40951             r = new RegExp(",", "g");
40952             value = value.replace(r, "");
40953         }
40954         
40955         var nan = isNaN(value);
40956         
40957         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40958             return nan ? '' : value;
40959         }
40960         return parseFloat(value).toFixed(this.decimalPrecision);
40961     },
40962     
40963     decimalPrecisionFcn : function(v)
40964     {
40965         return Math.floor(v);
40966     },
40967     
40968     validateValue : function(value)
40969     {
40970         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40971             return false;
40972         }
40973         
40974         var num = this.parseValue(value);
40975         
40976         if(isNaN(num)){
40977             this.markInvalid(String.format(this.nanText, value));
40978             return false;
40979         }
40980         
40981         if(num < this.minValue){
40982             this.markInvalid(String.format(this.minText, this.minValue));
40983             return false;
40984         }
40985         
40986         if(num > this.maxValue){
40987             this.markInvalid(String.format(this.maxText, this.maxValue));
40988             return false;
40989         }
40990         
40991         return true;
40992     },
40993     
40994     validate : function()
40995     {
40996         if(this.disabled || this.allowBlank){
40997             this.markValid();
40998             return true;
40999         }
41000         
41001         var currency = this.getCurrency();
41002         
41003         if(this.validateValue(this.getRawValue()) && currency.length){
41004             this.markValid();
41005             return true;
41006         }
41007         
41008         this.markInvalid();
41009         return false;
41010     },
41011     
41012     getName: function()
41013     {
41014         return this.name;
41015     },
41016     
41017     beforeBlur : function()
41018     {
41019         if(!this.castInt){
41020             return;
41021         }
41022         
41023         var v = this.parseValue(this.getRawValue());
41024         
41025         if(v || v == 0){
41026             this.setValue(v);
41027         }
41028     },
41029     
41030     onBlur : function()
41031     {
41032         this.beforeBlur();
41033         
41034         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41035             //this.el.removeClass(this.focusClass);
41036         }
41037         
41038         this.hasFocus = false;
41039         
41040         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41041             this.validate();
41042         }
41043         
41044         var v = this.getValue();
41045         
41046         if(String(v) !== String(this.startValue)){
41047             this.fireEvent('change', this, v, this.startValue);
41048         }
41049         
41050         this.fireEvent("blur", this);
41051     },
41052     
41053     inputEl : function()
41054     {
41055         return this.el.select('.roo-money-amount-input', true).first();
41056     },
41057     
41058     currencyEl : function()
41059     {
41060         return this.el.select('.roo-money-currency-input', true).first();
41061     },
41062     
41063     hiddenEl : function()
41064     {
41065         return this.el.select('input.hidden-number-input',true).first();
41066     }
41067     
41068 });