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         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7114         
7115         Roo.log(rows);
7116     }
7117 });
7118
7119  
7120
7121  /*
7122  * - LGPL
7123  *
7124  * table cell
7125  * 
7126  */
7127
7128 /**
7129  * @class Roo.bootstrap.TableCell
7130  * @extends Roo.bootstrap.Component
7131  * Bootstrap TableCell class
7132  * @cfg {String} html cell contain text
7133  * @cfg {String} cls cell class
7134  * @cfg {String} tag cell tag (td|th) default td
7135  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7136  * @cfg {String} align Aligns the content in a cell
7137  * @cfg {String} axis Categorizes cells
7138  * @cfg {String} bgcolor Specifies the background color of a cell
7139  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7140  * @cfg {Number} colspan Specifies the number of columns a cell should span
7141  * @cfg {String} headers Specifies one or more header cells a cell is related to
7142  * @cfg {Number} height Sets the height of a cell
7143  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7144  * @cfg {Number} rowspan Sets the number of rows a cell should span
7145  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7146  * @cfg {String} valign Vertical aligns the content in a cell
7147  * @cfg {Number} width Specifies the width of a cell
7148  * 
7149  * @constructor
7150  * Create a new TableCell
7151  * @param {Object} config The config object
7152  */
7153
7154 Roo.bootstrap.TableCell = function(config){
7155     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7156 };
7157
7158 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7159     
7160     html: false,
7161     cls: false,
7162     tag: false,
7163     abbr: false,
7164     align: false,
7165     axis: false,
7166     bgcolor: false,
7167     charoff: false,
7168     colspan: false,
7169     headers: false,
7170     height: false,
7171     nowrap: false,
7172     rowspan: false,
7173     scope: false,
7174     valign: false,
7175     width: false,
7176     
7177     
7178     getAutoCreate : function(){
7179         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7180         
7181         cfg = {
7182             tag: 'td'
7183         };
7184         
7185         if(this.tag){
7186             cfg.tag = this.tag;
7187         }
7188         
7189         if (this.html) {
7190             cfg.html=this.html
7191         }
7192         if (this.cls) {
7193             cfg.cls=this.cls
7194         }
7195         if (this.abbr) {
7196             cfg.abbr=this.abbr
7197         }
7198         if (this.align) {
7199             cfg.align=this.align
7200         }
7201         if (this.axis) {
7202             cfg.axis=this.axis
7203         }
7204         if (this.bgcolor) {
7205             cfg.bgcolor=this.bgcolor
7206         }
7207         if (this.charoff) {
7208             cfg.charoff=this.charoff
7209         }
7210         if (this.colspan) {
7211             cfg.colspan=this.colspan
7212         }
7213         if (this.headers) {
7214             cfg.headers=this.headers
7215         }
7216         if (this.height) {
7217             cfg.height=this.height
7218         }
7219         if (this.nowrap) {
7220             cfg.nowrap=this.nowrap
7221         }
7222         if (this.rowspan) {
7223             cfg.rowspan=this.rowspan
7224         }
7225         if (this.scope) {
7226             cfg.scope=this.scope
7227         }
7228         if (this.valign) {
7229             cfg.valign=this.valign
7230         }
7231         if (this.width) {
7232             cfg.width=this.width
7233         }
7234         
7235         
7236         return cfg;
7237     }
7238    
7239 });
7240
7241  
7242
7243  /*
7244  * - LGPL
7245  *
7246  * table row
7247  * 
7248  */
7249
7250 /**
7251  * @class Roo.bootstrap.TableRow
7252  * @extends Roo.bootstrap.Component
7253  * Bootstrap TableRow class
7254  * @cfg {String} cls row class
7255  * @cfg {String} align Aligns the content in a table row
7256  * @cfg {String} bgcolor Specifies a background color for a table row
7257  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7258  * @cfg {String} valign Vertical aligns the content in a table row
7259  * 
7260  * @constructor
7261  * Create a new TableRow
7262  * @param {Object} config The config object
7263  */
7264
7265 Roo.bootstrap.TableRow = function(config){
7266     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7267 };
7268
7269 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7270     
7271     cls: false,
7272     align: false,
7273     bgcolor: false,
7274     charoff: false,
7275     valign: false,
7276     
7277     getAutoCreate : function(){
7278         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7279         
7280         cfg = {
7281             tag: 'tr'
7282         };
7283             
7284         if(this.cls){
7285             cfg.cls = this.cls;
7286         }
7287         if(this.align){
7288             cfg.align = this.align;
7289         }
7290         if(this.bgcolor){
7291             cfg.bgcolor = this.bgcolor;
7292         }
7293         if(this.charoff){
7294             cfg.charoff = this.charoff;
7295         }
7296         if(this.valign){
7297             cfg.valign = this.valign;
7298         }
7299         
7300         return cfg;
7301     }
7302    
7303 });
7304
7305  
7306
7307  /*
7308  * - LGPL
7309  *
7310  * table body
7311  * 
7312  */
7313
7314 /**
7315  * @class Roo.bootstrap.TableBody
7316  * @extends Roo.bootstrap.Component
7317  * Bootstrap TableBody class
7318  * @cfg {String} cls element class
7319  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7320  * @cfg {String} align Aligns the content inside the element
7321  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7322  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7323  * 
7324  * @constructor
7325  * Create a new TableBody
7326  * @param {Object} config The config object
7327  */
7328
7329 Roo.bootstrap.TableBody = function(config){
7330     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7331 };
7332
7333 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7334     
7335     cls: false,
7336     tag: false,
7337     align: false,
7338     charoff: false,
7339     valign: false,
7340     
7341     getAutoCreate : function(){
7342         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7343         
7344         cfg = {
7345             tag: 'tbody'
7346         };
7347             
7348         if (this.cls) {
7349             cfg.cls=this.cls
7350         }
7351         if(this.tag){
7352             cfg.tag = this.tag;
7353         }
7354         
7355         if(this.align){
7356             cfg.align = this.align;
7357         }
7358         if(this.charoff){
7359             cfg.charoff = this.charoff;
7360         }
7361         if(this.valign){
7362             cfg.valign = this.valign;
7363         }
7364         
7365         return cfg;
7366     }
7367     
7368     
7369 //    initEvents : function()
7370 //    {
7371 //        
7372 //        if(!this.store){
7373 //            return;
7374 //        }
7375 //        
7376 //        this.store = Roo.factory(this.store, Roo.data);
7377 //        this.store.on('load', this.onLoad, this);
7378 //        
7379 //        this.store.load();
7380 //        
7381 //    },
7382 //    
7383 //    onLoad: function () 
7384 //    {   
7385 //        this.fireEvent('load', this);
7386 //    }
7387 //    
7388 //   
7389 });
7390
7391  
7392
7393  /*
7394  * Based on:
7395  * Ext JS Library 1.1.1
7396  * Copyright(c) 2006-2007, Ext JS, LLC.
7397  *
7398  * Originally Released Under LGPL - original licence link has changed is not relivant.
7399  *
7400  * Fork - LGPL
7401  * <script type="text/javascript">
7402  */
7403
7404 // as we use this in bootstrap.
7405 Roo.namespace('Roo.form');
7406  /**
7407  * @class Roo.form.Action
7408  * Internal Class used to handle form actions
7409  * @constructor
7410  * @param {Roo.form.BasicForm} el The form element or its id
7411  * @param {Object} config Configuration options
7412  */
7413
7414  
7415  
7416 // define the action interface
7417 Roo.form.Action = function(form, options){
7418     this.form = form;
7419     this.options = options || {};
7420 };
7421 /**
7422  * Client Validation Failed
7423  * @const 
7424  */
7425 Roo.form.Action.CLIENT_INVALID = 'client';
7426 /**
7427  * Server Validation Failed
7428  * @const 
7429  */
7430 Roo.form.Action.SERVER_INVALID = 'server';
7431  /**
7432  * Connect to Server Failed
7433  * @const 
7434  */
7435 Roo.form.Action.CONNECT_FAILURE = 'connect';
7436 /**
7437  * Reading Data from Server Failed
7438  * @const 
7439  */
7440 Roo.form.Action.LOAD_FAILURE = 'load';
7441
7442 Roo.form.Action.prototype = {
7443     type : 'default',
7444     failureType : undefined,
7445     response : undefined,
7446     result : undefined,
7447
7448     // interface method
7449     run : function(options){
7450
7451     },
7452
7453     // interface method
7454     success : function(response){
7455
7456     },
7457
7458     // interface method
7459     handleResponse : function(response){
7460
7461     },
7462
7463     // default connection failure
7464     failure : function(response){
7465         
7466         this.response = response;
7467         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7468         this.form.afterAction(this, false);
7469     },
7470
7471     processResponse : function(response){
7472         this.response = response;
7473         if(!response.responseText){
7474             return true;
7475         }
7476         this.result = this.handleResponse(response);
7477         return this.result;
7478     },
7479
7480     // utility functions used internally
7481     getUrl : function(appendParams){
7482         var url = this.options.url || this.form.url || this.form.el.dom.action;
7483         if(appendParams){
7484             var p = this.getParams();
7485             if(p){
7486                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7487             }
7488         }
7489         return url;
7490     },
7491
7492     getMethod : function(){
7493         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7494     },
7495
7496     getParams : function(){
7497         var bp = this.form.baseParams;
7498         var p = this.options.params;
7499         if(p){
7500             if(typeof p == "object"){
7501                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7502             }else if(typeof p == 'string' && bp){
7503                 p += '&' + Roo.urlEncode(bp);
7504             }
7505         }else if(bp){
7506             p = Roo.urlEncode(bp);
7507         }
7508         return p;
7509     },
7510
7511     createCallback : function(){
7512         return {
7513             success: this.success,
7514             failure: this.failure,
7515             scope: this,
7516             timeout: (this.form.timeout*1000),
7517             upload: this.form.fileUpload ? this.success : undefined
7518         };
7519     }
7520 };
7521
7522 Roo.form.Action.Submit = function(form, options){
7523     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7524 };
7525
7526 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7527     type : 'submit',
7528
7529     haveProgress : false,
7530     uploadComplete : false,
7531     
7532     // uploadProgress indicator.
7533     uploadProgress : function()
7534     {
7535         if (!this.form.progressUrl) {
7536             return;
7537         }
7538         
7539         if (!this.haveProgress) {
7540             Roo.MessageBox.progress("Uploading", "Uploading");
7541         }
7542         if (this.uploadComplete) {
7543            Roo.MessageBox.hide();
7544            return;
7545         }
7546         
7547         this.haveProgress = true;
7548    
7549         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7550         
7551         var c = new Roo.data.Connection();
7552         c.request({
7553             url : this.form.progressUrl,
7554             params: {
7555                 id : uid
7556             },
7557             method: 'GET',
7558             success : function(req){
7559                //console.log(data);
7560                 var rdata = false;
7561                 var edata;
7562                 try  {
7563                    rdata = Roo.decode(req.responseText)
7564                 } catch (e) {
7565                     Roo.log("Invalid data from server..");
7566                     Roo.log(edata);
7567                     return;
7568                 }
7569                 if (!rdata || !rdata.success) {
7570                     Roo.log(rdata);
7571                     Roo.MessageBox.alert(Roo.encode(rdata));
7572                     return;
7573                 }
7574                 var data = rdata.data;
7575                 
7576                 if (this.uploadComplete) {
7577                    Roo.MessageBox.hide();
7578                    return;
7579                 }
7580                    
7581                 if (data){
7582                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7583                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7584                     );
7585                 }
7586                 this.uploadProgress.defer(2000,this);
7587             },
7588        
7589             failure: function(data) {
7590                 Roo.log('progress url failed ');
7591                 Roo.log(data);
7592             },
7593             scope : this
7594         });
7595            
7596     },
7597     
7598     
7599     run : function()
7600     {
7601         // run get Values on the form, so it syncs any secondary forms.
7602         this.form.getValues();
7603         
7604         var o = this.options;
7605         var method = this.getMethod();
7606         var isPost = method == 'POST';
7607         if(o.clientValidation === false || this.form.isValid()){
7608             
7609             if (this.form.progressUrl) {
7610                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7611                     (new Date() * 1) + '' + Math.random());
7612                     
7613             } 
7614             
7615             
7616             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7617                 form:this.form.el.dom,
7618                 url:this.getUrl(!isPost),
7619                 method: method,
7620                 params:isPost ? this.getParams() : null,
7621                 isUpload: this.form.fileUpload
7622             }));
7623             
7624             this.uploadProgress();
7625
7626         }else if (o.clientValidation !== false){ // client validation failed
7627             this.failureType = Roo.form.Action.CLIENT_INVALID;
7628             this.form.afterAction(this, false);
7629         }
7630     },
7631
7632     success : function(response)
7633     {
7634         this.uploadComplete= true;
7635         if (this.haveProgress) {
7636             Roo.MessageBox.hide();
7637         }
7638         
7639         
7640         var result = this.processResponse(response);
7641         if(result === true || result.success){
7642             this.form.afterAction(this, true);
7643             return;
7644         }
7645         if(result.errors){
7646             this.form.markInvalid(result.errors);
7647             this.failureType = Roo.form.Action.SERVER_INVALID;
7648         }
7649         this.form.afterAction(this, false);
7650     },
7651     failure : function(response)
7652     {
7653         this.uploadComplete= true;
7654         if (this.haveProgress) {
7655             Roo.MessageBox.hide();
7656         }
7657         
7658         this.response = response;
7659         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7660         this.form.afterAction(this, false);
7661     },
7662     
7663     handleResponse : function(response){
7664         if(this.form.errorReader){
7665             var rs = this.form.errorReader.read(response);
7666             var errors = [];
7667             if(rs.records){
7668                 for(var i = 0, len = rs.records.length; i < len; i++) {
7669                     var r = rs.records[i];
7670                     errors[i] = r.data;
7671                 }
7672             }
7673             if(errors.length < 1){
7674                 errors = null;
7675             }
7676             return {
7677                 success : rs.success,
7678                 errors : errors
7679             };
7680         }
7681         var ret = false;
7682         try {
7683             ret = Roo.decode(response.responseText);
7684         } catch (e) {
7685             ret = {
7686                 success: false,
7687                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7688                 errors : []
7689             };
7690         }
7691         return ret;
7692         
7693     }
7694 });
7695
7696
7697 Roo.form.Action.Load = function(form, options){
7698     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7699     this.reader = this.form.reader;
7700 };
7701
7702 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7703     type : 'load',
7704
7705     run : function(){
7706         
7707         Roo.Ajax.request(Roo.apply(
7708                 this.createCallback(), {
7709                     method:this.getMethod(),
7710                     url:this.getUrl(false),
7711                     params:this.getParams()
7712         }));
7713     },
7714
7715     success : function(response){
7716         
7717         var result = this.processResponse(response);
7718         if(result === true || !result.success || !result.data){
7719             this.failureType = Roo.form.Action.LOAD_FAILURE;
7720             this.form.afterAction(this, false);
7721             return;
7722         }
7723         this.form.clearInvalid();
7724         this.form.setValues(result.data);
7725         this.form.afterAction(this, true);
7726     },
7727
7728     handleResponse : function(response){
7729         if(this.form.reader){
7730             var rs = this.form.reader.read(response);
7731             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7732             return {
7733                 success : rs.success,
7734                 data : data
7735             };
7736         }
7737         return Roo.decode(response.responseText);
7738     }
7739 });
7740
7741 Roo.form.Action.ACTION_TYPES = {
7742     'load' : Roo.form.Action.Load,
7743     'submit' : Roo.form.Action.Submit
7744 };/*
7745  * - LGPL
7746  *
7747  * form
7748  *
7749  */
7750
7751 /**
7752  * @class Roo.bootstrap.Form
7753  * @extends Roo.bootstrap.Component
7754  * Bootstrap Form class
7755  * @cfg {String} method  GET | POST (default POST)
7756  * @cfg {String} labelAlign top | left (default top)
7757  * @cfg {String} align left  | right - for navbars
7758  * @cfg {Boolean} loadMask load mask when submit (default true)
7759
7760  *
7761  * @constructor
7762  * Create a new Form
7763  * @param {Object} config The config object
7764  */
7765
7766
7767 Roo.bootstrap.Form = function(config){
7768     
7769     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7770     
7771     Roo.bootstrap.Form.popover.apply();
7772     
7773     this.addEvents({
7774         /**
7775          * @event clientvalidation
7776          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7777          * @param {Form} this
7778          * @param {Boolean} valid true if the form has passed client-side validation
7779          */
7780         clientvalidation: true,
7781         /**
7782          * @event beforeaction
7783          * Fires before any action is performed. Return false to cancel the action.
7784          * @param {Form} this
7785          * @param {Action} action The action to be performed
7786          */
7787         beforeaction: true,
7788         /**
7789          * @event actionfailed
7790          * Fires when an action fails.
7791          * @param {Form} this
7792          * @param {Action} action The action that failed
7793          */
7794         actionfailed : true,
7795         /**
7796          * @event actioncomplete
7797          * Fires when an action is completed.
7798          * @param {Form} this
7799          * @param {Action} action The action that completed
7800          */
7801         actioncomplete : true
7802     });
7803 };
7804
7805 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7806
7807      /**
7808      * @cfg {String} method
7809      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7810      */
7811     method : 'POST',
7812     /**
7813      * @cfg {String} url
7814      * The URL to use for form actions if one isn't supplied in the action options.
7815      */
7816     /**
7817      * @cfg {Boolean} fileUpload
7818      * Set to true if this form is a file upload.
7819      */
7820
7821     /**
7822      * @cfg {Object} baseParams
7823      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7824      */
7825
7826     /**
7827      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7828      */
7829     timeout: 30,
7830     /**
7831      * @cfg {Sting} align (left|right) for navbar forms
7832      */
7833     align : 'left',
7834
7835     // private
7836     activeAction : null,
7837
7838     /**
7839      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7840      * element by passing it or its id or mask the form itself by passing in true.
7841      * @type Mixed
7842      */
7843     waitMsgTarget : false,
7844
7845     loadMask : true,
7846     
7847     /**
7848      * @cfg {Boolean} errorMask (true|false) default false
7849      */
7850     errorMask : false,
7851     
7852     /**
7853      * @cfg {Number} maskOffset Default 100
7854      */
7855     maskOffset : 100,
7856     
7857     /**
7858      * @cfg {Boolean} maskBody
7859      */
7860     maskBody : false,
7861
7862     getAutoCreate : function(){
7863
7864         var cfg = {
7865             tag: 'form',
7866             method : this.method || 'POST',
7867             id : this.id || Roo.id(),
7868             cls : ''
7869         };
7870         if (this.parent().xtype.match(/^Nav/)) {
7871             cfg.cls = 'navbar-form navbar-' + this.align;
7872
7873         }
7874
7875         if (this.labelAlign == 'left' ) {
7876             cfg.cls += ' form-horizontal';
7877         }
7878
7879
7880         return cfg;
7881     },
7882     initEvents : function()
7883     {
7884         this.el.on('submit', this.onSubmit, this);
7885         // this was added as random key presses on the form where triggering form submit.
7886         this.el.on('keypress', function(e) {
7887             if (e.getCharCode() != 13) {
7888                 return true;
7889             }
7890             // we might need to allow it for textareas.. and some other items.
7891             // check e.getTarget().
7892
7893             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7894                 return true;
7895             }
7896
7897             Roo.log("keypress blocked");
7898
7899             e.preventDefault();
7900             return false;
7901         });
7902         
7903     },
7904     // private
7905     onSubmit : function(e){
7906         e.stopEvent();
7907     },
7908
7909      /**
7910      * Returns true if client-side validation on the form is successful.
7911      * @return Boolean
7912      */
7913     isValid : function(){
7914         var items = this.getItems();
7915         var valid = true;
7916         var target = false;
7917         
7918         items.each(function(f){
7919             
7920             if(f.validate()){
7921                 return;
7922             }
7923             
7924             Roo.log('invalid field: ' + f.name);
7925             
7926             valid = false;
7927
7928             if(!target && f.el.isVisible(true)){
7929                 target = f;
7930             }
7931            
7932         });
7933         
7934         if(this.errorMask && !valid){
7935             Roo.bootstrap.Form.popover.mask(this, target);
7936         }
7937         
7938         return valid;
7939     },
7940     
7941     /**
7942      * Returns true if any fields in this form have changed since their original load.
7943      * @return Boolean
7944      */
7945     isDirty : function(){
7946         var dirty = false;
7947         var items = this.getItems();
7948         items.each(function(f){
7949            if(f.isDirty()){
7950                dirty = true;
7951                return false;
7952            }
7953            return true;
7954         });
7955         return dirty;
7956     },
7957      /**
7958      * Performs a predefined action (submit or load) or custom actions you define on this form.
7959      * @param {String} actionName The name of the action type
7960      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7961      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7962      * accept other config options):
7963      * <pre>
7964 Property          Type             Description
7965 ----------------  ---------------  ----------------------------------------------------------------------------------
7966 url               String           The url for the action (defaults to the form's url)
7967 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7968 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7969 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7970                                    validate the form on the client (defaults to false)
7971      * </pre>
7972      * @return {BasicForm} this
7973      */
7974     doAction : function(action, options){
7975         if(typeof action == 'string'){
7976             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7977         }
7978         if(this.fireEvent('beforeaction', this, action) !== false){
7979             this.beforeAction(action);
7980             action.run.defer(100, action);
7981         }
7982         return this;
7983     },
7984
7985     // private
7986     beforeAction : function(action){
7987         var o = action.options;
7988         
7989         if(this.loadMask){
7990             
7991             if(this.maskBody){
7992                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7993             } else {
7994                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7995             }
7996         }
7997         // not really supported yet.. ??
7998
7999         //if(this.waitMsgTarget === true){
8000         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8001         //}else if(this.waitMsgTarget){
8002         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8003         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8004         //}else {
8005         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8006        // }
8007
8008     },
8009
8010     // private
8011     afterAction : function(action, success){
8012         this.activeAction = null;
8013         var o = action.options;
8014
8015         if(this.loadMask){
8016             
8017             if(this.maskBody){
8018                 Roo.get(document.body).unmask();
8019             } else {
8020                 this.el.unmask();
8021             }
8022         }
8023         
8024         //if(this.waitMsgTarget === true){
8025 //            this.el.unmask();
8026         //}else if(this.waitMsgTarget){
8027         //    this.waitMsgTarget.unmask();
8028         //}else{
8029         //    Roo.MessageBox.updateProgress(1);
8030         //    Roo.MessageBox.hide();
8031        // }
8032         //
8033         if(success){
8034             if(o.reset){
8035                 this.reset();
8036             }
8037             Roo.callback(o.success, o.scope, [this, action]);
8038             this.fireEvent('actioncomplete', this, action);
8039
8040         }else{
8041
8042             // failure condition..
8043             // we have a scenario where updates need confirming.
8044             // eg. if a locking scenario exists..
8045             // we look for { errors : { needs_confirm : true }} in the response.
8046             if (
8047                 (typeof(action.result) != 'undefined')  &&
8048                 (typeof(action.result.errors) != 'undefined')  &&
8049                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8050            ){
8051                 var _t = this;
8052                 Roo.log("not supported yet");
8053                  /*
8054
8055                 Roo.MessageBox.confirm(
8056                     "Change requires confirmation",
8057                     action.result.errorMsg,
8058                     function(r) {
8059                         if (r != 'yes') {
8060                             return;
8061                         }
8062                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8063                     }
8064
8065                 );
8066                 */
8067
8068
8069                 return;
8070             }
8071
8072             Roo.callback(o.failure, o.scope, [this, action]);
8073             // show an error message if no failed handler is set..
8074             if (!this.hasListener('actionfailed')) {
8075                 Roo.log("need to add dialog support");
8076                 /*
8077                 Roo.MessageBox.alert("Error",
8078                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8079                         action.result.errorMsg :
8080                         "Saving Failed, please check your entries or try again"
8081                 );
8082                 */
8083             }
8084
8085             this.fireEvent('actionfailed', this, action);
8086         }
8087
8088     },
8089     /**
8090      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8091      * @param {String} id The value to search for
8092      * @return Field
8093      */
8094     findField : function(id){
8095         var items = this.getItems();
8096         var field = items.get(id);
8097         if(!field){
8098              items.each(function(f){
8099                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8100                     field = f;
8101                     return false;
8102                 }
8103                 return true;
8104             });
8105         }
8106         return field || null;
8107     },
8108      /**
8109      * Mark fields in this form invalid in bulk.
8110      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8111      * @return {BasicForm} this
8112      */
8113     markInvalid : function(errors){
8114         if(errors instanceof Array){
8115             for(var i = 0, len = errors.length; i < len; i++){
8116                 var fieldError = errors[i];
8117                 var f = this.findField(fieldError.id);
8118                 if(f){
8119                     f.markInvalid(fieldError.msg);
8120                 }
8121             }
8122         }else{
8123             var field, id;
8124             for(id in errors){
8125                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8126                     field.markInvalid(errors[id]);
8127                 }
8128             }
8129         }
8130         //Roo.each(this.childForms || [], function (f) {
8131         //    f.markInvalid(errors);
8132         //});
8133
8134         return this;
8135     },
8136
8137     /**
8138      * Set values for fields in this form in bulk.
8139      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8140      * @return {BasicForm} this
8141      */
8142     setValues : function(values){
8143         if(values instanceof Array){ // array of objects
8144             for(var i = 0, len = values.length; i < len; i++){
8145                 var v = values[i];
8146                 var f = this.findField(v.id);
8147                 if(f){
8148                     f.setValue(v.value);
8149                     if(this.trackResetOnLoad){
8150                         f.originalValue = f.getValue();
8151                     }
8152                 }
8153             }
8154         }else{ // object hash
8155             var field, id;
8156             for(id in values){
8157                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8158
8159                     if (field.setFromData &&
8160                         field.valueField &&
8161                         field.displayField &&
8162                         // combos' with local stores can
8163                         // be queried via setValue()
8164                         // to set their value..
8165                         (field.store && !field.store.isLocal)
8166                         ) {
8167                         // it's a combo
8168                         var sd = { };
8169                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8170                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8171                         field.setFromData(sd);
8172
8173                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8174                         
8175                         field.setFromData(values);
8176                         
8177                     } else {
8178                         field.setValue(values[id]);
8179                     }
8180
8181
8182                     if(this.trackResetOnLoad){
8183                         field.originalValue = field.getValue();
8184                     }
8185                 }
8186             }
8187         }
8188
8189         //Roo.each(this.childForms || [], function (f) {
8190         //    f.setValues(values);
8191         //});
8192
8193         return this;
8194     },
8195
8196     /**
8197      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8198      * they are returned as an array.
8199      * @param {Boolean} asString
8200      * @return {Object}
8201      */
8202     getValues : function(asString){
8203         //if (this.childForms) {
8204             // copy values from the child forms
8205         //    Roo.each(this.childForms, function (f) {
8206         //        this.setValues(f.getValues());
8207         //    }, this);
8208         //}
8209
8210
8211
8212         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8213         if(asString === true){
8214             return fs;
8215         }
8216         return Roo.urlDecode(fs);
8217     },
8218
8219     /**
8220      * Returns the fields in this form as an object with key/value pairs.
8221      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8222      * @return {Object}
8223      */
8224     getFieldValues : function(with_hidden)
8225     {
8226         var items = this.getItems();
8227         var ret = {};
8228         items.each(function(f){
8229             
8230             if (!f.getName()) {
8231                 return;
8232             }
8233             
8234             var v = f.getValue();
8235             
8236             if (f.inputType =='radio') {
8237                 if (typeof(ret[f.getName()]) == 'undefined') {
8238                     ret[f.getName()] = ''; // empty..
8239                 }
8240
8241                 if (!f.el.dom.checked) {
8242                     return;
8243
8244                 }
8245                 v = f.el.dom.value;
8246
8247             }
8248             
8249             if(f.xtype == 'MoneyField'){
8250                 ret[f.currencyName] = f.getCurrency();
8251             }
8252
8253             // not sure if this supported any more..
8254             if ((typeof(v) == 'object') && f.getRawValue) {
8255                 v = f.getRawValue() ; // dates..
8256             }
8257             // combo boxes where name != hiddenName...
8258             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8259                 ret[f.name] = f.getRawValue();
8260             }
8261             ret[f.getName()] = v;
8262         });
8263
8264         return ret;
8265     },
8266
8267     /**
8268      * Clears all invalid messages in this form.
8269      * @return {BasicForm} this
8270      */
8271     clearInvalid : function(){
8272         var items = this.getItems();
8273
8274         items.each(function(f){
8275            f.clearInvalid();
8276         });
8277
8278         return this;
8279     },
8280
8281     /**
8282      * Resets this form.
8283      * @return {BasicForm} this
8284      */
8285     reset : function(){
8286         var items = this.getItems();
8287         items.each(function(f){
8288             f.reset();
8289         });
8290
8291         Roo.each(this.childForms || [], function (f) {
8292             f.reset();
8293         });
8294
8295
8296         return this;
8297     },
8298     
8299     getItems : function()
8300     {
8301         var r=new Roo.util.MixedCollection(false, function(o){
8302             return o.id || (o.id = Roo.id());
8303         });
8304         var iter = function(el) {
8305             if (el.inputEl) {
8306                 r.add(el);
8307             }
8308             if (!el.items) {
8309                 return;
8310             }
8311             Roo.each(el.items,function(e) {
8312                 iter(e);
8313             });
8314         };
8315
8316         iter(this);
8317         return r;
8318     },
8319     
8320     hideFields : function(items)
8321     {
8322         Roo.each(items, function(i){
8323             
8324             var f = this.findField(i);
8325             
8326             if(!f){
8327                 return;
8328             }
8329             
8330             f.hide();
8331             
8332         }, this);
8333     },
8334     
8335     showFields : function(items)
8336     {
8337         Roo.each(items, function(i){
8338             
8339             var f = this.findField(i);
8340             
8341             if(!f){
8342                 return;
8343             }
8344             
8345             f.show();
8346             
8347         }, this);
8348     }
8349
8350 });
8351
8352 Roo.apply(Roo.bootstrap.Form, {
8353     
8354     popover : {
8355         
8356         padding : 5,
8357         
8358         isApplied : false,
8359         
8360         isMasked : false,
8361         
8362         form : false,
8363         
8364         target : false,
8365         
8366         toolTip : false,
8367         
8368         intervalID : false,
8369         
8370         maskEl : false,
8371         
8372         apply : function()
8373         {
8374             if(this.isApplied){
8375                 return;
8376             }
8377             
8378             this.maskEl = {
8379                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8380                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8381                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8382                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8383             };
8384             
8385             this.maskEl.top.enableDisplayMode("block");
8386             this.maskEl.left.enableDisplayMode("block");
8387             this.maskEl.bottom.enableDisplayMode("block");
8388             this.maskEl.right.enableDisplayMode("block");
8389             
8390             this.toolTip = new Roo.bootstrap.Tooltip({
8391                 cls : 'roo-form-error-popover',
8392                 alignment : {
8393                     'left' : ['r-l', [-2,0], 'right'],
8394                     'right' : ['l-r', [2,0], 'left'],
8395                     'bottom' : ['tl-bl', [0,2], 'top'],
8396                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8397                 }
8398             });
8399             
8400             this.toolTip.render(Roo.get(document.body));
8401
8402             this.toolTip.el.enableDisplayMode("block");
8403             
8404             Roo.get(document.body).on('click', function(){
8405                 this.unmask();
8406             }, this);
8407             
8408             Roo.get(document.body).on('touchstart', function(){
8409                 this.unmask();
8410             }, this);
8411             
8412             this.isApplied = true
8413         },
8414         
8415         mask : function(form, target)
8416         {
8417             this.form = form;
8418             
8419             this.target = target;
8420             
8421             if(!this.form.errorMask || !target.el){
8422                 return;
8423             }
8424             
8425             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8426             
8427             Roo.log(scrollable);
8428             
8429             var ot = this.target.el.calcOffsetsTo(scrollable);
8430             
8431             var scrollTo = ot[1] - this.form.maskOffset;
8432             
8433             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8434             
8435             scrollable.scrollTo('top', scrollTo);
8436             
8437             var box = this.target.el.getBox();
8438             Roo.log(box);
8439             var zIndex = Roo.bootstrap.Modal.zIndex++;
8440
8441             
8442             this.maskEl.top.setStyle('position', 'absolute');
8443             this.maskEl.top.setStyle('z-index', zIndex);
8444             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8445             this.maskEl.top.setLeft(0);
8446             this.maskEl.top.setTop(0);
8447             this.maskEl.top.show();
8448             
8449             this.maskEl.left.setStyle('position', 'absolute');
8450             this.maskEl.left.setStyle('z-index', zIndex);
8451             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8452             this.maskEl.left.setLeft(0);
8453             this.maskEl.left.setTop(box.y - this.padding);
8454             this.maskEl.left.show();
8455
8456             this.maskEl.bottom.setStyle('position', 'absolute');
8457             this.maskEl.bottom.setStyle('z-index', zIndex);
8458             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8459             this.maskEl.bottom.setLeft(0);
8460             this.maskEl.bottom.setTop(box.bottom + this.padding);
8461             this.maskEl.bottom.show();
8462
8463             this.maskEl.right.setStyle('position', 'absolute');
8464             this.maskEl.right.setStyle('z-index', zIndex);
8465             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8466             this.maskEl.right.setLeft(box.right + this.padding);
8467             this.maskEl.right.setTop(box.y - this.padding);
8468             this.maskEl.right.show();
8469
8470             this.toolTip.bindEl = this.target.el;
8471
8472             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8473
8474             var tip = this.target.blankText;
8475
8476             if(this.target.getValue() !== '' ) {
8477                 
8478                 if (this.target.invalidText.length) {
8479                     tip = this.target.invalidText;
8480                 } else if (this.target.regexText.length){
8481                     tip = this.target.regexText;
8482                 }
8483             }
8484
8485             this.toolTip.show(tip);
8486
8487             this.intervalID = window.setInterval(function() {
8488                 Roo.bootstrap.Form.popover.unmask();
8489             }, 10000);
8490
8491             window.onwheel = function(){ return false;};
8492             
8493             (function(){ this.isMasked = true; }).defer(500, this);
8494             
8495         },
8496         
8497         unmask : function()
8498         {
8499             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8500                 return;
8501             }
8502             
8503             this.maskEl.top.setStyle('position', 'absolute');
8504             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8505             this.maskEl.top.hide();
8506
8507             this.maskEl.left.setStyle('position', 'absolute');
8508             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8509             this.maskEl.left.hide();
8510
8511             this.maskEl.bottom.setStyle('position', 'absolute');
8512             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8513             this.maskEl.bottom.hide();
8514
8515             this.maskEl.right.setStyle('position', 'absolute');
8516             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8517             this.maskEl.right.hide();
8518             
8519             this.toolTip.hide();
8520             
8521             this.toolTip.el.hide();
8522             
8523             window.onwheel = function(){ return true;};
8524             
8525             if(this.intervalID){
8526                 window.clearInterval(this.intervalID);
8527                 this.intervalID = false;
8528             }
8529             
8530             this.isMasked = false;
8531             
8532         }
8533         
8534     }
8535     
8536 });
8537
8538 /*
8539  * Based on:
8540  * Ext JS Library 1.1.1
8541  * Copyright(c) 2006-2007, Ext JS, LLC.
8542  *
8543  * Originally Released Under LGPL - original licence link has changed is not relivant.
8544  *
8545  * Fork - LGPL
8546  * <script type="text/javascript">
8547  */
8548 /**
8549  * @class Roo.form.VTypes
8550  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8551  * @singleton
8552  */
8553 Roo.form.VTypes = function(){
8554     // closure these in so they are only created once.
8555     var alpha = /^[a-zA-Z_]+$/;
8556     var alphanum = /^[a-zA-Z0-9_]+$/;
8557     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8558     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8559
8560     // All these messages and functions are configurable
8561     return {
8562         /**
8563          * The function used to validate email addresses
8564          * @param {String} value The email address
8565          */
8566         'email' : function(v){
8567             return email.test(v);
8568         },
8569         /**
8570          * The error text to display when the email validation function returns false
8571          * @type String
8572          */
8573         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8574         /**
8575          * The keystroke filter mask to be applied on email input
8576          * @type RegExp
8577          */
8578         'emailMask' : /[a-z0-9_\.\-@]/i,
8579
8580         /**
8581          * The function used to validate URLs
8582          * @param {String} value The URL
8583          */
8584         'url' : function(v){
8585             return url.test(v);
8586         },
8587         /**
8588          * The error text to display when the url validation function returns false
8589          * @type String
8590          */
8591         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8592         
8593         /**
8594          * The function used to validate alpha values
8595          * @param {String} value The value
8596          */
8597         'alpha' : function(v){
8598             return alpha.test(v);
8599         },
8600         /**
8601          * The error text to display when the alpha validation function returns false
8602          * @type String
8603          */
8604         'alphaText' : 'This field should only contain letters and _',
8605         /**
8606          * The keystroke filter mask to be applied on alpha input
8607          * @type RegExp
8608          */
8609         'alphaMask' : /[a-z_]/i,
8610
8611         /**
8612          * The function used to validate alphanumeric values
8613          * @param {String} value The value
8614          */
8615         'alphanum' : function(v){
8616             return alphanum.test(v);
8617         },
8618         /**
8619          * The error text to display when the alphanumeric validation function returns false
8620          * @type String
8621          */
8622         'alphanumText' : 'This field should only contain letters, numbers and _',
8623         /**
8624          * The keystroke filter mask to be applied on alphanumeric input
8625          * @type RegExp
8626          */
8627         'alphanumMask' : /[a-z0-9_]/i
8628     };
8629 }();/*
8630  * - LGPL
8631  *
8632  * Input
8633  * 
8634  */
8635
8636 /**
8637  * @class Roo.bootstrap.Input
8638  * @extends Roo.bootstrap.Component
8639  * Bootstrap Input class
8640  * @cfg {Boolean} disabled is it disabled
8641  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8642  * @cfg {String} name name of the input
8643  * @cfg {string} fieldLabel - the label associated
8644  * @cfg {string} placeholder - placeholder to put in text.
8645  * @cfg {string}  before - input group add on before
8646  * @cfg {string} after - input group add on after
8647  * @cfg {string} size - (lg|sm) or leave empty..
8648  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8649  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8650  * @cfg {Number} md colspan out of 12 for computer-sized screens
8651  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8652  * @cfg {string} value default value of the input
8653  * @cfg {Number} labelWidth set the width of label 
8654  * @cfg {Number} labellg set the width of label (1-12)
8655  * @cfg {Number} labelmd set the width of label (1-12)
8656  * @cfg {Number} labelsm set the width of label (1-12)
8657  * @cfg {Number} labelxs set the width of label (1-12)
8658  * @cfg {String} labelAlign (top|left)
8659  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8660  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8661  * @cfg {String} indicatorpos (left|right) default left
8662  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8663  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8664
8665  * @cfg {String} align (left|center|right) Default left
8666  * @cfg {Boolean} forceFeedback (true|false) Default false
8667  * 
8668  * @constructor
8669  * Create a new Input
8670  * @param {Object} config The config object
8671  */
8672
8673 Roo.bootstrap.Input = function(config){
8674     
8675     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8676     
8677     this.addEvents({
8678         /**
8679          * @event focus
8680          * Fires when this field receives input focus.
8681          * @param {Roo.form.Field} this
8682          */
8683         focus : true,
8684         /**
8685          * @event blur
8686          * Fires when this field loses input focus.
8687          * @param {Roo.form.Field} this
8688          */
8689         blur : true,
8690         /**
8691          * @event specialkey
8692          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8693          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8694          * @param {Roo.form.Field} this
8695          * @param {Roo.EventObject} e The event object
8696          */
8697         specialkey : true,
8698         /**
8699          * @event change
8700          * Fires just before the field blurs if the field value has changed.
8701          * @param {Roo.form.Field} this
8702          * @param {Mixed} newValue The new value
8703          * @param {Mixed} oldValue The original value
8704          */
8705         change : true,
8706         /**
8707          * @event invalid
8708          * Fires after the field has been marked as invalid.
8709          * @param {Roo.form.Field} this
8710          * @param {String} msg The validation message
8711          */
8712         invalid : true,
8713         /**
8714          * @event valid
8715          * Fires after the field has been validated with no errors.
8716          * @param {Roo.form.Field} this
8717          */
8718         valid : true,
8719          /**
8720          * @event keyup
8721          * Fires after the key up
8722          * @param {Roo.form.Field} this
8723          * @param {Roo.EventObject}  e The event Object
8724          */
8725         keyup : true
8726     });
8727 };
8728
8729 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8730      /**
8731      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8732       automatic validation (defaults to "keyup").
8733      */
8734     validationEvent : "keyup",
8735      /**
8736      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8737      */
8738     validateOnBlur : true,
8739     /**
8740      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8741      */
8742     validationDelay : 250,
8743      /**
8744      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8745      */
8746     focusClass : "x-form-focus",  // not needed???
8747     
8748        
8749     /**
8750      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8751      */
8752     invalidClass : "has-warning",
8753     
8754     /**
8755      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8756      */
8757     validClass : "has-success",
8758     
8759     /**
8760      * @cfg {Boolean} hasFeedback (true|false) default true
8761      */
8762     hasFeedback : true,
8763     
8764     /**
8765      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8766      */
8767     invalidFeedbackClass : "glyphicon-warning-sign",
8768     
8769     /**
8770      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8771      */
8772     validFeedbackClass : "glyphicon-ok",
8773     
8774     /**
8775      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8776      */
8777     selectOnFocus : false,
8778     
8779      /**
8780      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8781      */
8782     maskRe : null,
8783        /**
8784      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8785      */
8786     vtype : null,
8787     
8788       /**
8789      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8790      */
8791     disableKeyFilter : false,
8792     
8793        /**
8794      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8795      */
8796     disabled : false,
8797      /**
8798      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8799      */
8800     allowBlank : true,
8801     /**
8802      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8803      */
8804     blankText : "Please complete this mandatory field",
8805     
8806      /**
8807      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8808      */
8809     minLength : 0,
8810     /**
8811      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8812      */
8813     maxLength : Number.MAX_VALUE,
8814     /**
8815      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8816      */
8817     minLengthText : "The minimum length for this field is {0}",
8818     /**
8819      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8820      */
8821     maxLengthText : "The maximum length for this field is {0}",
8822   
8823     
8824     /**
8825      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8826      * If available, this function will be called only after the basic validators all return true, and will be passed the
8827      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8828      */
8829     validator : null,
8830     /**
8831      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8832      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8833      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8834      */
8835     regex : null,
8836     /**
8837      * @cfg {String} regexText -- Depricated - use Invalid Text
8838      */
8839     regexText : "",
8840     
8841     /**
8842      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8843      */
8844     invalidText : "",
8845     
8846     
8847     
8848     autocomplete: false,
8849     
8850     
8851     fieldLabel : '',
8852     inputType : 'text',
8853     
8854     name : false,
8855     placeholder: false,
8856     before : false,
8857     after : false,
8858     size : false,
8859     hasFocus : false,
8860     preventMark: false,
8861     isFormField : true,
8862     value : '',
8863     labelWidth : 2,
8864     labelAlign : false,
8865     readOnly : false,
8866     align : false,
8867     formatedValue : false,
8868     forceFeedback : false,
8869     
8870     indicatorpos : 'left',
8871     
8872     labellg : 0,
8873     labelmd : 0,
8874     labelsm : 0,
8875     labelxs : 0,
8876     
8877     capture : '',
8878     accept : '',
8879     
8880     parentLabelAlign : function()
8881     {
8882         var parent = this;
8883         while (parent.parent()) {
8884             parent = parent.parent();
8885             if (typeof(parent.labelAlign) !='undefined') {
8886                 return parent.labelAlign;
8887             }
8888         }
8889         return 'left';
8890         
8891     },
8892     
8893     getAutoCreate : function()
8894     {
8895         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8896         
8897         var id = Roo.id();
8898         
8899         var cfg = {};
8900         
8901         if(this.inputType != 'hidden'){
8902             cfg.cls = 'form-group' //input-group
8903         }
8904         
8905         var input =  {
8906             tag: 'input',
8907             id : id,
8908             type : this.inputType,
8909             value : this.value,
8910             cls : 'form-control',
8911             placeholder : this.placeholder || '',
8912             autocomplete : this.autocomplete || 'new-password'
8913         };
8914         
8915         if(this.capture.length){
8916             input.capture = this.capture;
8917         }
8918         
8919         if(this.accept.length){
8920             input.accept = this.accept + "/*";
8921         }
8922         
8923         if(this.align){
8924             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8925         }
8926         
8927         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8928             input.maxLength = this.maxLength;
8929         }
8930         
8931         if (this.disabled) {
8932             input.disabled=true;
8933         }
8934         
8935         if (this.readOnly) {
8936             input.readonly=true;
8937         }
8938         
8939         if (this.name) {
8940             input.name = this.name;
8941         }
8942         
8943         if (this.size) {
8944             input.cls += ' input-' + this.size;
8945         }
8946         
8947         var settings=this;
8948         ['xs','sm','md','lg'].map(function(size){
8949             if (settings[size]) {
8950                 cfg.cls += ' col-' + size + '-' + settings[size];
8951             }
8952         });
8953         
8954         var inputblock = input;
8955         
8956         var feedback = {
8957             tag: 'span',
8958             cls: 'glyphicon form-control-feedback'
8959         };
8960             
8961         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8962             
8963             inputblock = {
8964                 cls : 'has-feedback',
8965                 cn :  [
8966                     input,
8967                     feedback
8968                 ] 
8969             };  
8970         }
8971         
8972         if (this.before || this.after) {
8973             
8974             inputblock = {
8975                 cls : 'input-group',
8976                 cn :  [] 
8977             };
8978             
8979             if (this.before && typeof(this.before) == 'string') {
8980                 
8981                 inputblock.cn.push({
8982                     tag :'span',
8983                     cls : 'roo-input-before input-group-addon',
8984                     html : this.before
8985                 });
8986             }
8987             if (this.before && typeof(this.before) == 'object') {
8988                 this.before = Roo.factory(this.before);
8989                 
8990                 inputblock.cn.push({
8991                     tag :'span',
8992                     cls : 'roo-input-before input-group-' +
8993                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8994                 });
8995             }
8996             
8997             inputblock.cn.push(input);
8998             
8999             if (this.after && typeof(this.after) == 'string') {
9000                 inputblock.cn.push({
9001                     tag :'span',
9002                     cls : 'roo-input-after input-group-addon',
9003                     html : this.after
9004                 });
9005             }
9006             if (this.after && typeof(this.after) == 'object') {
9007                 this.after = Roo.factory(this.after);
9008                 
9009                 inputblock.cn.push({
9010                     tag :'span',
9011                     cls : 'roo-input-after input-group-' +
9012                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9013                 });
9014             }
9015             
9016             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9017                 inputblock.cls += ' has-feedback';
9018                 inputblock.cn.push(feedback);
9019             }
9020         };
9021         
9022         if (align ==='left' && this.fieldLabel.length) {
9023             
9024             cfg.cls += ' roo-form-group-label-left';
9025             
9026             cfg.cn = [
9027                 {
9028                     tag : 'i',
9029                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9030                     tooltip : 'This field is required'
9031                 },
9032                 {
9033                     tag: 'label',
9034                     'for' :  id,
9035                     cls : 'control-label',
9036                     html : this.fieldLabel
9037
9038                 },
9039                 {
9040                     cls : "", 
9041                     cn: [
9042                         inputblock
9043                     ]
9044                 }
9045             ];
9046             
9047             var labelCfg = cfg.cn[1];
9048             var contentCfg = cfg.cn[2];
9049             
9050             if(this.indicatorpos == 'right'){
9051                 cfg.cn = [
9052                     {
9053                         tag: 'label',
9054                         'for' :  id,
9055                         cls : 'control-label',
9056                         cn : [
9057                             {
9058                                 tag : 'span',
9059                                 html : this.fieldLabel
9060                             },
9061                             {
9062                                 tag : 'i',
9063                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9064                                 tooltip : 'This field is required'
9065                             }
9066                         ]
9067                     },
9068                     {
9069                         cls : "",
9070                         cn: [
9071                             inputblock
9072                         ]
9073                     }
9074
9075                 ];
9076                 
9077                 labelCfg = cfg.cn[0];
9078                 contentCfg = cfg.cn[1];
9079             
9080             }
9081             
9082             if(this.labelWidth > 12){
9083                 labelCfg.style = "width: " + this.labelWidth + 'px';
9084             }
9085             
9086             if(this.labelWidth < 13 && this.labelmd == 0){
9087                 this.labelmd = this.labelWidth;
9088             }
9089             
9090             if(this.labellg > 0){
9091                 labelCfg.cls += ' col-lg-' + this.labellg;
9092                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9093             }
9094             
9095             if(this.labelmd > 0){
9096                 labelCfg.cls += ' col-md-' + this.labelmd;
9097                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9098             }
9099             
9100             if(this.labelsm > 0){
9101                 labelCfg.cls += ' col-sm-' + this.labelsm;
9102                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9103             }
9104             
9105             if(this.labelxs > 0){
9106                 labelCfg.cls += ' col-xs-' + this.labelxs;
9107                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9108             }
9109             
9110             
9111         } else if ( this.fieldLabel.length) {
9112                 
9113             cfg.cn = [
9114                 {
9115                     tag : 'i',
9116                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9117                     tooltip : 'This field is required'
9118                 },
9119                 {
9120                     tag: 'label',
9121                    //cls : 'input-group-addon',
9122                     html : this.fieldLabel
9123
9124                 },
9125
9126                inputblock
9127
9128            ];
9129            
9130            if(this.indicatorpos == 'right'){
9131                 
9132                 cfg.cn = [
9133                     {
9134                         tag: 'label',
9135                        //cls : 'input-group-addon',
9136                         html : this.fieldLabel
9137
9138                     },
9139                     {
9140                         tag : 'i',
9141                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9142                         tooltip : 'This field is required'
9143                     },
9144
9145                    inputblock
9146
9147                ];
9148
9149             }
9150
9151         } else {
9152             
9153             cfg.cn = [
9154
9155                     inputblock
9156
9157             ];
9158                 
9159                 
9160         };
9161         
9162         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9163            cfg.cls += ' navbar-form';
9164         }
9165         
9166         if (this.parentType === 'NavGroup') {
9167            cfg.cls += ' navbar-form';
9168            cfg.tag = 'li';
9169         }
9170         
9171         return cfg;
9172         
9173     },
9174     /**
9175      * return the real input element.
9176      */
9177     inputEl: function ()
9178     {
9179         return this.el.select('input.form-control',true).first();
9180     },
9181     
9182     tooltipEl : function()
9183     {
9184         return this.inputEl();
9185     },
9186     
9187     indicatorEl : function()
9188     {
9189         var indicator = this.el.select('i.roo-required-indicator',true).first();
9190         
9191         if(!indicator){
9192             return false;
9193         }
9194         
9195         return indicator;
9196         
9197     },
9198     
9199     setDisabled : function(v)
9200     {
9201         var i  = this.inputEl().dom;
9202         if (!v) {
9203             i.removeAttribute('disabled');
9204             return;
9205             
9206         }
9207         i.setAttribute('disabled','true');
9208     },
9209     initEvents : function()
9210     {
9211           
9212         this.inputEl().on("keydown" , this.fireKey,  this);
9213         this.inputEl().on("focus", this.onFocus,  this);
9214         this.inputEl().on("blur", this.onBlur,  this);
9215         
9216         this.inputEl().relayEvent('keyup', this);
9217         
9218         this.indicator = this.indicatorEl();
9219         
9220         if(this.indicator){
9221             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9222         }
9223  
9224         // reference to original value for reset
9225         this.originalValue = this.getValue();
9226         //Roo.form.TextField.superclass.initEvents.call(this);
9227         if(this.validationEvent == 'keyup'){
9228             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9229             this.inputEl().on('keyup', this.filterValidation, this);
9230         }
9231         else if(this.validationEvent !== false){
9232             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9233         }
9234         
9235         if(this.selectOnFocus){
9236             this.on("focus", this.preFocus, this);
9237             
9238         }
9239         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9240             this.inputEl().on("keypress", this.filterKeys, this);
9241         } else {
9242             this.inputEl().relayEvent('keypress', this);
9243         }
9244        /* if(this.grow){
9245             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9246             this.el.on("click", this.autoSize,  this);
9247         }
9248         */
9249         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9250             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9251         }
9252         
9253         if (typeof(this.before) == 'object') {
9254             this.before.render(this.el.select('.roo-input-before',true).first());
9255         }
9256         if (typeof(this.after) == 'object') {
9257             this.after.render(this.el.select('.roo-input-after',true).first());
9258         }
9259         
9260         this.inputEl().on('change', this.onChange, this);
9261         
9262     },
9263     filterValidation : function(e){
9264         if(!e.isNavKeyPress()){
9265             this.validationTask.delay(this.validationDelay);
9266         }
9267     },
9268      /**
9269      * Validates the field value
9270      * @return {Boolean} True if the value is valid, else false
9271      */
9272     validate : function(){
9273         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9274         if(this.disabled || this.validateValue(this.getRawValue())){
9275             this.markValid();
9276             return true;
9277         }
9278         
9279         this.markInvalid();
9280         return false;
9281     },
9282     
9283     
9284     /**
9285      * Validates a value according to the field's validation rules and marks the field as invalid
9286      * if the validation fails
9287      * @param {Mixed} value The value to validate
9288      * @return {Boolean} True if the value is valid, else false
9289      */
9290     validateValue : function(value)
9291     {
9292         if(this.getVisibilityEl().hasClass('hidden')){
9293             return true;
9294         }
9295         
9296         if(value.length < 1)  { // if it's blank
9297             if(this.allowBlank){
9298                 return true;
9299             }
9300             return false;
9301         }
9302         
9303         if(value.length < this.minLength){
9304             return false;
9305         }
9306         if(value.length > this.maxLength){
9307             return false;
9308         }
9309         if(this.vtype){
9310             var vt = Roo.form.VTypes;
9311             if(!vt[this.vtype](value, this)){
9312                 return false;
9313             }
9314         }
9315         if(typeof this.validator == "function"){
9316             var msg = this.validator(value);
9317             if(msg !== true){
9318                 return false;
9319             }
9320             if (typeof(msg) == 'string') {
9321                 this.invalidText = msg;
9322             }
9323         }
9324         
9325         if(this.regex && !this.regex.test(value)){
9326             return false;
9327         }
9328         
9329         return true;
9330     },
9331     
9332      // private
9333     fireKey : function(e){
9334         //Roo.log('field ' + e.getKey());
9335         if(e.isNavKeyPress()){
9336             this.fireEvent("specialkey", this, e);
9337         }
9338     },
9339     focus : function (selectText){
9340         if(this.rendered){
9341             this.inputEl().focus();
9342             if(selectText === true){
9343                 this.inputEl().dom.select();
9344             }
9345         }
9346         return this;
9347     } ,
9348     
9349     onFocus : function(){
9350         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9351            // this.el.addClass(this.focusClass);
9352         }
9353         if(!this.hasFocus){
9354             this.hasFocus = true;
9355             this.startValue = this.getValue();
9356             this.fireEvent("focus", this);
9357         }
9358     },
9359     
9360     beforeBlur : Roo.emptyFn,
9361
9362     
9363     // private
9364     onBlur : function(){
9365         this.beforeBlur();
9366         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9367             //this.el.removeClass(this.focusClass);
9368         }
9369         this.hasFocus = false;
9370         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9371             this.validate();
9372         }
9373         var v = this.getValue();
9374         if(String(v) !== String(this.startValue)){
9375             this.fireEvent('change', this, v, this.startValue);
9376         }
9377         this.fireEvent("blur", this);
9378     },
9379     
9380     onChange : function(e)
9381     {
9382         var v = this.getValue();
9383         if(String(v) !== String(this.startValue)){
9384             this.fireEvent('change', this, v, this.startValue);
9385         }
9386         
9387     },
9388     
9389     /**
9390      * Resets the current field value to the originally loaded value and clears any validation messages
9391      */
9392     reset : function(){
9393         this.setValue(this.originalValue);
9394         this.validate();
9395     },
9396      /**
9397      * Returns the name of the field
9398      * @return {Mixed} name The name field
9399      */
9400     getName: function(){
9401         return this.name;
9402     },
9403      /**
9404      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9405      * @return {Mixed} value The field value
9406      */
9407     getValue : function(){
9408         
9409         var v = this.inputEl().getValue();
9410         
9411         return v;
9412     },
9413     /**
9414      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9415      * @return {Mixed} value The field value
9416      */
9417     getRawValue : function(){
9418         var v = this.inputEl().getValue();
9419         
9420         return v;
9421     },
9422     
9423     /**
9424      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9425      * @param {Mixed} value The value to set
9426      */
9427     setRawValue : function(v){
9428         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9429     },
9430     
9431     selectText : function(start, end){
9432         var v = this.getRawValue();
9433         if(v.length > 0){
9434             start = start === undefined ? 0 : start;
9435             end = end === undefined ? v.length : end;
9436             var d = this.inputEl().dom;
9437             if(d.setSelectionRange){
9438                 d.setSelectionRange(start, end);
9439             }else if(d.createTextRange){
9440                 var range = d.createTextRange();
9441                 range.moveStart("character", start);
9442                 range.moveEnd("character", v.length-end);
9443                 range.select();
9444             }
9445         }
9446     },
9447     
9448     /**
9449      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9450      * @param {Mixed} value The value to set
9451      */
9452     setValue : function(v){
9453         this.value = v;
9454         if(this.rendered){
9455             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9456             this.validate();
9457         }
9458     },
9459     
9460     /*
9461     processValue : function(value){
9462         if(this.stripCharsRe){
9463             var newValue = value.replace(this.stripCharsRe, '');
9464             if(newValue !== value){
9465                 this.setRawValue(newValue);
9466                 return newValue;
9467             }
9468         }
9469         return value;
9470     },
9471   */
9472     preFocus : function(){
9473         
9474         if(this.selectOnFocus){
9475             this.inputEl().dom.select();
9476         }
9477     },
9478     filterKeys : function(e){
9479         var k = e.getKey();
9480         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9481             return;
9482         }
9483         var c = e.getCharCode(), cc = String.fromCharCode(c);
9484         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9485             return;
9486         }
9487         if(!this.maskRe.test(cc)){
9488             e.stopEvent();
9489         }
9490     },
9491      /**
9492      * Clear any invalid styles/messages for this field
9493      */
9494     clearInvalid : function(){
9495         
9496         if(!this.el || this.preventMark){ // not rendered
9497             return;
9498         }
9499         
9500      
9501         this.el.removeClass(this.invalidClass);
9502         
9503         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9504             
9505             var feedback = this.el.select('.form-control-feedback', true).first();
9506             
9507             if(feedback){
9508                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9509             }
9510             
9511         }
9512         
9513         if(this.indicator){
9514             this.indicator.removeClass('visible');
9515             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9516         }
9517         
9518         this.fireEvent('valid', this);
9519     },
9520     
9521      /**
9522      * Mark this field as valid
9523      */
9524     markValid : function()
9525     {
9526         if(!this.el  || this.preventMark){ // not rendered...
9527             return;
9528         }
9529         
9530         this.el.removeClass([this.invalidClass, this.validClass]);
9531         
9532         var feedback = this.el.select('.form-control-feedback', true).first();
9533             
9534         if(feedback){
9535             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9536         }
9537         
9538         if(this.indicator){
9539             this.indicator.removeClass('visible');
9540             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9541         }
9542         
9543         if(this.disabled){
9544             return;
9545         }
9546         
9547         if(this.allowBlank && !this.getRawValue().length){
9548             return;
9549         }
9550         
9551         this.el.addClass(this.validClass);
9552         
9553         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9554             
9555             var feedback = this.el.select('.form-control-feedback', true).first();
9556             
9557             if(feedback){
9558                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9559                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9560             }
9561             
9562         }
9563         
9564         this.fireEvent('valid', this);
9565     },
9566     
9567      /**
9568      * Mark this field as invalid
9569      * @param {String} msg The validation message
9570      */
9571     markInvalid : function(msg)
9572     {
9573         if(!this.el  || this.preventMark){ // not rendered
9574             return;
9575         }
9576         
9577         this.el.removeClass([this.invalidClass, this.validClass]);
9578         
9579         var feedback = this.el.select('.form-control-feedback', true).first();
9580             
9581         if(feedback){
9582             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9583         }
9584
9585         if(this.disabled){
9586             return;
9587         }
9588         
9589         if(this.allowBlank && !this.getRawValue().length){
9590             return;
9591         }
9592         
9593         if(this.indicator){
9594             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9595             this.indicator.addClass('visible');
9596         }
9597         
9598         this.el.addClass(this.invalidClass);
9599         
9600         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9601             
9602             var feedback = this.el.select('.form-control-feedback', true).first();
9603             
9604             if(feedback){
9605                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9606                 
9607                 if(this.getValue().length || this.forceFeedback){
9608                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9609                 }
9610                 
9611             }
9612             
9613         }
9614         
9615         this.fireEvent('invalid', this, msg);
9616     },
9617     // private
9618     SafariOnKeyDown : function(event)
9619     {
9620         // this is a workaround for a password hang bug on chrome/ webkit.
9621         if (this.inputEl().dom.type != 'password') {
9622             return;
9623         }
9624         
9625         var isSelectAll = false;
9626         
9627         if(this.inputEl().dom.selectionEnd > 0){
9628             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9629         }
9630         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9631             event.preventDefault();
9632             this.setValue('');
9633             return;
9634         }
9635         
9636         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9637             
9638             event.preventDefault();
9639             // this is very hacky as keydown always get's upper case.
9640             //
9641             var cc = String.fromCharCode(event.getCharCode());
9642             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9643             
9644         }
9645     },
9646     adjustWidth : function(tag, w){
9647         tag = tag.toLowerCase();
9648         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9649             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9650                 if(tag == 'input'){
9651                     return w + 2;
9652                 }
9653                 if(tag == 'textarea'){
9654                     return w-2;
9655                 }
9656             }else if(Roo.isOpera){
9657                 if(tag == 'input'){
9658                     return w + 2;
9659                 }
9660                 if(tag == 'textarea'){
9661                     return w-2;
9662                 }
9663             }
9664         }
9665         return w;
9666     },
9667     
9668     setFieldLabel : function(v)
9669     {
9670         if(!this.rendered){
9671             return;
9672         }
9673         
9674         if(this.indicator){
9675             var ar = this.el.select('label > span',true);
9676             
9677             if (ar.elements.length) {
9678                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9679                 this.fieldLabel = v;
9680                 return;
9681             }
9682             
9683             var br = this.el.select('label',true);
9684             
9685             if(br.elements.length) {
9686                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9687                 this.fieldLabel = v;
9688                 return;
9689             }
9690             
9691             Roo.log('Cannot Found any of label > span || label in input');
9692             return;
9693         }
9694         
9695         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9696         this.fieldLabel = v;
9697         
9698         
9699     }
9700 });
9701
9702  
9703 /*
9704  * - LGPL
9705  *
9706  * Input
9707  * 
9708  */
9709
9710 /**
9711  * @class Roo.bootstrap.TextArea
9712  * @extends Roo.bootstrap.Input
9713  * Bootstrap TextArea class
9714  * @cfg {Number} cols Specifies the visible width of a text area
9715  * @cfg {Number} rows Specifies the visible number of lines in a text area
9716  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9717  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9718  * @cfg {string} html text
9719  * 
9720  * @constructor
9721  * Create a new TextArea
9722  * @param {Object} config The config object
9723  */
9724
9725 Roo.bootstrap.TextArea = function(config){
9726     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9727    
9728 };
9729
9730 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9731      
9732     cols : false,
9733     rows : 5,
9734     readOnly : false,
9735     warp : 'soft',
9736     resize : false,
9737     value: false,
9738     html: false,
9739     
9740     getAutoCreate : function(){
9741         
9742         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9743         
9744         var id = Roo.id();
9745         
9746         var cfg = {};
9747         
9748         if(this.inputType != 'hidden'){
9749             cfg.cls = 'form-group' //input-group
9750         }
9751         
9752         var input =  {
9753             tag: 'textarea',
9754             id : id,
9755             warp : this.warp,
9756             rows : this.rows,
9757             value : this.value || '',
9758             html: this.html || '',
9759             cls : 'form-control',
9760             placeholder : this.placeholder || '' 
9761             
9762         };
9763         
9764         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9765             input.maxLength = this.maxLength;
9766         }
9767         
9768         if(this.resize){
9769             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9770         }
9771         
9772         if(this.cols){
9773             input.cols = this.cols;
9774         }
9775         
9776         if (this.readOnly) {
9777             input.readonly = true;
9778         }
9779         
9780         if (this.name) {
9781             input.name = this.name;
9782         }
9783         
9784         if (this.size) {
9785             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9786         }
9787         
9788         var settings=this;
9789         ['xs','sm','md','lg'].map(function(size){
9790             if (settings[size]) {
9791                 cfg.cls += ' col-' + size + '-' + settings[size];
9792             }
9793         });
9794         
9795         var inputblock = input;
9796         
9797         if(this.hasFeedback && !this.allowBlank){
9798             
9799             var feedback = {
9800                 tag: 'span',
9801                 cls: 'glyphicon form-control-feedback'
9802             };
9803
9804             inputblock = {
9805                 cls : 'has-feedback',
9806                 cn :  [
9807                     input,
9808                     feedback
9809                 ] 
9810             };  
9811         }
9812         
9813         
9814         if (this.before || this.after) {
9815             
9816             inputblock = {
9817                 cls : 'input-group',
9818                 cn :  [] 
9819             };
9820             if (this.before) {
9821                 inputblock.cn.push({
9822                     tag :'span',
9823                     cls : 'input-group-addon',
9824                     html : this.before
9825                 });
9826             }
9827             
9828             inputblock.cn.push(input);
9829             
9830             if(this.hasFeedback && !this.allowBlank){
9831                 inputblock.cls += ' has-feedback';
9832                 inputblock.cn.push(feedback);
9833             }
9834             
9835             if (this.after) {
9836                 inputblock.cn.push({
9837                     tag :'span',
9838                     cls : 'input-group-addon',
9839                     html : this.after
9840                 });
9841             }
9842             
9843         }
9844         
9845         if (align ==='left' && this.fieldLabel.length) {
9846             cfg.cn = [
9847                 {
9848                     tag: 'label',
9849                     'for' :  id,
9850                     cls : 'control-label',
9851                     html : this.fieldLabel
9852                 },
9853                 {
9854                     cls : "",
9855                     cn: [
9856                         inputblock
9857                     ]
9858                 }
9859
9860             ];
9861             
9862             if(this.labelWidth > 12){
9863                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9864             }
9865
9866             if(this.labelWidth < 13 && this.labelmd == 0){
9867                 this.labelmd = this.labelWidth;
9868             }
9869
9870             if(this.labellg > 0){
9871                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9872                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9873             }
9874
9875             if(this.labelmd > 0){
9876                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9877                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9878             }
9879
9880             if(this.labelsm > 0){
9881                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9882                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9883             }
9884
9885             if(this.labelxs > 0){
9886                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9887                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9888             }
9889             
9890         } else if ( this.fieldLabel.length) {
9891             cfg.cn = [
9892
9893                {
9894                    tag: 'label',
9895                    //cls : 'input-group-addon',
9896                    html : this.fieldLabel
9897
9898                },
9899
9900                inputblock
9901
9902            ];
9903
9904         } else {
9905
9906             cfg.cn = [
9907
9908                 inputblock
9909
9910             ];
9911                 
9912         }
9913         
9914         if (this.disabled) {
9915             input.disabled=true;
9916         }
9917         
9918         return cfg;
9919         
9920     },
9921     /**
9922      * return the real textarea element.
9923      */
9924     inputEl: function ()
9925     {
9926         return this.el.select('textarea.form-control',true).first();
9927     },
9928     
9929     /**
9930      * Clear any invalid styles/messages for this field
9931      */
9932     clearInvalid : function()
9933     {
9934         
9935         if(!this.el || this.preventMark){ // not rendered
9936             return;
9937         }
9938         
9939         var label = this.el.select('label', true).first();
9940         var icon = this.el.select('i.fa-star', true).first();
9941         
9942         if(label && icon){
9943             icon.remove();
9944         }
9945         
9946         this.el.removeClass(this.invalidClass);
9947         
9948         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9949             
9950             var feedback = this.el.select('.form-control-feedback', true).first();
9951             
9952             if(feedback){
9953                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9954             }
9955             
9956         }
9957         
9958         this.fireEvent('valid', this);
9959     },
9960     
9961      /**
9962      * Mark this field as valid
9963      */
9964     markValid : function()
9965     {
9966         if(!this.el  || this.preventMark){ // not rendered
9967             return;
9968         }
9969         
9970         this.el.removeClass([this.invalidClass, this.validClass]);
9971         
9972         var feedback = this.el.select('.form-control-feedback', true).first();
9973             
9974         if(feedback){
9975             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9976         }
9977
9978         if(this.disabled || this.allowBlank){
9979             return;
9980         }
9981         
9982         var label = this.el.select('label', true).first();
9983         var icon = this.el.select('i.fa-star', true).first();
9984         
9985         if(label && icon){
9986             icon.remove();
9987         }
9988         
9989         this.el.addClass(this.validClass);
9990         
9991         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9992             
9993             var feedback = this.el.select('.form-control-feedback', true).first();
9994             
9995             if(feedback){
9996                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9997                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9998             }
9999             
10000         }
10001         
10002         this.fireEvent('valid', this);
10003     },
10004     
10005      /**
10006      * Mark this field as invalid
10007      * @param {String} msg The validation message
10008      */
10009     markInvalid : function(msg)
10010     {
10011         if(!this.el  || this.preventMark){ // not rendered
10012             return;
10013         }
10014         
10015         this.el.removeClass([this.invalidClass, this.validClass]);
10016         
10017         var feedback = this.el.select('.form-control-feedback', true).first();
10018             
10019         if(feedback){
10020             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10021         }
10022
10023         if(this.disabled || this.allowBlank){
10024             return;
10025         }
10026         
10027         var label = this.el.select('label', true).first();
10028         var icon = this.el.select('i.fa-star', true).first();
10029         
10030         if(!this.getValue().length && label && !icon){
10031             this.el.createChild({
10032                 tag : 'i',
10033                 cls : 'text-danger fa fa-lg fa-star',
10034                 tooltip : 'This field is required',
10035                 style : 'margin-right:5px;'
10036             }, label, true);
10037         }
10038
10039         this.el.addClass(this.invalidClass);
10040         
10041         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10042             
10043             var feedback = this.el.select('.form-control-feedback', true).first();
10044             
10045             if(feedback){
10046                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10047                 
10048                 if(this.getValue().length || this.forceFeedback){
10049                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10050                 }
10051                 
10052             }
10053             
10054         }
10055         
10056         this.fireEvent('invalid', this, msg);
10057     }
10058 });
10059
10060  
10061 /*
10062  * - LGPL
10063  *
10064  * trigger field - base class for combo..
10065  * 
10066  */
10067  
10068 /**
10069  * @class Roo.bootstrap.TriggerField
10070  * @extends Roo.bootstrap.Input
10071  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10072  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10073  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10074  * for which you can provide a custom implementation.  For example:
10075  * <pre><code>
10076 var trigger = new Roo.bootstrap.TriggerField();
10077 trigger.onTriggerClick = myTriggerFn;
10078 trigger.applyTo('my-field');
10079 </code></pre>
10080  *
10081  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10082  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10083  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10084  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10085  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10086
10087  * @constructor
10088  * Create a new TriggerField.
10089  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10090  * to the base TextField)
10091  */
10092 Roo.bootstrap.TriggerField = function(config){
10093     this.mimicing = false;
10094     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10095 };
10096
10097 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10098     /**
10099      * @cfg {String} triggerClass A CSS class to apply to the trigger
10100      */
10101      /**
10102      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10103      */
10104     hideTrigger:false,
10105
10106     /**
10107      * @cfg {Boolean} removable (true|false) special filter default false
10108      */
10109     removable : false,
10110     
10111     /** @cfg {Boolean} grow @hide */
10112     /** @cfg {Number} growMin @hide */
10113     /** @cfg {Number} growMax @hide */
10114
10115     /**
10116      * @hide 
10117      * @method
10118      */
10119     autoSize: Roo.emptyFn,
10120     // private
10121     monitorTab : true,
10122     // private
10123     deferHeight : true,
10124
10125     
10126     actionMode : 'wrap',
10127     
10128     caret : false,
10129     
10130     
10131     getAutoCreate : function(){
10132        
10133         var align = this.labelAlign || this.parentLabelAlign();
10134         
10135         var id = Roo.id();
10136         
10137         var cfg = {
10138             cls: 'form-group' //input-group
10139         };
10140         
10141         
10142         var input =  {
10143             tag: 'input',
10144             id : id,
10145             type : this.inputType,
10146             cls : 'form-control',
10147             autocomplete: 'new-password',
10148             placeholder : this.placeholder || '' 
10149             
10150         };
10151         if (this.name) {
10152             input.name = this.name;
10153         }
10154         if (this.size) {
10155             input.cls += ' input-' + this.size;
10156         }
10157         
10158         if (this.disabled) {
10159             input.disabled=true;
10160         }
10161         
10162         var inputblock = input;
10163         
10164         if(this.hasFeedback && !this.allowBlank){
10165             
10166             var feedback = {
10167                 tag: 'span',
10168                 cls: 'glyphicon form-control-feedback'
10169             };
10170             
10171             if(this.removable && !this.editable && !this.tickable){
10172                 inputblock = {
10173                     cls : 'has-feedback',
10174                     cn :  [
10175                         inputblock,
10176                         {
10177                             tag: 'button',
10178                             html : 'x',
10179                             cls : 'roo-combo-removable-btn close'
10180                         },
10181                         feedback
10182                     ] 
10183                 };
10184             } else {
10185                 inputblock = {
10186                     cls : 'has-feedback',
10187                     cn :  [
10188                         inputblock,
10189                         feedback
10190                     ] 
10191                 };
10192             }
10193
10194         } else {
10195             if(this.removable && !this.editable && !this.tickable){
10196                 inputblock = {
10197                     cls : 'roo-removable',
10198                     cn :  [
10199                         inputblock,
10200                         {
10201                             tag: 'button',
10202                             html : 'x',
10203                             cls : 'roo-combo-removable-btn close'
10204                         }
10205                     ] 
10206                 };
10207             }
10208         }
10209         
10210         if (this.before || this.after) {
10211             
10212             inputblock = {
10213                 cls : 'input-group',
10214                 cn :  [] 
10215             };
10216             if (this.before) {
10217                 inputblock.cn.push({
10218                     tag :'span',
10219                     cls : 'input-group-addon',
10220                     html : this.before
10221                 });
10222             }
10223             
10224             inputblock.cn.push(input);
10225             
10226             if(this.hasFeedback && !this.allowBlank){
10227                 inputblock.cls += ' has-feedback';
10228                 inputblock.cn.push(feedback);
10229             }
10230             
10231             if (this.after) {
10232                 inputblock.cn.push({
10233                     tag :'span',
10234                     cls : 'input-group-addon',
10235                     html : this.after
10236                 });
10237             }
10238             
10239         };
10240         
10241         var box = {
10242             tag: 'div',
10243             cn: [
10244                 {
10245                     tag: 'input',
10246                     type : 'hidden',
10247                     cls: 'form-hidden-field'
10248                 },
10249                 inputblock
10250             ]
10251             
10252         };
10253         
10254         if(this.multiple){
10255             box = {
10256                 tag: 'div',
10257                 cn: [
10258                     {
10259                         tag: 'input',
10260                         type : 'hidden',
10261                         cls: 'form-hidden-field'
10262                     },
10263                     {
10264                         tag: 'ul',
10265                         cls: 'roo-select2-choices',
10266                         cn:[
10267                             {
10268                                 tag: 'li',
10269                                 cls: 'roo-select2-search-field',
10270                                 cn: [
10271
10272                                     inputblock
10273                                 ]
10274                             }
10275                         ]
10276                     }
10277                 ]
10278             }
10279         };
10280         
10281         var combobox = {
10282             cls: 'roo-select2-container input-group',
10283             cn: [
10284                 box
10285 //                {
10286 //                    tag: 'ul',
10287 //                    cls: 'typeahead typeahead-long dropdown-menu',
10288 //                    style: 'display:none'
10289 //                }
10290             ]
10291         };
10292         
10293         if(!this.multiple && this.showToggleBtn){
10294             
10295             var caret = {
10296                         tag: 'span',
10297                         cls: 'caret'
10298              };
10299             if (this.caret != false) {
10300                 caret = {
10301                      tag: 'i',
10302                      cls: 'fa fa-' + this.caret
10303                 };
10304                 
10305             }
10306             
10307             combobox.cn.push({
10308                 tag :'span',
10309                 cls : 'input-group-addon btn dropdown-toggle',
10310                 cn : [
10311                     caret,
10312                     {
10313                         tag: 'span',
10314                         cls: 'combobox-clear',
10315                         cn  : [
10316                             {
10317                                 tag : 'i',
10318                                 cls: 'icon-remove'
10319                             }
10320                         ]
10321                     }
10322                 ]
10323
10324             })
10325         }
10326         
10327         if(this.multiple){
10328             combobox.cls += ' roo-select2-container-multi';
10329         }
10330         
10331         if (align ==='left' && this.fieldLabel.length) {
10332             
10333             cfg.cls += ' roo-form-group-label-left';
10334
10335             cfg.cn = [
10336                 {
10337                     tag : 'i',
10338                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10339                     tooltip : 'This field is required'
10340                 },
10341                 {
10342                     tag: 'label',
10343                     'for' :  id,
10344                     cls : 'control-label',
10345                     html : this.fieldLabel
10346
10347                 },
10348                 {
10349                     cls : "", 
10350                     cn: [
10351                         combobox
10352                     ]
10353                 }
10354
10355             ];
10356             
10357             var labelCfg = cfg.cn[1];
10358             var contentCfg = cfg.cn[2];
10359             
10360             if(this.indicatorpos == 'right'){
10361                 cfg.cn = [
10362                     {
10363                         tag: 'label',
10364                         'for' :  id,
10365                         cls : 'control-label',
10366                         cn : [
10367                             {
10368                                 tag : 'span',
10369                                 html : this.fieldLabel
10370                             },
10371                             {
10372                                 tag : 'i',
10373                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10374                                 tooltip : 'This field is required'
10375                             }
10376                         ]
10377                     },
10378                     {
10379                         cls : "", 
10380                         cn: [
10381                             combobox
10382                         ]
10383                     }
10384
10385                 ];
10386                 
10387                 labelCfg = cfg.cn[0];
10388                 contentCfg = cfg.cn[1];
10389             }
10390             
10391             if(this.labelWidth > 12){
10392                 labelCfg.style = "width: " + this.labelWidth + 'px';
10393             }
10394             
10395             if(this.labelWidth < 13 && this.labelmd == 0){
10396                 this.labelmd = this.labelWidth;
10397             }
10398             
10399             if(this.labellg > 0){
10400                 labelCfg.cls += ' col-lg-' + this.labellg;
10401                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10402             }
10403             
10404             if(this.labelmd > 0){
10405                 labelCfg.cls += ' col-md-' + this.labelmd;
10406                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10407             }
10408             
10409             if(this.labelsm > 0){
10410                 labelCfg.cls += ' col-sm-' + this.labelsm;
10411                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10412             }
10413             
10414             if(this.labelxs > 0){
10415                 labelCfg.cls += ' col-xs-' + this.labelxs;
10416                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10417             }
10418             
10419         } else if ( this.fieldLabel.length) {
10420 //                Roo.log(" label");
10421             cfg.cn = [
10422                 {
10423                    tag : 'i',
10424                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10425                    tooltip : 'This field is required'
10426                },
10427                {
10428                    tag: 'label',
10429                    //cls : 'input-group-addon',
10430                    html : this.fieldLabel
10431
10432                },
10433
10434                combobox
10435
10436             ];
10437             
10438             if(this.indicatorpos == 'right'){
10439                 
10440                 cfg.cn = [
10441                     {
10442                        tag: 'label',
10443                        cn : [
10444                            {
10445                                tag : 'span',
10446                                html : this.fieldLabel
10447                            },
10448                            {
10449                               tag : 'i',
10450                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10451                               tooltip : 'This field is required'
10452                            }
10453                        ]
10454
10455                     },
10456                     combobox
10457
10458                 ];
10459
10460             }
10461
10462         } else {
10463             
10464 //                Roo.log(" no label && no align");
10465                 cfg = combobox
10466                      
10467                 
10468         }
10469         
10470         var settings=this;
10471         ['xs','sm','md','lg'].map(function(size){
10472             if (settings[size]) {
10473                 cfg.cls += ' col-' + size + '-' + settings[size];
10474             }
10475         });
10476         
10477         return cfg;
10478         
10479     },
10480     
10481     
10482     
10483     // private
10484     onResize : function(w, h){
10485 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10486 //        if(typeof w == 'number'){
10487 //            var x = w - this.trigger.getWidth();
10488 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10489 //            this.trigger.setStyle('left', x+'px');
10490 //        }
10491     },
10492
10493     // private
10494     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10495
10496     // private
10497     getResizeEl : function(){
10498         return this.inputEl();
10499     },
10500
10501     // private
10502     getPositionEl : function(){
10503         return this.inputEl();
10504     },
10505
10506     // private
10507     alignErrorIcon : function(){
10508         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10509     },
10510
10511     // private
10512     initEvents : function(){
10513         
10514         this.createList();
10515         
10516         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10517         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10518         if(!this.multiple && this.showToggleBtn){
10519             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10520             if(this.hideTrigger){
10521                 this.trigger.setDisplayed(false);
10522             }
10523             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10524         }
10525         
10526         if(this.multiple){
10527             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10528         }
10529         
10530         if(this.removable && !this.editable && !this.tickable){
10531             var close = this.closeTriggerEl();
10532             
10533             if(close){
10534                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10535                 close.on('click', this.removeBtnClick, this, close);
10536             }
10537         }
10538         
10539         //this.trigger.addClassOnOver('x-form-trigger-over');
10540         //this.trigger.addClassOnClick('x-form-trigger-click');
10541         
10542         //if(!this.width){
10543         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10544         //}
10545     },
10546     
10547     closeTriggerEl : function()
10548     {
10549         var close = this.el.select('.roo-combo-removable-btn', true).first();
10550         return close ? close : false;
10551     },
10552     
10553     removeBtnClick : function(e, h, el)
10554     {
10555         e.preventDefault();
10556         
10557         if(this.fireEvent("remove", this) !== false){
10558             this.reset();
10559             this.fireEvent("afterremove", this)
10560         }
10561     },
10562     
10563     createList : function()
10564     {
10565         this.list = Roo.get(document.body).createChild({
10566             tag: 'ul',
10567             cls: 'typeahead typeahead-long dropdown-menu',
10568             style: 'display:none'
10569         });
10570         
10571         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10572         
10573     },
10574
10575     // private
10576     initTrigger : function(){
10577        
10578     },
10579
10580     // private
10581     onDestroy : function(){
10582         if(this.trigger){
10583             this.trigger.removeAllListeners();
10584           //  this.trigger.remove();
10585         }
10586         //if(this.wrap){
10587         //    this.wrap.remove();
10588         //}
10589         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10590     },
10591
10592     // private
10593     onFocus : function(){
10594         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10595         /*
10596         if(!this.mimicing){
10597             this.wrap.addClass('x-trigger-wrap-focus');
10598             this.mimicing = true;
10599             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10600             if(this.monitorTab){
10601                 this.el.on("keydown", this.checkTab, this);
10602             }
10603         }
10604         */
10605     },
10606
10607     // private
10608     checkTab : function(e){
10609         if(e.getKey() == e.TAB){
10610             this.triggerBlur();
10611         }
10612     },
10613
10614     // private
10615     onBlur : function(){
10616         // do nothing
10617     },
10618
10619     // private
10620     mimicBlur : function(e, t){
10621         /*
10622         if(!this.wrap.contains(t) && this.validateBlur()){
10623             this.triggerBlur();
10624         }
10625         */
10626     },
10627
10628     // private
10629     triggerBlur : function(){
10630         this.mimicing = false;
10631         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10632         if(this.monitorTab){
10633             this.el.un("keydown", this.checkTab, this);
10634         }
10635         //this.wrap.removeClass('x-trigger-wrap-focus');
10636         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10637     },
10638
10639     // private
10640     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10641     validateBlur : function(e, t){
10642         return true;
10643     },
10644
10645     // private
10646     onDisable : function(){
10647         this.inputEl().dom.disabled = true;
10648         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10649         //if(this.wrap){
10650         //    this.wrap.addClass('x-item-disabled');
10651         //}
10652     },
10653
10654     // private
10655     onEnable : function(){
10656         this.inputEl().dom.disabled = false;
10657         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10658         //if(this.wrap){
10659         //    this.el.removeClass('x-item-disabled');
10660         //}
10661     },
10662
10663     // private
10664     onShow : function(){
10665         var ae = this.getActionEl();
10666         
10667         if(ae){
10668             ae.dom.style.display = '';
10669             ae.dom.style.visibility = 'visible';
10670         }
10671     },
10672
10673     // private
10674     
10675     onHide : function(){
10676         var ae = this.getActionEl();
10677         ae.dom.style.display = 'none';
10678     },
10679
10680     /**
10681      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10682      * by an implementing function.
10683      * @method
10684      * @param {EventObject} e
10685      */
10686     onTriggerClick : Roo.emptyFn
10687 });
10688  /*
10689  * Based on:
10690  * Ext JS Library 1.1.1
10691  * Copyright(c) 2006-2007, Ext JS, LLC.
10692  *
10693  * Originally Released Under LGPL - original licence link has changed is not relivant.
10694  *
10695  * Fork - LGPL
10696  * <script type="text/javascript">
10697  */
10698
10699
10700 /**
10701  * @class Roo.data.SortTypes
10702  * @singleton
10703  * Defines the default sorting (casting?) comparison functions used when sorting data.
10704  */
10705 Roo.data.SortTypes = {
10706     /**
10707      * Default sort that does nothing
10708      * @param {Mixed} s The value being converted
10709      * @return {Mixed} The comparison value
10710      */
10711     none : function(s){
10712         return s;
10713     },
10714     
10715     /**
10716      * The regular expression used to strip tags
10717      * @type {RegExp}
10718      * @property
10719      */
10720     stripTagsRE : /<\/?[^>]+>/gi,
10721     
10722     /**
10723      * Strips all HTML tags to sort on text only
10724      * @param {Mixed} s The value being converted
10725      * @return {String} The comparison value
10726      */
10727     asText : function(s){
10728         return String(s).replace(this.stripTagsRE, "");
10729     },
10730     
10731     /**
10732      * Strips all HTML tags to sort on text only - Case insensitive
10733      * @param {Mixed} s The value being converted
10734      * @return {String} The comparison value
10735      */
10736     asUCText : function(s){
10737         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10738     },
10739     
10740     /**
10741      * Case insensitive string
10742      * @param {Mixed} s The value being converted
10743      * @return {String} The comparison value
10744      */
10745     asUCString : function(s) {
10746         return String(s).toUpperCase();
10747     },
10748     
10749     /**
10750      * Date sorting
10751      * @param {Mixed} s The value being converted
10752      * @return {Number} The comparison value
10753      */
10754     asDate : function(s) {
10755         if(!s){
10756             return 0;
10757         }
10758         if(s instanceof Date){
10759             return s.getTime();
10760         }
10761         return Date.parse(String(s));
10762     },
10763     
10764     /**
10765      * Float sorting
10766      * @param {Mixed} s The value being converted
10767      * @return {Float} The comparison value
10768      */
10769     asFloat : function(s) {
10770         var val = parseFloat(String(s).replace(/,/g, ""));
10771         if(isNaN(val)) {
10772             val = 0;
10773         }
10774         return val;
10775     },
10776     
10777     /**
10778      * Integer sorting
10779      * @param {Mixed} s The value being converted
10780      * @return {Number} The comparison value
10781      */
10782     asInt : function(s) {
10783         var val = parseInt(String(s).replace(/,/g, ""));
10784         if(isNaN(val)) {
10785             val = 0;
10786         }
10787         return val;
10788     }
10789 };/*
10790  * Based on:
10791  * Ext JS Library 1.1.1
10792  * Copyright(c) 2006-2007, Ext JS, LLC.
10793  *
10794  * Originally Released Under LGPL - original licence link has changed is not relivant.
10795  *
10796  * Fork - LGPL
10797  * <script type="text/javascript">
10798  */
10799
10800 /**
10801 * @class Roo.data.Record
10802  * Instances of this class encapsulate both record <em>definition</em> information, and record
10803  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10804  * to access Records cached in an {@link Roo.data.Store} object.<br>
10805  * <p>
10806  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10807  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10808  * objects.<br>
10809  * <p>
10810  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10811  * @constructor
10812  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10813  * {@link #create}. The parameters are the same.
10814  * @param {Array} data An associative Array of data values keyed by the field name.
10815  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10816  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10817  * not specified an integer id is generated.
10818  */
10819 Roo.data.Record = function(data, id){
10820     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10821     this.data = data;
10822 };
10823
10824 /**
10825  * Generate a constructor for a specific record layout.
10826  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10827  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10828  * Each field definition object may contain the following properties: <ul>
10829  * <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,
10830  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10831  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10832  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10833  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10834  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10835  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10836  * this may be omitted.</p></li>
10837  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10838  * <ul><li>auto (Default, implies no conversion)</li>
10839  * <li>string</li>
10840  * <li>int</li>
10841  * <li>float</li>
10842  * <li>boolean</li>
10843  * <li>date</li></ul></p></li>
10844  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10845  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10846  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10847  * by the Reader into an object that will be stored in the Record. It is passed the
10848  * following parameters:<ul>
10849  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10850  * </ul></p></li>
10851  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10852  * </ul>
10853  * <br>usage:<br><pre><code>
10854 var TopicRecord = Roo.data.Record.create(
10855     {name: 'title', mapping: 'topic_title'},
10856     {name: 'author', mapping: 'username'},
10857     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10858     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10859     {name: 'lastPoster', mapping: 'user2'},
10860     {name: 'excerpt', mapping: 'post_text'}
10861 );
10862
10863 var myNewRecord = new TopicRecord({
10864     title: 'Do my job please',
10865     author: 'noobie',
10866     totalPosts: 1,
10867     lastPost: new Date(),
10868     lastPoster: 'Animal',
10869     excerpt: 'No way dude!'
10870 });
10871 myStore.add(myNewRecord);
10872 </code></pre>
10873  * @method create
10874  * @static
10875  */
10876 Roo.data.Record.create = function(o){
10877     var f = function(){
10878         f.superclass.constructor.apply(this, arguments);
10879     };
10880     Roo.extend(f, Roo.data.Record);
10881     var p = f.prototype;
10882     p.fields = new Roo.util.MixedCollection(false, function(field){
10883         return field.name;
10884     });
10885     for(var i = 0, len = o.length; i < len; i++){
10886         p.fields.add(new Roo.data.Field(o[i]));
10887     }
10888     f.getField = function(name){
10889         return p.fields.get(name);  
10890     };
10891     return f;
10892 };
10893
10894 Roo.data.Record.AUTO_ID = 1000;
10895 Roo.data.Record.EDIT = 'edit';
10896 Roo.data.Record.REJECT = 'reject';
10897 Roo.data.Record.COMMIT = 'commit';
10898
10899 Roo.data.Record.prototype = {
10900     /**
10901      * Readonly flag - true if this record has been modified.
10902      * @type Boolean
10903      */
10904     dirty : false,
10905     editing : false,
10906     error: null,
10907     modified: null,
10908
10909     // private
10910     join : function(store){
10911         this.store = store;
10912     },
10913
10914     /**
10915      * Set the named field to the specified value.
10916      * @param {String} name The name of the field to set.
10917      * @param {Object} value The value to set the field to.
10918      */
10919     set : function(name, value){
10920         if(this.data[name] == value){
10921             return;
10922         }
10923         this.dirty = true;
10924         if(!this.modified){
10925             this.modified = {};
10926         }
10927         if(typeof this.modified[name] == 'undefined'){
10928             this.modified[name] = this.data[name];
10929         }
10930         this.data[name] = value;
10931         if(!this.editing && this.store){
10932             this.store.afterEdit(this);
10933         }       
10934     },
10935
10936     /**
10937      * Get the value of the named field.
10938      * @param {String} name The name of the field to get the value of.
10939      * @return {Object} The value of the field.
10940      */
10941     get : function(name){
10942         return this.data[name]; 
10943     },
10944
10945     // private
10946     beginEdit : function(){
10947         this.editing = true;
10948         this.modified = {}; 
10949     },
10950
10951     // private
10952     cancelEdit : function(){
10953         this.editing = false;
10954         delete this.modified;
10955     },
10956
10957     // private
10958     endEdit : function(){
10959         this.editing = false;
10960         if(this.dirty && this.store){
10961             this.store.afterEdit(this);
10962         }
10963     },
10964
10965     /**
10966      * Usually called by the {@link Roo.data.Store} which owns the Record.
10967      * Rejects all changes made to the Record since either creation, or the last commit operation.
10968      * Modified fields are reverted to their original values.
10969      * <p>
10970      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10971      * of reject operations.
10972      */
10973     reject : function(){
10974         var m = this.modified;
10975         for(var n in m){
10976             if(typeof m[n] != "function"){
10977                 this.data[n] = m[n];
10978             }
10979         }
10980         this.dirty = false;
10981         delete this.modified;
10982         this.editing = false;
10983         if(this.store){
10984             this.store.afterReject(this);
10985         }
10986     },
10987
10988     /**
10989      * Usually called by the {@link Roo.data.Store} which owns the Record.
10990      * Commits all changes made to the Record since either creation, or the last commit operation.
10991      * <p>
10992      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10993      * of commit operations.
10994      */
10995     commit : function(){
10996         this.dirty = false;
10997         delete this.modified;
10998         this.editing = false;
10999         if(this.store){
11000             this.store.afterCommit(this);
11001         }
11002     },
11003
11004     // private
11005     hasError : function(){
11006         return this.error != null;
11007     },
11008
11009     // private
11010     clearError : function(){
11011         this.error = null;
11012     },
11013
11014     /**
11015      * Creates a copy of this record.
11016      * @param {String} id (optional) A new record id if you don't want to use this record's id
11017      * @return {Record}
11018      */
11019     copy : function(newId) {
11020         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11021     }
11022 };/*
11023  * Based on:
11024  * Ext JS Library 1.1.1
11025  * Copyright(c) 2006-2007, Ext JS, LLC.
11026  *
11027  * Originally Released Under LGPL - original licence link has changed is not relivant.
11028  *
11029  * Fork - LGPL
11030  * <script type="text/javascript">
11031  */
11032
11033
11034
11035 /**
11036  * @class Roo.data.Store
11037  * @extends Roo.util.Observable
11038  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11039  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11040  * <p>
11041  * 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
11042  * has no knowledge of the format of the data returned by the Proxy.<br>
11043  * <p>
11044  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11045  * instances from the data object. These records are cached and made available through accessor functions.
11046  * @constructor
11047  * Creates a new Store.
11048  * @param {Object} config A config object containing the objects needed for the Store to access data,
11049  * and read the data into Records.
11050  */
11051 Roo.data.Store = function(config){
11052     this.data = new Roo.util.MixedCollection(false);
11053     this.data.getKey = function(o){
11054         return o.id;
11055     };
11056     this.baseParams = {};
11057     // private
11058     this.paramNames = {
11059         "start" : "start",
11060         "limit" : "limit",
11061         "sort" : "sort",
11062         "dir" : "dir",
11063         "multisort" : "_multisort"
11064     };
11065
11066     if(config && config.data){
11067         this.inlineData = config.data;
11068         delete config.data;
11069     }
11070
11071     Roo.apply(this, config);
11072     
11073     if(this.reader){ // reader passed
11074         this.reader = Roo.factory(this.reader, Roo.data);
11075         this.reader.xmodule = this.xmodule || false;
11076         if(!this.recordType){
11077             this.recordType = this.reader.recordType;
11078         }
11079         if(this.reader.onMetaChange){
11080             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11081         }
11082     }
11083
11084     if(this.recordType){
11085         this.fields = this.recordType.prototype.fields;
11086     }
11087     this.modified = [];
11088
11089     this.addEvents({
11090         /**
11091          * @event datachanged
11092          * Fires when the data cache has changed, and a widget which is using this Store
11093          * as a Record cache should refresh its view.
11094          * @param {Store} this
11095          */
11096         datachanged : true,
11097         /**
11098          * @event metachange
11099          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11100          * @param {Store} this
11101          * @param {Object} meta The JSON metadata
11102          */
11103         metachange : true,
11104         /**
11105          * @event add
11106          * Fires when Records have been added to the Store
11107          * @param {Store} this
11108          * @param {Roo.data.Record[]} records The array of Records added
11109          * @param {Number} index The index at which the record(s) were added
11110          */
11111         add : true,
11112         /**
11113          * @event remove
11114          * Fires when a Record has been removed from the Store
11115          * @param {Store} this
11116          * @param {Roo.data.Record} record The Record that was removed
11117          * @param {Number} index The index at which the record was removed
11118          */
11119         remove : true,
11120         /**
11121          * @event update
11122          * Fires when a Record has been updated
11123          * @param {Store} this
11124          * @param {Roo.data.Record} record The Record that was updated
11125          * @param {String} operation The update operation being performed.  Value may be one of:
11126          * <pre><code>
11127  Roo.data.Record.EDIT
11128  Roo.data.Record.REJECT
11129  Roo.data.Record.COMMIT
11130          * </code></pre>
11131          */
11132         update : true,
11133         /**
11134          * @event clear
11135          * Fires when the data cache has been cleared.
11136          * @param {Store} this
11137          */
11138         clear : true,
11139         /**
11140          * @event beforeload
11141          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11142          * the load action will be canceled.
11143          * @param {Store} this
11144          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11145          */
11146         beforeload : true,
11147         /**
11148          * @event beforeloadadd
11149          * Fires after a new set of Records has been loaded.
11150          * @param {Store} this
11151          * @param {Roo.data.Record[]} records The Records that were loaded
11152          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11153          */
11154         beforeloadadd : true,
11155         /**
11156          * @event load
11157          * Fires after a new set of Records has been loaded, before they are added to the store.
11158          * @param {Store} this
11159          * @param {Roo.data.Record[]} records The Records that were loaded
11160          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11161          * @params {Object} return from reader
11162          */
11163         load : true,
11164         /**
11165          * @event loadexception
11166          * Fires if an exception occurs in the Proxy during loading.
11167          * Called with the signature of the Proxy's "loadexception" event.
11168          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11169          * 
11170          * @param {Proxy} 
11171          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11172          * @param {Object} load options 
11173          * @param {Object} jsonData from your request (normally this contains the Exception)
11174          */
11175         loadexception : true
11176     });
11177     
11178     if(this.proxy){
11179         this.proxy = Roo.factory(this.proxy, Roo.data);
11180         this.proxy.xmodule = this.xmodule || false;
11181         this.relayEvents(this.proxy,  ["loadexception"]);
11182     }
11183     this.sortToggle = {};
11184     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11185
11186     Roo.data.Store.superclass.constructor.call(this);
11187
11188     if(this.inlineData){
11189         this.loadData(this.inlineData);
11190         delete this.inlineData;
11191     }
11192 };
11193
11194 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11195      /**
11196     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11197     * without a remote query - used by combo/forms at present.
11198     */
11199     
11200     /**
11201     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11202     */
11203     /**
11204     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11205     */
11206     /**
11207     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11208     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11209     */
11210     /**
11211     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11212     * on any HTTP request
11213     */
11214     /**
11215     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11216     */
11217     /**
11218     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11219     */
11220     multiSort: false,
11221     /**
11222     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11223     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11224     */
11225     remoteSort : false,
11226
11227     /**
11228     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11229      * loaded or when a record is removed. (defaults to false).
11230     */
11231     pruneModifiedRecords : false,
11232
11233     // private
11234     lastOptions : null,
11235
11236     /**
11237      * Add Records to the Store and fires the add event.
11238      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11239      */
11240     add : function(records){
11241         records = [].concat(records);
11242         for(var i = 0, len = records.length; i < len; i++){
11243             records[i].join(this);
11244         }
11245         var index = this.data.length;
11246         this.data.addAll(records);
11247         this.fireEvent("add", this, records, index);
11248     },
11249
11250     /**
11251      * Remove a Record from the Store and fires the remove event.
11252      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11253      */
11254     remove : function(record){
11255         var index = this.data.indexOf(record);
11256         this.data.removeAt(index);
11257  
11258         if(this.pruneModifiedRecords){
11259             this.modified.remove(record);
11260         }
11261         this.fireEvent("remove", this, record, index);
11262     },
11263
11264     /**
11265      * Remove all Records from the Store and fires the clear event.
11266      */
11267     removeAll : function(){
11268         this.data.clear();
11269         if(this.pruneModifiedRecords){
11270             this.modified = [];
11271         }
11272         this.fireEvent("clear", this);
11273     },
11274
11275     /**
11276      * Inserts Records to the Store at the given index and fires the add event.
11277      * @param {Number} index The start index at which to insert the passed Records.
11278      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11279      */
11280     insert : function(index, records){
11281         records = [].concat(records);
11282         for(var i = 0, len = records.length; i < len; i++){
11283             this.data.insert(index, records[i]);
11284             records[i].join(this);
11285         }
11286         this.fireEvent("add", this, records, index);
11287     },
11288
11289     /**
11290      * Get the index within the cache of the passed Record.
11291      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11292      * @return {Number} The index of the passed Record. Returns -1 if not found.
11293      */
11294     indexOf : function(record){
11295         return this.data.indexOf(record);
11296     },
11297
11298     /**
11299      * Get the index within the cache of the Record with the passed id.
11300      * @param {String} id The id of the Record to find.
11301      * @return {Number} The index of the Record. Returns -1 if not found.
11302      */
11303     indexOfId : function(id){
11304         return this.data.indexOfKey(id);
11305     },
11306
11307     /**
11308      * Get the Record with the specified id.
11309      * @param {String} id The id of the Record to find.
11310      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11311      */
11312     getById : function(id){
11313         return this.data.key(id);
11314     },
11315
11316     /**
11317      * Get the Record at the specified index.
11318      * @param {Number} index The index of the Record to find.
11319      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11320      */
11321     getAt : function(index){
11322         return this.data.itemAt(index);
11323     },
11324
11325     /**
11326      * Returns a range of Records between specified indices.
11327      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11328      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11329      * @return {Roo.data.Record[]} An array of Records
11330      */
11331     getRange : function(start, end){
11332         return this.data.getRange(start, end);
11333     },
11334
11335     // private
11336     storeOptions : function(o){
11337         o = Roo.apply({}, o);
11338         delete o.callback;
11339         delete o.scope;
11340         this.lastOptions = o;
11341     },
11342
11343     /**
11344      * Loads the Record cache from the configured Proxy using the configured Reader.
11345      * <p>
11346      * If using remote paging, then the first load call must specify the <em>start</em>
11347      * and <em>limit</em> properties in the options.params property to establish the initial
11348      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11349      * <p>
11350      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11351      * and this call will return before the new data has been loaded. Perform any post-processing
11352      * in a callback function, or in a "load" event handler.</strong>
11353      * <p>
11354      * @param {Object} options An object containing properties which control loading options:<ul>
11355      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11356      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11357      * passed the following arguments:<ul>
11358      * <li>r : Roo.data.Record[]</li>
11359      * <li>options: Options object from the load call</li>
11360      * <li>success: Boolean success indicator</li></ul></li>
11361      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11362      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11363      * </ul>
11364      */
11365     load : function(options){
11366         options = options || {};
11367         if(this.fireEvent("beforeload", this, options) !== false){
11368             this.storeOptions(options);
11369             var p = Roo.apply(options.params || {}, this.baseParams);
11370             // if meta was not loaded from remote source.. try requesting it.
11371             if (!this.reader.metaFromRemote) {
11372                 p._requestMeta = 1;
11373             }
11374             if(this.sortInfo && this.remoteSort){
11375                 var pn = this.paramNames;
11376                 p[pn["sort"]] = this.sortInfo.field;
11377                 p[pn["dir"]] = this.sortInfo.direction;
11378             }
11379             if (this.multiSort) {
11380                 var pn = this.paramNames;
11381                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11382             }
11383             
11384             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11385         }
11386     },
11387
11388     /**
11389      * Reloads the Record cache from the configured Proxy using the configured Reader and
11390      * the options from the last load operation performed.
11391      * @param {Object} options (optional) An object containing properties which may override the options
11392      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11393      * the most recently used options are reused).
11394      */
11395     reload : function(options){
11396         this.load(Roo.applyIf(options||{}, this.lastOptions));
11397     },
11398
11399     // private
11400     // Called as a callback by the Reader during a load operation.
11401     loadRecords : function(o, options, success){
11402         if(!o || success === false){
11403             if(success !== false){
11404                 this.fireEvent("load", this, [], options, o);
11405             }
11406             if(options.callback){
11407                 options.callback.call(options.scope || this, [], options, false);
11408             }
11409             return;
11410         }
11411         // if data returned failure - throw an exception.
11412         if (o.success === false) {
11413             // show a message if no listener is registered.
11414             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11415                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11416             }
11417             // loadmask wil be hooked into this..
11418             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11419             return;
11420         }
11421         var r = o.records, t = o.totalRecords || r.length;
11422         
11423         this.fireEvent("beforeloadadd", this, r, options, o);
11424         
11425         if(!options || options.add !== true){
11426             if(this.pruneModifiedRecords){
11427                 this.modified = [];
11428             }
11429             for(var i = 0, len = r.length; i < len; i++){
11430                 r[i].join(this);
11431             }
11432             if(this.snapshot){
11433                 this.data = this.snapshot;
11434                 delete this.snapshot;
11435             }
11436             this.data.clear();
11437             this.data.addAll(r);
11438             this.totalLength = t;
11439             this.applySort();
11440             this.fireEvent("datachanged", this);
11441         }else{
11442             this.totalLength = Math.max(t, this.data.length+r.length);
11443             this.add(r);
11444         }
11445         
11446         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11447                 
11448             var e = new Roo.data.Record({});
11449
11450             e.set(this.parent.displayField, this.parent.emptyTitle);
11451             e.set(this.parent.valueField, '');
11452
11453             this.insert(0, e);
11454         }
11455             
11456         this.fireEvent("load", this, r, options, o);
11457         if(options.callback){
11458             options.callback.call(options.scope || this, r, options, true);
11459         }
11460     },
11461
11462
11463     /**
11464      * Loads data from a passed data block. A Reader which understands the format of the data
11465      * must have been configured in the constructor.
11466      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11467      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11468      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11469      */
11470     loadData : function(o, append){
11471         var r = this.reader.readRecords(o);
11472         this.loadRecords(r, {add: append}, true);
11473     },
11474
11475     /**
11476      * Gets the number of cached records.
11477      * <p>
11478      * <em>If using paging, this may not be the total size of the dataset. If the data object
11479      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11480      * the data set size</em>
11481      */
11482     getCount : function(){
11483         return this.data.length || 0;
11484     },
11485
11486     /**
11487      * Gets the total number of records in the dataset as returned by the server.
11488      * <p>
11489      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11490      * the dataset size</em>
11491      */
11492     getTotalCount : function(){
11493         return this.totalLength || 0;
11494     },
11495
11496     /**
11497      * Returns the sort state of the Store as an object with two properties:
11498      * <pre><code>
11499  field {String} The name of the field by which the Records are sorted
11500  direction {String} The sort order, "ASC" or "DESC"
11501      * </code></pre>
11502      */
11503     getSortState : function(){
11504         return this.sortInfo;
11505     },
11506
11507     // private
11508     applySort : function(){
11509         if(this.sortInfo && !this.remoteSort){
11510             var s = this.sortInfo, f = s.field;
11511             var st = this.fields.get(f).sortType;
11512             var fn = function(r1, r2){
11513                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11514                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11515             };
11516             this.data.sort(s.direction, fn);
11517             if(this.snapshot && this.snapshot != this.data){
11518                 this.snapshot.sort(s.direction, fn);
11519             }
11520         }
11521     },
11522
11523     /**
11524      * Sets the default sort column and order to be used by the next load operation.
11525      * @param {String} fieldName The name of the field to sort by.
11526      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11527      */
11528     setDefaultSort : function(field, dir){
11529         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11530     },
11531
11532     /**
11533      * Sort the Records.
11534      * If remote sorting is used, the sort is performed on the server, and the cache is
11535      * reloaded. If local sorting is used, the cache is sorted internally.
11536      * @param {String} fieldName The name of the field to sort by.
11537      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11538      */
11539     sort : function(fieldName, dir){
11540         var f = this.fields.get(fieldName);
11541         if(!dir){
11542             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11543             
11544             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11545                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11546             }else{
11547                 dir = f.sortDir;
11548             }
11549         }
11550         this.sortToggle[f.name] = dir;
11551         this.sortInfo = {field: f.name, direction: dir};
11552         if(!this.remoteSort){
11553             this.applySort();
11554             this.fireEvent("datachanged", this);
11555         }else{
11556             this.load(this.lastOptions);
11557         }
11558     },
11559
11560     /**
11561      * Calls the specified function for each of the Records in the cache.
11562      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11563      * Returning <em>false</em> aborts and exits the iteration.
11564      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11565      */
11566     each : function(fn, scope){
11567         this.data.each(fn, scope);
11568     },
11569
11570     /**
11571      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11572      * (e.g., during paging).
11573      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11574      */
11575     getModifiedRecords : function(){
11576         return this.modified;
11577     },
11578
11579     // private
11580     createFilterFn : function(property, value, anyMatch){
11581         if(!value.exec){ // not a regex
11582             value = String(value);
11583             if(value.length == 0){
11584                 return false;
11585             }
11586             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11587         }
11588         return function(r){
11589             return value.test(r.data[property]);
11590         };
11591     },
11592
11593     /**
11594      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11595      * @param {String} property A field on your records
11596      * @param {Number} start The record index to start at (defaults to 0)
11597      * @param {Number} end The last record index to include (defaults to length - 1)
11598      * @return {Number} The sum
11599      */
11600     sum : function(property, start, end){
11601         var rs = this.data.items, v = 0;
11602         start = start || 0;
11603         end = (end || end === 0) ? end : rs.length-1;
11604
11605         for(var i = start; i <= end; i++){
11606             v += (rs[i].data[property] || 0);
11607         }
11608         return v;
11609     },
11610
11611     /**
11612      * Filter the records by a specified property.
11613      * @param {String} field A field on your records
11614      * @param {String/RegExp} value Either a string that the field
11615      * should start with or a RegExp to test against the field
11616      * @param {Boolean} anyMatch True to match any part not just the beginning
11617      */
11618     filter : function(property, value, anyMatch){
11619         var fn = this.createFilterFn(property, value, anyMatch);
11620         return fn ? this.filterBy(fn) : this.clearFilter();
11621     },
11622
11623     /**
11624      * Filter by a function. The specified function will be called with each
11625      * record in this data source. If the function returns true the record is included,
11626      * otherwise it is filtered.
11627      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11628      * @param {Object} scope (optional) The scope of the function (defaults to this)
11629      */
11630     filterBy : function(fn, scope){
11631         this.snapshot = this.snapshot || this.data;
11632         this.data = this.queryBy(fn, scope||this);
11633         this.fireEvent("datachanged", this);
11634     },
11635
11636     /**
11637      * Query the records by a specified property.
11638      * @param {String} field A field on your records
11639      * @param {String/RegExp} value Either a string that the field
11640      * should start with or a RegExp to test against the field
11641      * @param {Boolean} anyMatch True to match any part not just the beginning
11642      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11643      */
11644     query : function(property, value, anyMatch){
11645         var fn = this.createFilterFn(property, value, anyMatch);
11646         return fn ? this.queryBy(fn) : this.data.clone();
11647     },
11648
11649     /**
11650      * Query by a function. The specified function will be called with each
11651      * record in this data source. If the function returns true the record is included
11652      * in the results.
11653      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11654      * @param {Object} scope (optional) The scope of the function (defaults to this)
11655       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11656      **/
11657     queryBy : function(fn, scope){
11658         var data = this.snapshot || this.data;
11659         return data.filterBy(fn, scope||this);
11660     },
11661
11662     /**
11663      * Collects unique values for a particular dataIndex from this store.
11664      * @param {String} dataIndex The property to collect
11665      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11666      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11667      * @return {Array} An array of the unique values
11668      **/
11669     collect : function(dataIndex, allowNull, bypassFilter){
11670         var d = (bypassFilter === true && this.snapshot) ?
11671                 this.snapshot.items : this.data.items;
11672         var v, sv, r = [], l = {};
11673         for(var i = 0, len = d.length; i < len; i++){
11674             v = d[i].data[dataIndex];
11675             sv = String(v);
11676             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11677                 l[sv] = true;
11678                 r[r.length] = v;
11679             }
11680         }
11681         return r;
11682     },
11683
11684     /**
11685      * Revert to a view of the Record cache with no filtering applied.
11686      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11687      */
11688     clearFilter : function(suppressEvent){
11689         if(this.snapshot && this.snapshot != this.data){
11690             this.data = this.snapshot;
11691             delete this.snapshot;
11692             if(suppressEvent !== true){
11693                 this.fireEvent("datachanged", this);
11694             }
11695         }
11696     },
11697
11698     // private
11699     afterEdit : function(record){
11700         if(this.modified.indexOf(record) == -1){
11701             this.modified.push(record);
11702         }
11703         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11704     },
11705     
11706     // private
11707     afterReject : function(record){
11708         this.modified.remove(record);
11709         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11710     },
11711
11712     // private
11713     afterCommit : function(record){
11714         this.modified.remove(record);
11715         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11716     },
11717
11718     /**
11719      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11720      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11721      */
11722     commitChanges : function(){
11723         var m = this.modified.slice(0);
11724         this.modified = [];
11725         for(var i = 0, len = m.length; i < len; i++){
11726             m[i].commit();
11727         }
11728     },
11729
11730     /**
11731      * Cancel outstanding changes on all changed records.
11732      */
11733     rejectChanges : function(){
11734         var m = this.modified.slice(0);
11735         this.modified = [];
11736         for(var i = 0, len = m.length; i < len; i++){
11737             m[i].reject();
11738         }
11739     },
11740
11741     onMetaChange : function(meta, rtype, o){
11742         this.recordType = rtype;
11743         this.fields = rtype.prototype.fields;
11744         delete this.snapshot;
11745         this.sortInfo = meta.sortInfo || this.sortInfo;
11746         this.modified = [];
11747         this.fireEvent('metachange', this, this.reader.meta);
11748     },
11749     
11750     moveIndex : function(data, type)
11751     {
11752         var index = this.indexOf(data);
11753         
11754         var newIndex = index + type;
11755         
11756         this.remove(data);
11757         
11758         this.insert(newIndex, data);
11759         
11760     }
11761 });/*
11762  * Based on:
11763  * Ext JS Library 1.1.1
11764  * Copyright(c) 2006-2007, Ext JS, LLC.
11765  *
11766  * Originally Released Under LGPL - original licence link has changed is not relivant.
11767  *
11768  * Fork - LGPL
11769  * <script type="text/javascript">
11770  */
11771
11772 /**
11773  * @class Roo.data.SimpleStore
11774  * @extends Roo.data.Store
11775  * Small helper class to make creating Stores from Array data easier.
11776  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11777  * @cfg {Array} fields An array of field definition objects, or field name strings.
11778  * @cfg {Array} data The multi-dimensional array of data
11779  * @constructor
11780  * @param {Object} config
11781  */
11782 Roo.data.SimpleStore = function(config){
11783     Roo.data.SimpleStore.superclass.constructor.call(this, {
11784         isLocal : true,
11785         reader: new Roo.data.ArrayReader({
11786                 id: config.id
11787             },
11788             Roo.data.Record.create(config.fields)
11789         ),
11790         proxy : new Roo.data.MemoryProxy(config.data)
11791     });
11792     this.load();
11793 };
11794 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11795  * Based on:
11796  * Ext JS Library 1.1.1
11797  * Copyright(c) 2006-2007, Ext JS, LLC.
11798  *
11799  * Originally Released Under LGPL - original licence link has changed is not relivant.
11800  *
11801  * Fork - LGPL
11802  * <script type="text/javascript">
11803  */
11804
11805 /**
11806 /**
11807  * @extends Roo.data.Store
11808  * @class Roo.data.JsonStore
11809  * Small helper class to make creating Stores for JSON data easier. <br/>
11810 <pre><code>
11811 var store = new Roo.data.JsonStore({
11812     url: 'get-images.php',
11813     root: 'images',
11814     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11815 });
11816 </code></pre>
11817  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11818  * JsonReader and HttpProxy (unless inline data is provided).</b>
11819  * @cfg {Array} fields An array of field definition objects, or field name strings.
11820  * @constructor
11821  * @param {Object} config
11822  */
11823 Roo.data.JsonStore = function(c){
11824     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11825         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11826         reader: new Roo.data.JsonReader(c, c.fields)
11827     }));
11828 };
11829 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11830  * Based on:
11831  * Ext JS Library 1.1.1
11832  * Copyright(c) 2006-2007, Ext JS, LLC.
11833  *
11834  * Originally Released Under LGPL - original licence link has changed is not relivant.
11835  *
11836  * Fork - LGPL
11837  * <script type="text/javascript">
11838  */
11839
11840  
11841 Roo.data.Field = function(config){
11842     if(typeof config == "string"){
11843         config = {name: config};
11844     }
11845     Roo.apply(this, config);
11846     
11847     if(!this.type){
11848         this.type = "auto";
11849     }
11850     
11851     var st = Roo.data.SortTypes;
11852     // named sortTypes are supported, here we look them up
11853     if(typeof this.sortType == "string"){
11854         this.sortType = st[this.sortType];
11855     }
11856     
11857     // set default sortType for strings and dates
11858     if(!this.sortType){
11859         switch(this.type){
11860             case "string":
11861                 this.sortType = st.asUCString;
11862                 break;
11863             case "date":
11864                 this.sortType = st.asDate;
11865                 break;
11866             default:
11867                 this.sortType = st.none;
11868         }
11869     }
11870
11871     // define once
11872     var stripRe = /[\$,%]/g;
11873
11874     // prebuilt conversion function for this field, instead of
11875     // switching every time we're reading a value
11876     if(!this.convert){
11877         var cv, dateFormat = this.dateFormat;
11878         switch(this.type){
11879             case "":
11880             case "auto":
11881             case undefined:
11882                 cv = function(v){ return v; };
11883                 break;
11884             case "string":
11885                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11886                 break;
11887             case "int":
11888                 cv = function(v){
11889                     return v !== undefined && v !== null && v !== '' ?
11890                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11891                     };
11892                 break;
11893             case "float":
11894                 cv = function(v){
11895                     return v !== undefined && v !== null && v !== '' ?
11896                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11897                     };
11898                 break;
11899             case "bool":
11900             case "boolean":
11901                 cv = function(v){ return v === true || v === "true" || v == 1; };
11902                 break;
11903             case "date":
11904                 cv = function(v){
11905                     if(!v){
11906                         return '';
11907                     }
11908                     if(v instanceof Date){
11909                         return v;
11910                     }
11911                     if(dateFormat){
11912                         if(dateFormat == "timestamp"){
11913                             return new Date(v*1000);
11914                         }
11915                         return Date.parseDate(v, dateFormat);
11916                     }
11917                     var parsed = Date.parse(v);
11918                     return parsed ? new Date(parsed) : null;
11919                 };
11920              break;
11921             
11922         }
11923         this.convert = cv;
11924     }
11925 };
11926
11927 Roo.data.Field.prototype = {
11928     dateFormat: null,
11929     defaultValue: "",
11930     mapping: null,
11931     sortType : null,
11932     sortDir : "ASC"
11933 };/*
11934  * Based on:
11935  * Ext JS Library 1.1.1
11936  * Copyright(c) 2006-2007, Ext JS, LLC.
11937  *
11938  * Originally Released Under LGPL - original licence link has changed is not relivant.
11939  *
11940  * Fork - LGPL
11941  * <script type="text/javascript">
11942  */
11943  
11944 // Base class for reading structured data from a data source.  This class is intended to be
11945 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11946
11947 /**
11948  * @class Roo.data.DataReader
11949  * Base class for reading structured data from a data source.  This class is intended to be
11950  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11951  */
11952
11953 Roo.data.DataReader = function(meta, recordType){
11954     
11955     this.meta = meta;
11956     
11957     this.recordType = recordType instanceof Array ? 
11958         Roo.data.Record.create(recordType) : recordType;
11959 };
11960
11961 Roo.data.DataReader.prototype = {
11962      /**
11963      * Create an empty record
11964      * @param {Object} data (optional) - overlay some values
11965      * @return {Roo.data.Record} record created.
11966      */
11967     newRow :  function(d) {
11968         var da =  {};
11969         this.recordType.prototype.fields.each(function(c) {
11970             switch( c.type) {
11971                 case 'int' : da[c.name] = 0; break;
11972                 case 'date' : da[c.name] = new Date(); break;
11973                 case 'float' : da[c.name] = 0.0; break;
11974                 case 'boolean' : da[c.name] = false; break;
11975                 default : da[c.name] = ""; break;
11976             }
11977             
11978         });
11979         return new this.recordType(Roo.apply(da, d));
11980     }
11981     
11982 };/*
11983  * Based on:
11984  * Ext JS Library 1.1.1
11985  * Copyright(c) 2006-2007, Ext JS, LLC.
11986  *
11987  * Originally Released Under LGPL - original licence link has changed is not relivant.
11988  *
11989  * Fork - LGPL
11990  * <script type="text/javascript">
11991  */
11992
11993 /**
11994  * @class Roo.data.DataProxy
11995  * @extends Roo.data.Observable
11996  * This class is an abstract base class for implementations which provide retrieval of
11997  * unformatted data objects.<br>
11998  * <p>
11999  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12000  * (of the appropriate type which knows how to parse the data object) to provide a block of
12001  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12002  * <p>
12003  * Custom implementations must implement the load method as described in
12004  * {@link Roo.data.HttpProxy#load}.
12005  */
12006 Roo.data.DataProxy = function(){
12007     this.addEvents({
12008         /**
12009          * @event beforeload
12010          * Fires before a network request is made to retrieve a data object.
12011          * @param {Object} This DataProxy object.
12012          * @param {Object} params The params parameter to the load function.
12013          */
12014         beforeload : true,
12015         /**
12016          * @event load
12017          * Fires before the load method's callback is called.
12018          * @param {Object} This DataProxy object.
12019          * @param {Object} o The data object.
12020          * @param {Object} arg The callback argument object passed to the load function.
12021          */
12022         load : true,
12023         /**
12024          * @event loadexception
12025          * Fires if an Exception occurs during data retrieval.
12026          * @param {Object} This DataProxy object.
12027          * @param {Object} o The data object.
12028          * @param {Object} arg The callback argument object passed to the load function.
12029          * @param {Object} e The Exception.
12030          */
12031         loadexception : true
12032     });
12033     Roo.data.DataProxy.superclass.constructor.call(this);
12034 };
12035
12036 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12037
12038     /**
12039      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12040      */
12041 /*
12042  * Based on:
12043  * Ext JS Library 1.1.1
12044  * Copyright(c) 2006-2007, Ext JS, LLC.
12045  *
12046  * Originally Released Under LGPL - original licence link has changed is not relivant.
12047  *
12048  * Fork - LGPL
12049  * <script type="text/javascript">
12050  */
12051 /**
12052  * @class Roo.data.MemoryProxy
12053  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12054  * to the Reader when its load method is called.
12055  * @constructor
12056  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12057  */
12058 Roo.data.MemoryProxy = function(data){
12059     if (data.data) {
12060         data = data.data;
12061     }
12062     Roo.data.MemoryProxy.superclass.constructor.call(this);
12063     this.data = data;
12064 };
12065
12066 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12067     
12068     /**
12069      * Load data from the requested source (in this case an in-memory
12070      * data object passed to the constructor), read the data object into
12071      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12072      * process that block using the passed callback.
12073      * @param {Object} params This parameter is not used by the MemoryProxy class.
12074      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12075      * object into a block of Roo.data.Records.
12076      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12077      * The function must be passed <ul>
12078      * <li>The Record block object</li>
12079      * <li>The "arg" argument from the load function</li>
12080      * <li>A boolean success indicator</li>
12081      * </ul>
12082      * @param {Object} scope The scope in which to call the callback
12083      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12084      */
12085     load : function(params, reader, callback, scope, arg){
12086         params = params || {};
12087         var result;
12088         try {
12089             result = reader.readRecords(this.data);
12090         }catch(e){
12091             this.fireEvent("loadexception", this, arg, null, e);
12092             callback.call(scope, null, arg, false);
12093             return;
12094         }
12095         callback.call(scope, result, arg, true);
12096     },
12097     
12098     // private
12099     update : function(params, records){
12100         
12101     }
12102 });/*
12103  * Based on:
12104  * Ext JS Library 1.1.1
12105  * Copyright(c) 2006-2007, Ext JS, LLC.
12106  *
12107  * Originally Released Under LGPL - original licence link has changed is not relivant.
12108  *
12109  * Fork - LGPL
12110  * <script type="text/javascript">
12111  */
12112 /**
12113  * @class Roo.data.HttpProxy
12114  * @extends Roo.data.DataProxy
12115  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12116  * configured to reference a certain URL.<br><br>
12117  * <p>
12118  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12119  * from which the running page was served.<br><br>
12120  * <p>
12121  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12122  * <p>
12123  * Be aware that to enable the browser to parse an XML document, the server must set
12124  * the Content-Type header in the HTTP response to "text/xml".
12125  * @constructor
12126  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12127  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12128  * will be used to make the request.
12129  */
12130 Roo.data.HttpProxy = function(conn){
12131     Roo.data.HttpProxy.superclass.constructor.call(this);
12132     // is conn a conn config or a real conn?
12133     this.conn = conn;
12134     this.useAjax = !conn || !conn.events;
12135   
12136 };
12137
12138 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12139     // thse are take from connection...
12140     
12141     /**
12142      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12143      */
12144     /**
12145      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12146      * extra parameters to each request made by this object. (defaults to undefined)
12147      */
12148     /**
12149      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12150      *  to each request made by this object. (defaults to undefined)
12151      */
12152     /**
12153      * @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)
12154      */
12155     /**
12156      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12157      */
12158      /**
12159      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12160      * @type Boolean
12161      */
12162   
12163
12164     /**
12165      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12166      * @type Boolean
12167      */
12168     /**
12169      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12170      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12171      * a finer-grained basis than the DataProxy events.
12172      */
12173     getConnection : function(){
12174         return this.useAjax ? Roo.Ajax : this.conn;
12175     },
12176
12177     /**
12178      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12179      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12180      * process that block using the passed callback.
12181      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12182      * for the request to the remote server.
12183      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12184      * object into a block of Roo.data.Records.
12185      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12186      * The function must be passed <ul>
12187      * <li>The Record block object</li>
12188      * <li>The "arg" argument from the load function</li>
12189      * <li>A boolean success indicator</li>
12190      * </ul>
12191      * @param {Object} scope The scope in which to call the callback
12192      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12193      */
12194     load : function(params, reader, callback, scope, arg){
12195         if(this.fireEvent("beforeload", this, params) !== false){
12196             var  o = {
12197                 params : params || {},
12198                 request: {
12199                     callback : callback,
12200                     scope : scope,
12201                     arg : arg
12202                 },
12203                 reader: reader,
12204                 callback : this.loadResponse,
12205                 scope: this
12206             };
12207             if(this.useAjax){
12208                 Roo.applyIf(o, this.conn);
12209                 if(this.activeRequest){
12210                     Roo.Ajax.abort(this.activeRequest);
12211                 }
12212                 this.activeRequest = Roo.Ajax.request(o);
12213             }else{
12214                 this.conn.request(o);
12215             }
12216         }else{
12217             callback.call(scope||this, null, arg, false);
12218         }
12219     },
12220
12221     // private
12222     loadResponse : function(o, success, response){
12223         delete this.activeRequest;
12224         if(!success){
12225             this.fireEvent("loadexception", this, o, response);
12226             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12227             return;
12228         }
12229         var result;
12230         try {
12231             result = o.reader.read(response);
12232         }catch(e){
12233             this.fireEvent("loadexception", this, o, response, e);
12234             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12235             return;
12236         }
12237         
12238         this.fireEvent("load", this, o, o.request.arg);
12239         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12240     },
12241
12242     // private
12243     update : function(dataSet){
12244
12245     },
12246
12247     // private
12248     updateResponse : function(dataSet){
12249
12250     }
12251 });/*
12252  * Based on:
12253  * Ext JS Library 1.1.1
12254  * Copyright(c) 2006-2007, Ext JS, LLC.
12255  *
12256  * Originally Released Under LGPL - original licence link has changed is not relivant.
12257  *
12258  * Fork - LGPL
12259  * <script type="text/javascript">
12260  */
12261
12262 /**
12263  * @class Roo.data.ScriptTagProxy
12264  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12265  * other than the originating domain of the running page.<br><br>
12266  * <p>
12267  * <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
12268  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12269  * <p>
12270  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12271  * source code that is used as the source inside a &lt;script> tag.<br><br>
12272  * <p>
12273  * In order for the browser to process the returned data, the server must wrap the data object
12274  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12275  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12276  * depending on whether the callback name was passed:
12277  * <p>
12278  * <pre><code>
12279 boolean scriptTag = false;
12280 String cb = request.getParameter("callback");
12281 if (cb != null) {
12282     scriptTag = true;
12283     response.setContentType("text/javascript");
12284 } else {
12285     response.setContentType("application/x-json");
12286 }
12287 Writer out = response.getWriter();
12288 if (scriptTag) {
12289     out.write(cb + "(");
12290 }
12291 out.print(dataBlock.toJsonString());
12292 if (scriptTag) {
12293     out.write(");");
12294 }
12295 </pre></code>
12296  *
12297  * @constructor
12298  * @param {Object} config A configuration object.
12299  */
12300 Roo.data.ScriptTagProxy = function(config){
12301     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12302     Roo.apply(this, config);
12303     this.head = document.getElementsByTagName("head")[0];
12304 };
12305
12306 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12307
12308 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12309     /**
12310      * @cfg {String} url The URL from which to request the data object.
12311      */
12312     /**
12313      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12314      */
12315     timeout : 30000,
12316     /**
12317      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12318      * the server the name of the callback function set up by the load call to process the returned data object.
12319      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12320      * javascript output which calls this named function passing the data object as its only parameter.
12321      */
12322     callbackParam : "callback",
12323     /**
12324      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12325      * name to the request.
12326      */
12327     nocache : true,
12328
12329     /**
12330      * Load data from the configured URL, read the data object into
12331      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12332      * process that block using the passed callback.
12333      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12334      * for the request to the remote server.
12335      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12336      * object into a block of Roo.data.Records.
12337      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12338      * The function must be passed <ul>
12339      * <li>The Record block object</li>
12340      * <li>The "arg" argument from the load function</li>
12341      * <li>A boolean success indicator</li>
12342      * </ul>
12343      * @param {Object} scope The scope in which to call the callback
12344      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12345      */
12346     load : function(params, reader, callback, scope, arg){
12347         if(this.fireEvent("beforeload", this, params) !== false){
12348
12349             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12350
12351             var url = this.url;
12352             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12353             if(this.nocache){
12354                 url += "&_dc=" + (new Date().getTime());
12355             }
12356             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12357             var trans = {
12358                 id : transId,
12359                 cb : "stcCallback"+transId,
12360                 scriptId : "stcScript"+transId,
12361                 params : params,
12362                 arg : arg,
12363                 url : url,
12364                 callback : callback,
12365                 scope : scope,
12366                 reader : reader
12367             };
12368             var conn = this;
12369
12370             window[trans.cb] = function(o){
12371                 conn.handleResponse(o, trans);
12372             };
12373
12374             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12375
12376             if(this.autoAbort !== false){
12377                 this.abort();
12378             }
12379
12380             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12381
12382             var script = document.createElement("script");
12383             script.setAttribute("src", url);
12384             script.setAttribute("type", "text/javascript");
12385             script.setAttribute("id", trans.scriptId);
12386             this.head.appendChild(script);
12387
12388             this.trans = trans;
12389         }else{
12390             callback.call(scope||this, null, arg, false);
12391         }
12392     },
12393
12394     // private
12395     isLoading : function(){
12396         return this.trans ? true : false;
12397     },
12398
12399     /**
12400      * Abort the current server request.
12401      */
12402     abort : function(){
12403         if(this.isLoading()){
12404             this.destroyTrans(this.trans);
12405         }
12406     },
12407
12408     // private
12409     destroyTrans : function(trans, isLoaded){
12410         this.head.removeChild(document.getElementById(trans.scriptId));
12411         clearTimeout(trans.timeoutId);
12412         if(isLoaded){
12413             window[trans.cb] = undefined;
12414             try{
12415                 delete window[trans.cb];
12416             }catch(e){}
12417         }else{
12418             // if hasn't been loaded, wait for load to remove it to prevent script error
12419             window[trans.cb] = function(){
12420                 window[trans.cb] = undefined;
12421                 try{
12422                     delete window[trans.cb];
12423                 }catch(e){}
12424             };
12425         }
12426     },
12427
12428     // private
12429     handleResponse : function(o, trans){
12430         this.trans = false;
12431         this.destroyTrans(trans, true);
12432         var result;
12433         try {
12434             result = trans.reader.readRecords(o);
12435         }catch(e){
12436             this.fireEvent("loadexception", this, o, trans.arg, e);
12437             trans.callback.call(trans.scope||window, null, trans.arg, false);
12438             return;
12439         }
12440         this.fireEvent("load", this, o, trans.arg);
12441         trans.callback.call(trans.scope||window, result, trans.arg, true);
12442     },
12443
12444     // private
12445     handleFailure : function(trans){
12446         this.trans = false;
12447         this.destroyTrans(trans, false);
12448         this.fireEvent("loadexception", this, null, trans.arg);
12449         trans.callback.call(trans.scope||window, null, trans.arg, false);
12450     }
12451 });/*
12452  * Based on:
12453  * Ext JS Library 1.1.1
12454  * Copyright(c) 2006-2007, Ext JS, LLC.
12455  *
12456  * Originally Released Under LGPL - original licence link has changed is not relivant.
12457  *
12458  * Fork - LGPL
12459  * <script type="text/javascript">
12460  */
12461
12462 /**
12463  * @class Roo.data.JsonReader
12464  * @extends Roo.data.DataReader
12465  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12466  * based on mappings in a provided Roo.data.Record constructor.
12467  * 
12468  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12469  * in the reply previously. 
12470  * 
12471  * <p>
12472  * Example code:
12473  * <pre><code>
12474 var RecordDef = Roo.data.Record.create([
12475     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12476     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12477 ]);
12478 var myReader = new Roo.data.JsonReader({
12479     totalProperty: "results",    // The property which contains the total dataset size (optional)
12480     root: "rows",                // The property which contains an Array of row objects
12481     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12482 }, RecordDef);
12483 </code></pre>
12484  * <p>
12485  * This would consume a JSON file like this:
12486  * <pre><code>
12487 { 'results': 2, 'rows': [
12488     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12489     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12490 }
12491 </code></pre>
12492  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12493  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12494  * paged from the remote server.
12495  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12496  * @cfg {String} root name of the property which contains the Array of row objects.
12497  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12498  * @cfg {Array} fields Array of field definition objects
12499  * @constructor
12500  * Create a new JsonReader
12501  * @param {Object} meta Metadata configuration options
12502  * @param {Object} recordType Either an Array of field definition objects,
12503  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12504  */
12505 Roo.data.JsonReader = function(meta, recordType){
12506     
12507     meta = meta || {};
12508     // set some defaults:
12509     Roo.applyIf(meta, {
12510         totalProperty: 'total',
12511         successProperty : 'success',
12512         root : 'data',
12513         id : 'id'
12514     });
12515     
12516     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12517 };
12518 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12519     
12520     /**
12521      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12522      * Used by Store query builder to append _requestMeta to params.
12523      * 
12524      */
12525     metaFromRemote : false,
12526     /**
12527      * This method is only used by a DataProxy which has retrieved data from a remote server.
12528      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12529      * @return {Object} data A data block which is used by an Roo.data.Store object as
12530      * a cache of Roo.data.Records.
12531      */
12532     read : function(response){
12533         var json = response.responseText;
12534        
12535         var o = /* eval:var:o */ eval("("+json+")");
12536         if(!o) {
12537             throw {message: "JsonReader.read: Json object not found"};
12538         }
12539         
12540         if(o.metaData){
12541             
12542             delete this.ef;
12543             this.metaFromRemote = true;
12544             this.meta = o.metaData;
12545             this.recordType = Roo.data.Record.create(o.metaData.fields);
12546             this.onMetaChange(this.meta, this.recordType, o);
12547         }
12548         return this.readRecords(o);
12549     },
12550
12551     // private function a store will implement
12552     onMetaChange : function(meta, recordType, o){
12553
12554     },
12555
12556     /**
12557          * @ignore
12558          */
12559     simpleAccess: function(obj, subsc) {
12560         return obj[subsc];
12561     },
12562
12563         /**
12564          * @ignore
12565          */
12566     getJsonAccessor: function(){
12567         var re = /[\[\.]/;
12568         return function(expr) {
12569             try {
12570                 return(re.test(expr))
12571                     ? new Function("obj", "return obj." + expr)
12572                     : function(obj){
12573                         return obj[expr];
12574                     };
12575             } catch(e){}
12576             return Roo.emptyFn;
12577         };
12578     }(),
12579
12580     /**
12581      * Create a data block containing Roo.data.Records from an XML document.
12582      * @param {Object} o An object which contains an Array of row objects in the property specified
12583      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12584      * which contains the total size of the dataset.
12585      * @return {Object} data A data block which is used by an Roo.data.Store object as
12586      * a cache of Roo.data.Records.
12587      */
12588     readRecords : function(o){
12589         /**
12590          * After any data loads, the raw JSON data is available for further custom processing.
12591          * @type Object
12592          */
12593         this.o = o;
12594         var s = this.meta, Record = this.recordType,
12595             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12596
12597 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12598         if (!this.ef) {
12599             if(s.totalProperty) {
12600                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12601                 }
12602                 if(s.successProperty) {
12603                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12604                 }
12605                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12606                 if (s.id) {
12607                         var g = this.getJsonAccessor(s.id);
12608                         this.getId = function(rec) {
12609                                 var r = g(rec);  
12610                                 return (r === undefined || r === "") ? null : r;
12611                         };
12612                 } else {
12613                         this.getId = function(){return null;};
12614                 }
12615             this.ef = [];
12616             for(var jj = 0; jj < fl; jj++){
12617                 f = fi[jj];
12618                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12619                 this.ef[jj] = this.getJsonAccessor(map);
12620             }
12621         }
12622
12623         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12624         if(s.totalProperty){
12625             var vt = parseInt(this.getTotal(o), 10);
12626             if(!isNaN(vt)){
12627                 totalRecords = vt;
12628             }
12629         }
12630         if(s.successProperty){
12631             var vs = this.getSuccess(o);
12632             if(vs === false || vs === 'false'){
12633                 success = false;
12634             }
12635         }
12636         var records = [];
12637         for(var i = 0; i < c; i++){
12638                 var n = root[i];
12639             var values = {};
12640             var id = this.getId(n);
12641             for(var j = 0; j < fl; j++){
12642                 f = fi[j];
12643             var v = this.ef[j](n);
12644             if (!f.convert) {
12645                 Roo.log('missing convert for ' + f.name);
12646                 Roo.log(f);
12647                 continue;
12648             }
12649             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12650             }
12651             var record = new Record(values, id);
12652             record.json = n;
12653             records[i] = record;
12654         }
12655         return {
12656             raw : o,
12657             success : success,
12658             records : records,
12659             totalRecords : totalRecords
12660         };
12661     }
12662 });/*
12663  * Based on:
12664  * Ext JS Library 1.1.1
12665  * Copyright(c) 2006-2007, Ext JS, LLC.
12666  *
12667  * Originally Released Under LGPL - original licence link has changed is not relivant.
12668  *
12669  * Fork - LGPL
12670  * <script type="text/javascript">
12671  */
12672
12673 /**
12674  * @class Roo.data.ArrayReader
12675  * @extends Roo.data.DataReader
12676  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12677  * Each element of that Array represents a row of data fields. The
12678  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12679  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12680  * <p>
12681  * Example code:.
12682  * <pre><code>
12683 var RecordDef = Roo.data.Record.create([
12684     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12685     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12686 ]);
12687 var myReader = new Roo.data.ArrayReader({
12688     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12689 }, RecordDef);
12690 </code></pre>
12691  * <p>
12692  * This would consume an Array like this:
12693  * <pre><code>
12694 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12695   </code></pre>
12696  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12697  * @constructor
12698  * Create a new JsonReader
12699  * @param {Object} meta Metadata configuration options.
12700  * @param {Object} recordType Either an Array of field definition objects
12701  * as specified to {@link Roo.data.Record#create},
12702  * or an {@link Roo.data.Record} object
12703  * created using {@link Roo.data.Record#create}.
12704  */
12705 Roo.data.ArrayReader = function(meta, recordType){
12706     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12707 };
12708
12709 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12710     /**
12711      * Create a data block containing Roo.data.Records from an XML document.
12712      * @param {Object} o An Array of row objects which represents the dataset.
12713      * @return {Object} data A data block which is used by an Roo.data.Store object as
12714      * a cache of Roo.data.Records.
12715      */
12716     readRecords : function(o){
12717         var sid = this.meta ? this.meta.id : null;
12718         var recordType = this.recordType, fields = recordType.prototype.fields;
12719         var records = [];
12720         var root = o;
12721             for(var i = 0; i < root.length; i++){
12722                     var n = root[i];
12723                 var values = {};
12724                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12725                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12726                 var f = fields.items[j];
12727                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12728                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12729                 v = f.convert(v);
12730                 values[f.name] = v;
12731             }
12732                 var record = new recordType(values, id);
12733                 record.json = n;
12734                 records[records.length] = record;
12735             }
12736             return {
12737                 records : records,
12738                 totalRecords : records.length
12739             };
12740     }
12741 });/*
12742  * - LGPL
12743  * * 
12744  */
12745
12746 /**
12747  * @class Roo.bootstrap.ComboBox
12748  * @extends Roo.bootstrap.TriggerField
12749  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12750  * @cfg {Boolean} append (true|false) default false
12751  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12752  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12753  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12754  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12755  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12756  * @cfg {Boolean} animate default true
12757  * @cfg {Boolean} emptyResultText only for touch device
12758  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12759  * @cfg {String} emptyTitle default ''
12760  * @constructor
12761  * Create a new ComboBox.
12762  * @param {Object} config Configuration options
12763  */
12764 Roo.bootstrap.ComboBox = function(config){
12765     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12766     this.addEvents({
12767         /**
12768          * @event expand
12769          * Fires when the dropdown list is expanded
12770         * @param {Roo.bootstrap.ComboBox} combo This combo box
12771         */
12772         'expand' : true,
12773         /**
12774          * @event collapse
12775          * Fires when the dropdown list is collapsed
12776         * @param {Roo.bootstrap.ComboBox} combo This combo box
12777         */
12778         'collapse' : true,
12779         /**
12780          * @event beforeselect
12781          * Fires before a list item is selected. Return false to cancel the selection.
12782         * @param {Roo.bootstrap.ComboBox} combo This combo box
12783         * @param {Roo.data.Record} record The data record returned from the underlying store
12784         * @param {Number} index The index of the selected item in the dropdown list
12785         */
12786         'beforeselect' : true,
12787         /**
12788          * @event select
12789          * Fires when a list item is selected
12790         * @param {Roo.bootstrap.ComboBox} combo This combo box
12791         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12792         * @param {Number} index The index of the selected item in the dropdown list
12793         */
12794         'select' : true,
12795         /**
12796          * @event beforequery
12797          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12798          * The event object passed has these properties:
12799         * @param {Roo.bootstrap.ComboBox} combo This combo box
12800         * @param {String} query The query
12801         * @param {Boolean} forceAll true to force "all" query
12802         * @param {Boolean} cancel true to cancel the query
12803         * @param {Object} e The query event object
12804         */
12805         'beforequery': true,
12806          /**
12807          * @event add
12808          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12809         * @param {Roo.bootstrap.ComboBox} combo This combo box
12810         */
12811         'add' : true,
12812         /**
12813          * @event edit
12814          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12815         * @param {Roo.bootstrap.ComboBox} combo This combo box
12816         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12817         */
12818         'edit' : true,
12819         /**
12820          * @event remove
12821          * Fires when the remove value from the combobox array
12822         * @param {Roo.bootstrap.ComboBox} combo This combo box
12823         */
12824         'remove' : true,
12825         /**
12826          * @event afterremove
12827          * Fires when the remove value from the combobox array
12828         * @param {Roo.bootstrap.ComboBox} combo This combo box
12829         */
12830         'afterremove' : true,
12831         /**
12832          * @event specialfilter
12833          * Fires when specialfilter
12834             * @param {Roo.bootstrap.ComboBox} combo This combo box
12835             */
12836         'specialfilter' : true,
12837         /**
12838          * @event tick
12839          * Fires when tick the element
12840             * @param {Roo.bootstrap.ComboBox} combo This combo box
12841             */
12842         'tick' : true,
12843         /**
12844          * @event touchviewdisplay
12845          * Fires when touch view require special display (default is using displayField)
12846             * @param {Roo.bootstrap.ComboBox} combo This combo box
12847             * @param {Object} cfg set html .
12848             */
12849         'touchviewdisplay' : true
12850         
12851     });
12852     
12853     this.item = [];
12854     this.tickItems = [];
12855     
12856     this.selectedIndex = -1;
12857     if(this.mode == 'local'){
12858         if(config.queryDelay === undefined){
12859             this.queryDelay = 10;
12860         }
12861         if(config.minChars === undefined){
12862             this.minChars = 0;
12863         }
12864     }
12865 };
12866
12867 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12868      
12869     /**
12870      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12871      * rendering into an Roo.Editor, defaults to false)
12872      */
12873     /**
12874      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12875      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12876      */
12877     /**
12878      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12879      */
12880     /**
12881      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12882      * the dropdown list (defaults to undefined, with no header element)
12883      */
12884
12885      /**
12886      * @cfg {String/Roo.Template} tpl The template to use to render the output
12887      */
12888      
12889      /**
12890      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12891      */
12892     listWidth: undefined,
12893     /**
12894      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12895      * mode = 'remote' or 'text' if mode = 'local')
12896      */
12897     displayField: undefined,
12898     
12899     /**
12900      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12901      * mode = 'remote' or 'value' if mode = 'local'). 
12902      * Note: use of a valueField requires the user make a selection
12903      * in order for a value to be mapped.
12904      */
12905     valueField: undefined,
12906     /**
12907      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12908      */
12909     modalTitle : '',
12910     
12911     /**
12912      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12913      * field's data value (defaults to the underlying DOM element's name)
12914      */
12915     hiddenName: undefined,
12916     /**
12917      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12918      */
12919     listClass: '',
12920     /**
12921      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12922      */
12923     selectedClass: 'active',
12924     
12925     /**
12926      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12927      */
12928     shadow:'sides',
12929     /**
12930      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12931      * anchor positions (defaults to 'tl-bl')
12932      */
12933     listAlign: 'tl-bl?',
12934     /**
12935      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12936      */
12937     maxHeight: 300,
12938     /**
12939      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12940      * query specified by the allQuery config option (defaults to 'query')
12941      */
12942     triggerAction: 'query',
12943     /**
12944      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12945      * (defaults to 4, does not apply if editable = false)
12946      */
12947     minChars : 4,
12948     /**
12949      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12950      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12951      */
12952     typeAhead: false,
12953     /**
12954      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12955      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12956      */
12957     queryDelay: 500,
12958     /**
12959      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12960      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12961      */
12962     pageSize: 0,
12963     /**
12964      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12965      * when editable = true (defaults to false)
12966      */
12967     selectOnFocus:false,
12968     /**
12969      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12970      */
12971     queryParam: 'query',
12972     /**
12973      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12974      * when mode = 'remote' (defaults to 'Loading...')
12975      */
12976     loadingText: 'Loading...',
12977     /**
12978      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12979      */
12980     resizable: false,
12981     /**
12982      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12983      */
12984     handleHeight : 8,
12985     /**
12986      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12987      * traditional select (defaults to true)
12988      */
12989     editable: true,
12990     /**
12991      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12992      */
12993     allQuery: '',
12994     /**
12995      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12996      */
12997     mode: 'remote',
12998     /**
12999      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13000      * listWidth has a higher value)
13001      */
13002     minListWidth : 70,
13003     /**
13004      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13005      * allow the user to set arbitrary text into the field (defaults to false)
13006      */
13007     forceSelection:false,
13008     /**
13009      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13010      * if typeAhead = true (defaults to 250)
13011      */
13012     typeAheadDelay : 250,
13013     /**
13014      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13015      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13016      */
13017     valueNotFoundText : undefined,
13018     /**
13019      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13020      */
13021     blockFocus : false,
13022     
13023     /**
13024      * @cfg {Boolean} disableClear Disable showing of clear button.
13025      */
13026     disableClear : false,
13027     /**
13028      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13029      */
13030     alwaysQuery : false,
13031     
13032     /**
13033      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13034      */
13035     multiple : false,
13036     
13037     /**
13038      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13039      */
13040     invalidClass : "has-warning",
13041     
13042     /**
13043      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13044      */
13045     validClass : "has-success",
13046     
13047     /**
13048      * @cfg {Boolean} specialFilter (true|false) special filter default false
13049      */
13050     specialFilter : false,
13051     
13052     /**
13053      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13054      */
13055     mobileTouchView : true,
13056     
13057     /**
13058      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13059      */
13060     useNativeIOS : false,
13061     
13062     /**
13063      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13064      */
13065     mobile_restrict_height : false,
13066     
13067     ios_options : false,
13068     
13069     //private
13070     addicon : false,
13071     editicon: false,
13072     
13073     page: 0,
13074     hasQuery: false,
13075     append: false,
13076     loadNext: false,
13077     autoFocus : true,
13078     tickable : false,
13079     btnPosition : 'right',
13080     triggerList : true,
13081     showToggleBtn : true,
13082     animate : true,
13083     emptyResultText: 'Empty',
13084     triggerText : 'Select',
13085     emptyTitle : '',
13086     
13087     // element that contains real text value.. (when hidden is used..)
13088     
13089     getAutoCreate : function()
13090     {   
13091         var cfg = false;
13092         //render
13093         /*
13094          * Render classic select for iso
13095          */
13096         
13097         if(Roo.isIOS && this.useNativeIOS){
13098             cfg = this.getAutoCreateNativeIOS();
13099             return cfg;
13100         }
13101         
13102         /*
13103          * Touch Devices
13104          */
13105         
13106         if(Roo.isTouch && this.mobileTouchView){
13107             cfg = this.getAutoCreateTouchView();
13108             return cfg;;
13109         }
13110         
13111         /*
13112          *  Normal ComboBox
13113          */
13114         if(!this.tickable){
13115             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13116             return cfg;
13117         }
13118         
13119         /*
13120          *  ComboBox with tickable selections
13121          */
13122              
13123         var align = this.labelAlign || this.parentLabelAlign();
13124         
13125         cfg = {
13126             cls : 'form-group roo-combobox-tickable' //input-group
13127         };
13128         
13129         var btn_text_select = '';
13130         var btn_text_done = '';
13131         var btn_text_cancel = '';
13132         
13133         if (this.btn_text_show) {
13134             btn_text_select = 'Select';
13135             btn_text_done = 'Done';
13136             btn_text_cancel = 'Cancel'; 
13137         }
13138         
13139         var buttons = {
13140             tag : 'div',
13141             cls : 'tickable-buttons',
13142             cn : [
13143                 {
13144                     tag : 'button',
13145                     type : 'button',
13146                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13147                     //html : this.triggerText
13148                     html: btn_text_select
13149                 },
13150                 {
13151                     tag : 'button',
13152                     type : 'button',
13153                     name : 'ok',
13154                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13155                     //html : 'Done'
13156                     html: btn_text_done
13157                 },
13158                 {
13159                     tag : 'button',
13160                     type : 'button',
13161                     name : 'cancel',
13162                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13163                     //html : 'Cancel'
13164                     html: btn_text_cancel
13165                 }
13166             ]
13167         };
13168         
13169         if(this.editable){
13170             buttons.cn.unshift({
13171                 tag: 'input',
13172                 cls: 'roo-select2-search-field-input'
13173             });
13174         }
13175         
13176         var _this = this;
13177         
13178         Roo.each(buttons.cn, function(c){
13179             if (_this.size) {
13180                 c.cls += ' btn-' + _this.size;
13181             }
13182
13183             if (_this.disabled) {
13184                 c.disabled = true;
13185             }
13186         });
13187         
13188         var box = {
13189             tag: 'div',
13190             cn: [
13191                 {
13192                     tag: 'input',
13193                     type : 'hidden',
13194                     cls: 'form-hidden-field'
13195                 },
13196                 {
13197                     tag: 'ul',
13198                     cls: 'roo-select2-choices',
13199                     cn:[
13200                         {
13201                             tag: 'li',
13202                             cls: 'roo-select2-search-field',
13203                             cn: [
13204                                 buttons
13205                             ]
13206                         }
13207                     ]
13208                 }
13209             ]
13210         };
13211         
13212         var combobox = {
13213             cls: 'roo-select2-container input-group roo-select2-container-multi',
13214             cn: [
13215                 box
13216 //                {
13217 //                    tag: 'ul',
13218 //                    cls: 'typeahead typeahead-long dropdown-menu',
13219 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13220 //                }
13221             ]
13222         };
13223         
13224         if(this.hasFeedback && !this.allowBlank){
13225             
13226             var feedback = {
13227                 tag: 'span',
13228                 cls: 'glyphicon form-control-feedback'
13229             };
13230
13231             combobox.cn.push(feedback);
13232         }
13233         
13234         
13235         if (align ==='left' && this.fieldLabel.length) {
13236             
13237             cfg.cls += ' roo-form-group-label-left';
13238             
13239             cfg.cn = [
13240                 {
13241                     tag : 'i',
13242                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13243                     tooltip : 'This field is required'
13244                 },
13245                 {
13246                     tag: 'label',
13247                     'for' :  id,
13248                     cls : 'control-label',
13249                     html : this.fieldLabel
13250
13251                 },
13252                 {
13253                     cls : "", 
13254                     cn: [
13255                         combobox
13256                     ]
13257                 }
13258
13259             ];
13260             
13261             var labelCfg = cfg.cn[1];
13262             var contentCfg = cfg.cn[2];
13263             
13264
13265             if(this.indicatorpos == 'right'){
13266                 
13267                 cfg.cn = [
13268                     {
13269                         tag: 'label',
13270                         'for' :  id,
13271                         cls : 'control-label',
13272                         cn : [
13273                             {
13274                                 tag : 'span',
13275                                 html : this.fieldLabel
13276                             },
13277                             {
13278                                 tag : 'i',
13279                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13280                                 tooltip : 'This field is required'
13281                             }
13282                         ]
13283                     },
13284                     {
13285                         cls : "",
13286                         cn: [
13287                             combobox
13288                         ]
13289                     }
13290
13291                 ];
13292                 
13293                 
13294                 
13295                 labelCfg = cfg.cn[0];
13296                 contentCfg = cfg.cn[1];
13297             
13298             }
13299             
13300             if(this.labelWidth > 12){
13301                 labelCfg.style = "width: " + this.labelWidth + 'px';
13302             }
13303             
13304             if(this.labelWidth < 13 && this.labelmd == 0){
13305                 this.labelmd = this.labelWidth;
13306             }
13307             
13308             if(this.labellg > 0){
13309                 labelCfg.cls += ' col-lg-' + this.labellg;
13310                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13311             }
13312             
13313             if(this.labelmd > 0){
13314                 labelCfg.cls += ' col-md-' + this.labelmd;
13315                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13316             }
13317             
13318             if(this.labelsm > 0){
13319                 labelCfg.cls += ' col-sm-' + this.labelsm;
13320                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13321             }
13322             
13323             if(this.labelxs > 0){
13324                 labelCfg.cls += ' col-xs-' + this.labelxs;
13325                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13326             }
13327                 
13328                 
13329         } else if ( this.fieldLabel.length) {
13330 //                Roo.log(" label");
13331                  cfg.cn = [
13332                     {
13333                         tag : 'i',
13334                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13335                         tooltip : 'This field is required'
13336                     },
13337                     {
13338                         tag: 'label',
13339                         //cls : 'input-group-addon',
13340                         html : this.fieldLabel
13341                     },
13342                     combobox
13343                 ];
13344                 
13345                 if(this.indicatorpos == 'right'){
13346                     cfg.cn = [
13347                         {
13348                             tag: 'label',
13349                             //cls : 'input-group-addon',
13350                             html : this.fieldLabel
13351                         },
13352                         {
13353                             tag : 'i',
13354                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13355                             tooltip : 'This field is required'
13356                         },
13357                         combobox
13358                     ];
13359                     
13360                 }
13361
13362         } else {
13363             
13364 //                Roo.log(" no label && no align");
13365                 cfg = combobox
13366                      
13367                 
13368         }
13369          
13370         var settings=this;
13371         ['xs','sm','md','lg'].map(function(size){
13372             if (settings[size]) {
13373                 cfg.cls += ' col-' + size + '-' + settings[size];
13374             }
13375         });
13376         
13377         return cfg;
13378         
13379     },
13380     
13381     _initEventsCalled : false,
13382     
13383     // private
13384     initEvents: function()
13385     {   
13386         if (this._initEventsCalled) { // as we call render... prevent looping...
13387             return;
13388         }
13389         this._initEventsCalled = true;
13390         
13391         if (!this.store) {
13392             throw "can not find store for combo";
13393         }
13394         
13395         this.indicator = this.indicatorEl();
13396         
13397         this.store = Roo.factory(this.store, Roo.data);
13398         this.store.parent = this;
13399         
13400         // if we are building from html. then this element is so complex, that we can not really
13401         // use the rendered HTML.
13402         // so we have to trash and replace the previous code.
13403         if (Roo.XComponent.build_from_html) {
13404             // remove this element....
13405             var e = this.el.dom, k=0;
13406             while (e ) { e = e.previousSibling;  ++k;}
13407
13408             this.el.remove();
13409             
13410             this.el=false;
13411             this.rendered = false;
13412             
13413             this.render(this.parent().getChildContainer(true), k);
13414         }
13415         
13416         if(Roo.isIOS && this.useNativeIOS){
13417             this.initIOSView();
13418             return;
13419         }
13420         
13421         /*
13422          * Touch Devices
13423          */
13424         
13425         if(Roo.isTouch && this.mobileTouchView){
13426             this.initTouchView();
13427             return;
13428         }
13429         
13430         if(this.tickable){
13431             this.initTickableEvents();
13432             return;
13433         }
13434         
13435         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13436         
13437         if(this.hiddenName){
13438             
13439             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13440             
13441             this.hiddenField.dom.value =
13442                 this.hiddenValue !== undefined ? this.hiddenValue :
13443                 this.value !== undefined ? this.value : '';
13444
13445             // prevent input submission
13446             this.el.dom.removeAttribute('name');
13447             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13448              
13449              
13450         }
13451         //if(Roo.isGecko){
13452         //    this.el.dom.setAttribute('autocomplete', 'off');
13453         //}
13454         
13455         var cls = 'x-combo-list';
13456         
13457         //this.list = new Roo.Layer({
13458         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13459         //});
13460         
13461         var _this = this;
13462         
13463         (function(){
13464             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13465             _this.list.setWidth(lw);
13466         }).defer(100);
13467         
13468         this.list.on('mouseover', this.onViewOver, this);
13469         this.list.on('mousemove', this.onViewMove, this);
13470         this.list.on('scroll', this.onViewScroll, this);
13471         
13472         /*
13473         this.list.swallowEvent('mousewheel');
13474         this.assetHeight = 0;
13475
13476         if(this.title){
13477             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13478             this.assetHeight += this.header.getHeight();
13479         }
13480
13481         this.innerList = this.list.createChild({cls:cls+'-inner'});
13482         this.innerList.on('mouseover', this.onViewOver, this);
13483         this.innerList.on('mousemove', this.onViewMove, this);
13484         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13485         
13486         if(this.allowBlank && !this.pageSize && !this.disableClear){
13487             this.footer = this.list.createChild({cls:cls+'-ft'});
13488             this.pageTb = new Roo.Toolbar(this.footer);
13489            
13490         }
13491         if(this.pageSize){
13492             this.footer = this.list.createChild({cls:cls+'-ft'});
13493             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13494                     {pageSize: this.pageSize});
13495             
13496         }
13497         
13498         if (this.pageTb && this.allowBlank && !this.disableClear) {
13499             var _this = this;
13500             this.pageTb.add(new Roo.Toolbar.Fill(), {
13501                 cls: 'x-btn-icon x-btn-clear',
13502                 text: '&#160;',
13503                 handler: function()
13504                 {
13505                     _this.collapse();
13506                     _this.clearValue();
13507                     _this.onSelect(false, -1);
13508                 }
13509             });
13510         }
13511         if (this.footer) {
13512             this.assetHeight += this.footer.getHeight();
13513         }
13514         */
13515             
13516         if(!this.tpl){
13517             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13518         }
13519
13520         this.view = new Roo.View(this.list, this.tpl, {
13521             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13522         });
13523         //this.view.wrapEl.setDisplayed(false);
13524         this.view.on('click', this.onViewClick, this);
13525         
13526         
13527         this.store.on('beforeload', this.onBeforeLoad, this);
13528         this.store.on('load', this.onLoad, this);
13529         this.store.on('loadexception', this.onLoadException, this);
13530         /*
13531         if(this.resizable){
13532             this.resizer = new Roo.Resizable(this.list,  {
13533                pinned:true, handles:'se'
13534             });
13535             this.resizer.on('resize', function(r, w, h){
13536                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13537                 this.listWidth = w;
13538                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13539                 this.restrictHeight();
13540             }, this);
13541             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13542         }
13543         */
13544         if(!this.editable){
13545             this.editable = true;
13546             this.setEditable(false);
13547         }
13548         
13549         /*
13550         
13551         if (typeof(this.events.add.listeners) != 'undefined') {
13552             
13553             this.addicon = this.wrap.createChild(
13554                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13555        
13556             this.addicon.on('click', function(e) {
13557                 this.fireEvent('add', this);
13558             }, this);
13559         }
13560         if (typeof(this.events.edit.listeners) != 'undefined') {
13561             
13562             this.editicon = this.wrap.createChild(
13563                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13564             if (this.addicon) {
13565                 this.editicon.setStyle('margin-left', '40px');
13566             }
13567             this.editicon.on('click', function(e) {
13568                 
13569                 // we fire even  if inothing is selected..
13570                 this.fireEvent('edit', this, this.lastData );
13571                 
13572             }, this);
13573         }
13574         */
13575         
13576         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13577             "up" : function(e){
13578                 this.inKeyMode = true;
13579                 this.selectPrev();
13580             },
13581
13582             "down" : function(e){
13583                 if(!this.isExpanded()){
13584                     this.onTriggerClick();
13585                 }else{
13586                     this.inKeyMode = true;
13587                     this.selectNext();
13588                 }
13589             },
13590
13591             "enter" : function(e){
13592 //                this.onViewClick();
13593                 //return true;
13594                 this.collapse();
13595                 
13596                 if(this.fireEvent("specialkey", this, e)){
13597                     this.onViewClick(false);
13598                 }
13599                 
13600                 return true;
13601             },
13602
13603             "esc" : function(e){
13604                 this.collapse();
13605             },
13606
13607             "tab" : function(e){
13608                 this.collapse();
13609                 
13610                 if(this.fireEvent("specialkey", this, e)){
13611                     this.onViewClick(false);
13612                 }
13613                 
13614                 return true;
13615             },
13616
13617             scope : this,
13618
13619             doRelay : function(foo, bar, hname){
13620                 if(hname == 'down' || this.scope.isExpanded()){
13621                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13622                 }
13623                 return true;
13624             },
13625
13626             forceKeyDown: true
13627         });
13628         
13629         
13630         this.queryDelay = Math.max(this.queryDelay || 10,
13631                 this.mode == 'local' ? 10 : 250);
13632         
13633         
13634         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13635         
13636         if(this.typeAhead){
13637             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13638         }
13639         if(this.editable !== false){
13640             this.inputEl().on("keyup", this.onKeyUp, this);
13641         }
13642         if(this.forceSelection){
13643             this.inputEl().on('blur', this.doForce, this);
13644         }
13645         
13646         if(this.multiple){
13647             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13648             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13649         }
13650     },
13651     
13652     initTickableEvents: function()
13653     {   
13654         this.createList();
13655         
13656         if(this.hiddenName){
13657             
13658             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13659             
13660             this.hiddenField.dom.value =
13661                 this.hiddenValue !== undefined ? this.hiddenValue :
13662                 this.value !== undefined ? this.value : '';
13663
13664             // prevent input submission
13665             this.el.dom.removeAttribute('name');
13666             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13667              
13668              
13669         }
13670         
13671 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13672         
13673         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13674         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13675         if(this.triggerList){
13676             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13677         }
13678          
13679         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13680         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13681         
13682         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13683         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13684         
13685         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13686         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13687         
13688         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13689         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13690         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13691         
13692         this.okBtn.hide();
13693         this.cancelBtn.hide();
13694         
13695         var _this = this;
13696         
13697         (function(){
13698             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13699             _this.list.setWidth(lw);
13700         }).defer(100);
13701         
13702         this.list.on('mouseover', this.onViewOver, this);
13703         this.list.on('mousemove', this.onViewMove, this);
13704         
13705         this.list.on('scroll', this.onViewScroll, this);
13706         
13707         if(!this.tpl){
13708             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13709                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13710         }
13711
13712         this.view = new Roo.View(this.list, this.tpl, {
13713             singleSelect:true,
13714             tickable:true,
13715             parent:this,
13716             store: this.store,
13717             selectedClass: this.selectedClass
13718         });
13719         
13720         //this.view.wrapEl.setDisplayed(false);
13721         this.view.on('click', this.onViewClick, this);
13722         
13723         
13724         
13725         this.store.on('beforeload', this.onBeforeLoad, this);
13726         this.store.on('load', this.onLoad, this);
13727         this.store.on('loadexception', this.onLoadException, this);
13728         
13729         if(this.editable){
13730             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13731                 "up" : function(e){
13732                     this.inKeyMode = true;
13733                     this.selectPrev();
13734                 },
13735
13736                 "down" : function(e){
13737                     this.inKeyMode = true;
13738                     this.selectNext();
13739                 },
13740
13741                 "enter" : function(e){
13742                     if(this.fireEvent("specialkey", this, e)){
13743                         this.onViewClick(false);
13744                     }
13745                     
13746                     return true;
13747                 },
13748
13749                 "esc" : function(e){
13750                     this.onTickableFooterButtonClick(e, false, false);
13751                 },
13752
13753                 "tab" : function(e){
13754                     this.fireEvent("specialkey", this, e);
13755                     
13756                     this.onTickableFooterButtonClick(e, false, false);
13757                     
13758                     return true;
13759                 },
13760
13761                 scope : this,
13762
13763                 doRelay : function(e, fn, key){
13764                     if(this.scope.isExpanded()){
13765                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13766                     }
13767                     return true;
13768                 },
13769
13770                 forceKeyDown: true
13771             });
13772         }
13773         
13774         this.queryDelay = Math.max(this.queryDelay || 10,
13775                 this.mode == 'local' ? 10 : 250);
13776         
13777         
13778         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13779         
13780         if(this.typeAhead){
13781             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13782         }
13783         
13784         if(this.editable !== false){
13785             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13786         }
13787         
13788         this.indicator = this.indicatorEl();
13789         
13790         if(this.indicator){
13791             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13792             this.indicator.hide();
13793         }
13794         
13795     },
13796
13797     onDestroy : function(){
13798         if(this.view){
13799             this.view.setStore(null);
13800             this.view.el.removeAllListeners();
13801             this.view.el.remove();
13802             this.view.purgeListeners();
13803         }
13804         if(this.list){
13805             this.list.dom.innerHTML  = '';
13806         }
13807         
13808         if(this.store){
13809             this.store.un('beforeload', this.onBeforeLoad, this);
13810             this.store.un('load', this.onLoad, this);
13811             this.store.un('loadexception', this.onLoadException, this);
13812         }
13813         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13814     },
13815
13816     // private
13817     fireKey : function(e){
13818         if(e.isNavKeyPress() && !this.list.isVisible()){
13819             this.fireEvent("specialkey", this, e);
13820         }
13821     },
13822
13823     // private
13824     onResize: function(w, h){
13825 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13826 //        
13827 //        if(typeof w != 'number'){
13828 //            // we do not handle it!?!?
13829 //            return;
13830 //        }
13831 //        var tw = this.trigger.getWidth();
13832 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13833 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13834 //        var x = w - tw;
13835 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13836 //            
13837 //        //this.trigger.setStyle('left', x+'px');
13838 //        
13839 //        if(this.list && this.listWidth === undefined){
13840 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13841 //            this.list.setWidth(lw);
13842 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13843 //        }
13844         
13845     
13846         
13847     },
13848
13849     /**
13850      * Allow or prevent the user from directly editing the field text.  If false is passed,
13851      * the user will only be able to select from the items defined in the dropdown list.  This method
13852      * is the runtime equivalent of setting the 'editable' config option at config time.
13853      * @param {Boolean} value True to allow the user to directly edit the field text
13854      */
13855     setEditable : function(value){
13856         if(value == this.editable){
13857             return;
13858         }
13859         this.editable = value;
13860         if(!value){
13861             this.inputEl().dom.setAttribute('readOnly', true);
13862             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13863             this.inputEl().addClass('x-combo-noedit');
13864         }else{
13865             this.inputEl().dom.setAttribute('readOnly', false);
13866             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13867             this.inputEl().removeClass('x-combo-noedit');
13868         }
13869     },
13870
13871     // private
13872     
13873     onBeforeLoad : function(combo,opts){
13874         if(!this.hasFocus){
13875             return;
13876         }
13877          if (!opts.add) {
13878             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13879          }
13880         this.restrictHeight();
13881         this.selectedIndex = -1;
13882     },
13883
13884     // private
13885     onLoad : function(){
13886         
13887         this.hasQuery = false;
13888         
13889         if(!this.hasFocus){
13890             return;
13891         }
13892         
13893         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13894             this.loading.hide();
13895         }
13896         
13897         if(this.store.getCount() > 0){
13898             
13899             this.expand();
13900             this.restrictHeight();
13901             if(this.lastQuery == this.allQuery){
13902                 if(this.editable && !this.tickable){
13903                     this.inputEl().dom.select();
13904                 }
13905                 
13906                 if(
13907                     !this.selectByValue(this.value, true) &&
13908                     this.autoFocus && 
13909                     (
13910                         !this.store.lastOptions ||
13911                         typeof(this.store.lastOptions.add) == 'undefined' || 
13912                         this.store.lastOptions.add != true
13913                     )
13914                 ){
13915                     this.select(0, true);
13916                 }
13917             }else{
13918                 if(this.autoFocus){
13919                     this.selectNext();
13920                 }
13921                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13922                     this.taTask.delay(this.typeAheadDelay);
13923                 }
13924             }
13925         }else{
13926             this.onEmptyResults();
13927         }
13928         
13929         //this.el.focus();
13930     },
13931     // private
13932     onLoadException : function()
13933     {
13934         this.hasQuery = false;
13935         
13936         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13937             this.loading.hide();
13938         }
13939         
13940         if(this.tickable && this.editable){
13941             return;
13942         }
13943         
13944         this.collapse();
13945         // only causes errors at present
13946         //Roo.log(this.store.reader.jsonData);
13947         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13948             // fixme
13949             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13950         //}
13951         
13952         
13953     },
13954     // private
13955     onTypeAhead : function(){
13956         if(this.store.getCount() > 0){
13957             var r = this.store.getAt(0);
13958             var newValue = r.data[this.displayField];
13959             var len = newValue.length;
13960             var selStart = this.getRawValue().length;
13961             
13962             if(selStart != len){
13963                 this.setRawValue(newValue);
13964                 this.selectText(selStart, newValue.length);
13965             }
13966         }
13967     },
13968
13969     // private
13970     onSelect : function(record, index){
13971         
13972         if(this.fireEvent('beforeselect', this, record, index) !== false){
13973         
13974             this.setFromData(index > -1 ? record.data : false);
13975             
13976             this.collapse();
13977             this.fireEvent('select', this, record, index);
13978         }
13979     },
13980
13981     /**
13982      * Returns the currently selected field value or empty string if no value is set.
13983      * @return {String} value The selected value
13984      */
13985     getValue : function()
13986     {
13987         if(Roo.isIOS && this.useNativeIOS){
13988             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13989         }
13990         
13991         if(this.multiple){
13992             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13993         }
13994         
13995         if(this.valueField){
13996             return typeof this.value != 'undefined' ? this.value : '';
13997         }else{
13998             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13999         }
14000     },
14001     
14002     getRawValue : function()
14003     {
14004         if(Roo.isIOS && this.useNativeIOS){
14005             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14006         }
14007         
14008         var v = this.inputEl().getValue();
14009         
14010         return v;
14011     },
14012
14013     /**
14014      * Clears any text/value currently set in the field
14015      */
14016     clearValue : function(){
14017         
14018         if(this.hiddenField){
14019             this.hiddenField.dom.value = '';
14020         }
14021         this.value = '';
14022         this.setRawValue('');
14023         this.lastSelectionText = '';
14024         this.lastData = false;
14025         
14026         var close = this.closeTriggerEl();
14027         
14028         if(close){
14029             close.hide();
14030         }
14031         
14032         this.validate();
14033         
14034     },
14035
14036     /**
14037      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14038      * will be displayed in the field.  If the value does not match the data value of an existing item,
14039      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14040      * Otherwise the field will be blank (although the value will still be set).
14041      * @param {String} value The value to match
14042      */
14043     setValue : function(v)
14044     {
14045         if(Roo.isIOS && this.useNativeIOS){
14046             this.setIOSValue(v);
14047             return;
14048         }
14049         
14050         if(this.multiple){
14051             this.syncValue();
14052             return;
14053         }
14054         
14055         var text = v;
14056         if(this.valueField){
14057             var r = this.findRecord(this.valueField, v);
14058             if(r){
14059                 text = r.data[this.displayField];
14060             }else if(this.valueNotFoundText !== undefined){
14061                 text = this.valueNotFoundText;
14062             }
14063         }
14064         this.lastSelectionText = text;
14065         if(this.hiddenField){
14066             this.hiddenField.dom.value = v;
14067         }
14068         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14069         this.value = v;
14070         
14071         var close = this.closeTriggerEl();
14072         
14073         if(close){
14074             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14075         }
14076         
14077         this.validate();
14078     },
14079     /**
14080      * @property {Object} the last set data for the element
14081      */
14082     
14083     lastData : false,
14084     /**
14085      * Sets the value of the field based on a object which is related to the record format for the store.
14086      * @param {Object} value the value to set as. or false on reset?
14087      */
14088     setFromData : function(o){
14089         
14090         if(this.multiple){
14091             this.addItem(o);
14092             return;
14093         }
14094             
14095         var dv = ''; // display value
14096         var vv = ''; // value value..
14097         this.lastData = o;
14098         if (this.displayField) {
14099             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14100         } else {
14101             // this is an error condition!!!
14102             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14103         }
14104         
14105         if(this.valueField){
14106             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14107         }
14108         
14109         var close = this.closeTriggerEl();
14110         
14111         if(close){
14112             if(dv.length || vv * 1 > 0){
14113                 close.show() ;
14114                 this.blockFocus=true;
14115             } else {
14116                 close.hide();
14117             }             
14118         }
14119         
14120         if(this.hiddenField){
14121             this.hiddenField.dom.value = vv;
14122             
14123             this.lastSelectionText = dv;
14124             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14125             this.value = vv;
14126             return;
14127         }
14128         // no hidden field.. - we store the value in 'value', but still display
14129         // display field!!!!
14130         this.lastSelectionText = dv;
14131         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14132         this.value = vv;
14133         
14134         
14135         
14136     },
14137     // private
14138     reset : function(){
14139         // overridden so that last data is reset..
14140         
14141         if(this.multiple){
14142             this.clearItem();
14143             return;
14144         }
14145         
14146         this.setValue(this.originalValue);
14147         //this.clearInvalid();
14148         this.lastData = false;
14149         if (this.view) {
14150             this.view.clearSelections();
14151         }
14152         
14153         this.validate();
14154     },
14155     // private
14156     findRecord : function(prop, value){
14157         var record;
14158         if(this.store.getCount() > 0){
14159             this.store.each(function(r){
14160                 if(r.data[prop] == value){
14161                     record = r;
14162                     return false;
14163                 }
14164                 return true;
14165             });
14166         }
14167         return record;
14168     },
14169     
14170     getName: function()
14171     {
14172         // returns hidden if it's set..
14173         if (!this.rendered) {return ''};
14174         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14175         
14176     },
14177     // private
14178     onViewMove : function(e, t){
14179         this.inKeyMode = false;
14180     },
14181
14182     // private
14183     onViewOver : function(e, t){
14184         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14185             return;
14186         }
14187         var item = this.view.findItemFromChild(t);
14188         
14189         if(item){
14190             var index = this.view.indexOf(item);
14191             this.select(index, false);
14192         }
14193     },
14194
14195     // private
14196     onViewClick : function(view, doFocus, el, e)
14197     {
14198         var index = this.view.getSelectedIndexes()[0];
14199         
14200         var r = this.store.getAt(index);
14201         
14202         if(this.tickable){
14203             
14204             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14205                 return;
14206             }
14207             
14208             var rm = false;
14209             var _this = this;
14210             
14211             Roo.each(this.tickItems, function(v,k){
14212                 
14213                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14214                     Roo.log(v);
14215                     _this.tickItems.splice(k, 1);
14216                     
14217                     if(typeof(e) == 'undefined' && view == false){
14218                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14219                     }
14220                     
14221                     rm = true;
14222                     return;
14223                 }
14224             });
14225             
14226             if(rm){
14227                 return;
14228             }
14229             
14230             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14231                 this.tickItems.push(r.data);
14232             }
14233             
14234             if(typeof(e) == 'undefined' && view == false){
14235                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14236             }
14237                     
14238             return;
14239         }
14240         
14241         if(r){
14242             this.onSelect(r, index);
14243         }
14244         if(doFocus !== false && !this.blockFocus){
14245             this.inputEl().focus();
14246         }
14247     },
14248
14249     // private
14250     restrictHeight : function(){
14251         //this.innerList.dom.style.height = '';
14252         //var inner = this.innerList.dom;
14253         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14254         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14255         //this.list.beginUpdate();
14256         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14257         this.list.alignTo(this.inputEl(), this.listAlign);
14258         this.list.alignTo(this.inputEl(), this.listAlign);
14259         //this.list.endUpdate();
14260     },
14261
14262     // private
14263     onEmptyResults : function(){
14264         
14265         if(this.tickable && this.editable){
14266             this.hasFocus = false;
14267             this.restrictHeight();
14268             return;
14269         }
14270         
14271         this.collapse();
14272     },
14273
14274     /**
14275      * Returns true if the dropdown list is expanded, else false.
14276      */
14277     isExpanded : function(){
14278         return this.list.isVisible();
14279     },
14280
14281     /**
14282      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14283      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14284      * @param {String} value The data value of the item to select
14285      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14286      * selected item if it is not currently in view (defaults to true)
14287      * @return {Boolean} True if the value matched an item in the list, else false
14288      */
14289     selectByValue : function(v, scrollIntoView){
14290         if(v !== undefined && v !== null){
14291             var r = this.findRecord(this.valueField || this.displayField, v);
14292             if(r){
14293                 this.select(this.store.indexOf(r), scrollIntoView);
14294                 return true;
14295             }
14296         }
14297         return false;
14298     },
14299
14300     /**
14301      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14302      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14303      * @param {Number} index The zero-based index of the list item to select
14304      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14305      * selected item if it is not currently in view (defaults to true)
14306      */
14307     select : function(index, scrollIntoView){
14308         this.selectedIndex = index;
14309         this.view.select(index);
14310         if(scrollIntoView !== false){
14311             var el = this.view.getNode(index);
14312             /*
14313              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14314              */
14315             if(el){
14316                 this.list.scrollChildIntoView(el, false);
14317             }
14318         }
14319     },
14320
14321     // private
14322     selectNext : function(){
14323         var ct = this.store.getCount();
14324         if(ct > 0){
14325             if(this.selectedIndex == -1){
14326                 this.select(0);
14327             }else if(this.selectedIndex < ct-1){
14328                 this.select(this.selectedIndex+1);
14329             }
14330         }
14331     },
14332
14333     // private
14334     selectPrev : function(){
14335         var ct = this.store.getCount();
14336         if(ct > 0){
14337             if(this.selectedIndex == -1){
14338                 this.select(0);
14339             }else if(this.selectedIndex != 0){
14340                 this.select(this.selectedIndex-1);
14341             }
14342         }
14343     },
14344
14345     // private
14346     onKeyUp : function(e){
14347         if(this.editable !== false && !e.isSpecialKey()){
14348             this.lastKey = e.getKey();
14349             this.dqTask.delay(this.queryDelay);
14350         }
14351     },
14352
14353     // private
14354     validateBlur : function(){
14355         return !this.list || !this.list.isVisible();   
14356     },
14357
14358     // private
14359     initQuery : function(){
14360         
14361         var v = this.getRawValue();
14362         
14363         if(this.tickable && this.editable){
14364             v = this.tickableInputEl().getValue();
14365         }
14366         
14367         this.doQuery(v);
14368     },
14369
14370     // private
14371     doForce : function(){
14372         if(this.inputEl().dom.value.length > 0){
14373             this.inputEl().dom.value =
14374                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14375              
14376         }
14377     },
14378
14379     /**
14380      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14381      * query allowing the query action to be canceled if needed.
14382      * @param {String} query The SQL query to execute
14383      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14384      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14385      * saved in the current store (defaults to false)
14386      */
14387     doQuery : function(q, forceAll){
14388         
14389         if(q === undefined || q === null){
14390             q = '';
14391         }
14392         var qe = {
14393             query: q,
14394             forceAll: forceAll,
14395             combo: this,
14396             cancel:false
14397         };
14398         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14399             return false;
14400         }
14401         q = qe.query;
14402         
14403         forceAll = qe.forceAll;
14404         if(forceAll === true || (q.length >= this.minChars)){
14405             
14406             this.hasQuery = true;
14407             
14408             if(this.lastQuery != q || this.alwaysQuery){
14409                 this.lastQuery = q;
14410                 if(this.mode == 'local'){
14411                     this.selectedIndex = -1;
14412                     if(forceAll){
14413                         this.store.clearFilter();
14414                     }else{
14415                         
14416                         if(this.specialFilter){
14417                             this.fireEvent('specialfilter', this);
14418                             this.onLoad();
14419                             return;
14420                         }
14421                         
14422                         this.store.filter(this.displayField, q);
14423                     }
14424                     
14425                     this.store.fireEvent("datachanged", this.store);
14426                     
14427                     this.onLoad();
14428                     
14429                     
14430                 }else{
14431                     
14432                     this.store.baseParams[this.queryParam] = q;
14433                     
14434                     var options = {params : this.getParams(q)};
14435                     
14436                     if(this.loadNext){
14437                         options.add = true;
14438                         options.params.start = this.page * this.pageSize;
14439                     }
14440                     
14441                     this.store.load(options);
14442                     
14443                     /*
14444                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14445                      *  we should expand the list on onLoad
14446                      *  so command out it
14447                      */
14448 //                    this.expand();
14449                 }
14450             }else{
14451                 this.selectedIndex = -1;
14452                 this.onLoad();   
14453             }
14454         }
14455         
14456         this.loadNext = false;
14457     },
14458     
14459     // private
14460     getParams : function(q){
14461         var p = {};
14462         //p[this.queryParam] = q;
14463         
14464         if(this.pageSize){
14465             p.start = 0;
14466             p.limit = this.pageSize;
14467         }
14468         return p;
14469     },
14470
14471     /**
14472      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14473      */
14474     collapse : function(){
14475         if(!this.isExpanded()){
14476             return;
14477         }
14478         
14479         this.list.hide();
14480         
14481         this.hasFocus = false;
14482         
14483         if(this.tickable){
14484             this.okBtn.hide();
14485             this.cancelBtn.hide();
14486             this.trigger.show();
14487             
14488             if(this.editable){
14489                 this.tickableInputEl().dom.value = '';
14490                 this.tickableInputEl().blur();
14491             }
14492             
14493         }
14494         
14495         Roo.get(document).un('mousedown', this.collapseIf, this);
14496         Roo.get(document).un('mousewheel', this.collapseIf, this);
14497         if (!this.editable) {
14498             Roo.get(document).un('keydown', this.listKeyPress, this);
14499         }
14500         this.fireEvent('collapse', this);
14501         
14502         this.validate();
14503     },
14504
14505     // private
14506     collapseIf : function(e){
14507         var in_combo  = e.within(this.el);
14508         var in_list =  e.within(this.list);
14509         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14510         
14511         if (in_combo || in_list || is_list) {
14512             //e.stopPropagation();
14513             return;
14514         }
14515         
14516         if(this.tickable){
14517             this.onTickableFooterButtonClick(e, false, false);
14518         }
14519
14520         this.collapse();
14521         
14522     },
14523
14524     /**
14525      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14526      */
14527     expand : function(){
14528        
14529         if(this.isExpanded() || !this.hasFocus){
14530             return;
14531         }
14532         
14533         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14534         this.list.setWidth(lw);
14535         
14536         Roo.log('expand');
14537         
14538         this.list.show();
14539         
14540         this.restrictHeight();
14541         
14542         if(this.tickable){
14543             
14544             this.tickItems = Roo.apply([], this.item);
14545             
14546             this.okBtn.show();
14547             this.cancelBtn.show();
14548             this.trigger.hide();
14549             
14550             if(this.editable){
14551                 this.tickableInputEl().focus();
14552             }
14553             
14554         }
14555         
14556         Roo.get(document).on('mousedown', this.collapseIf, this);
14557         Roo.get(document).on('mousewheel', this.collapseIf, this);
14558         if (!this.editable) {
14559             Roo.get(document).on('keydown', this.listKeyPress, this);
14560         }
14561         
14562         this.fireEvent('expand', this);
14563     },
14564
14565     // private
14566     // Implements the default empty TriggerField.onTriggerClick function
14567     onTriggerClick : function(e)
14568     {
14569         Roo.log('trigger click');
14570         
14571         if(this.disabled || !this.triggerList){
14572             return;
14573         }
14574         
14575         this.page = 0;
14576         this.loadNext = false;
14577         
14578         if(this.isExpanded()){
14579             this.collapse();
14580             if (!this.blockFocus) {
14581                 this.inputEl().focus();
14582             }
14583             
14584         }else {
14585             this.hasFocus = true;
14586             if(this.triggerAction == 'all') {
14587                 this.doQuery(this.allQuery, true);
14588             } else {
14589                 this.doQuery(this.getRawValue());
14590             }
14591             if (!this.blockFocus) {
14592                 this.inputEl().focus();
14593             }
14594         }
14595     },
14596     
14597     onTickableTriggerClick : function(e)
14598     {
14599         if(this.disabled){
14600             return;
14601         }
14602         
14603         this.page = 0;
14604         this.loadNext = false;
14605         this.hasFocus = true;
14606         
14607         if(this.triggerAction == 'all') {
14608             this.doQuery(this.allQuery, true);
14609         } else {
14610             this.doQuery(this.getRawValue());
14611         }
14612     },
14613     
14614     onSearchFieldClick : function(e)
14615     {
14616         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14617             this.onTickableFooterButtonClick(e, false, false);
14618             return;
14619         }
14620         
14621         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14622             return;
14623         }
14624         
14625         this.page = 0;
14626         this.loadNext = false;
14627         this.hasFocus = true;
14628         
14629         if(this.triggerAction == 'all') {
14630             this.doQuery(this.allQuery, true);
14631         } else {
14632             this.doQuery(this.getRawValue());
14633         }
14634     },
14635     
14636     listKeyPress : function(e)
14637     {
14638         //Roo.log('listkeypress');
14639         // scroll to first matching element based on key pres..
14640         if (e.isSpecialKey()) {
14641             return false;
14642         }
14643         var k = String.fromCharCode(e.getKey()).toUpperCase();
14644         //Roo.log(k);
14645         var match  = false;
14646         var csel = this.view.getSelectedNodes();
14647         var cselitem = false;
14648         if (csel.length) {
14649             var ix = this.view.indexOf(csel[0]);
14650             cselitem  = this.store.getAt(ix);
14651             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14652                 cselitem = false;
14653             }
14654             
14655         }
14656         
14657         this.store.each(function(v) { 
14658             if (cselitem) {
14659                 // start at existing selection.
14660                 if (cselitem.id == v.id) {
14661                     cselitem = false;
14662                 }
14663                 return true;
14664             }
14665                 
14666             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14667                 match = this.store.indexOf(v);
14668                 return false;
14669             }
14670             return true;
14671         }, this);
14672         
14673         if (match === false) {
14674             return true; // no more action?
14675         }
14676         // scroll to?
14677         this.view.select(match);
14678         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14679         sn.scrollIntoView(sn.dom.parentNode, false);
14680     },
14681     
14682     onViewScroll : function(e, t){
14683         
14684         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){
14685             return;
14686         }
14687         
14688         this.hasQuery = true;
14689         
14690         this.loading = this.list.select('.loading', true).first();
14691         
14692         if(this.loading === null){
14693             this.list.createChild({
14694                 tag: 'div',
14695                 cls: 'loading roo-select2-more-results roo-select2-active',
14696                 html: 'Loading more results...'
14697             });
14698             
14699             this.loading = this.list.select('.loading', true).first();
14700             
14701             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14702             
14703             this.loading.hide();
14704         }
14705         
14706         this.loading.show();
14707         
14708         var _combo = this;
14709         
14710         this.page++;
14711         this.loadNext = true;
14712         
14713         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14714         
14715         return;
14716     },
14717     
14718     addItem : function(o)
14719     {   
14720         var dv = ''; // display value
14721         
14722         if (this.displayField) {
14723             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14724         } else {
14725             // this is an error condition!!!
14726             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14727         }
14728         
14729         if(!dv.length){
14730             return;
14731         }
14732         
14733         var choice = this.choices.createChild({
14734             tag: 'li',
14735             cls: 'roo-select2-search-choice',
14736             cn: [
14737                 {
14738                     tag: 'div',
14739                     html: dv
14740                 },
14741                 {
14742                     tag: 'a',
14743                     href: '#',
14744                     cls: 'roo-select2-search-choice-close fa fa-times',
14745                     tabindex: '-1'
14746                 }
14747             ]
14748             
14749         }, this.searchField);
14750         
14751         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14752         
14753         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14754         
14755         this.item.push(o);
14756         
14757         this.lastData = o;
14758         
14759         this.syncValue();
14760         
14761         this.inputEl().dom.value = '';
14762         
14763         this.validate();
14764     },
14765     
14766     onRemoveItem : function(e, _self, o)
14767     {
14768         e.preventDefault();
14769         
14770         this.lastItem = Roo.apply([], this.item);
14771         
14772         var index = this.item.indexOf(o.data) * 1;
14773         
14774         if( index < 0){
14775             Roo.log('not this item?!');
14776             return;
14777         }
14778         
14779         this.item.splice(index, 1);
14780         o.item.remove();
14781         
14782         this.syncValue();
14783         
14784         this.fireEvent('remove', this, e);
14785         
14786         this.validate();
14787         
14788     },
14789     
14790     syncValue : function()
14791     {
14792         if(!this.item.length){
14793             this.clearValue();
14794             return;
14795         }
14796             
14797         var value = [];
14798         var _this = this;
14799         Roo.each(this.item, function(i){
14800             if(_this.valueField){
14801                 value.push(i[_this.valueField]);
14802                 return;
14803             }
14804
14805             value.push(i);
14806         });
14807
14808         this.value = value.join(',');
14809
14810         if(this.hiddenField){
14811             this.hiddenField.dom.value = this.value;
14812         }
14813         
14814         this.store.fireEvent("datachanged", this.store);
14815         
14816         this.validate();
14817     },
14818     
14819     clearItem : function()
14820     {
14821         if(!this.multiple){
14822             return;
14823         }
14824         
14825         this.item = [];
14826         
14827         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14828            c.remove();
14829         });
14830         
14831         this.syncValue();
14832         
14833         this.validate();
14834         
14835         if(this.tickable && !Roo.isTouch){
14836             this.view.refresh();
14837         }
14838     },
14839     
14840     inputEl: function ()
14841     {
14842         if(Roo.isIOS && this.useNativeIOS){
14843             return this.el.select('select.roo-ios-select', true).first();
14844         }
14845         
14846         if(Roo.isTouch && this.mobileTouchView){
14847             return this.el.select('input.form-control',true).first();
14848         }
14849         
14850         if(this.tickable){
14851             return this.searchField;
14852         }
14853         
14854         return this.el.select('input.form-control',true).first();
14855     },
14856     
14857     onTickableFooterButtonClick : function(e, btn, el)
14858     {
14859         e.preventDefault();
14860         
14861         this.lastItem = Roo.apply([], this.item);
14862         
14863         if(btn && btn.name == 'cancel'){
14864             this.tickItems = Roo.apply([], this.item);
14865             this.collapse();
14866             return;
14867         }
14868         
14869         this.clearItem();
14870         
14871         var _this = this;
14872         
14873         Roo.each(this.tickItems, function(o){
14874             _this.addItem(o);
14875         });
14876         
14877         this.collapse();
14878         
14879     },
14880     
14881     validate : function()
14882     {
14883         if(this.getVisibilityEl().hasClass('hidden')){
14884             return true;
14885         }
14886         
14887         var v = this.getRawValue();
14888         
14889         if(this.multiple){
14890             v = this.getValue();
14891         }
14892         
14893         if(this.disabled || this.allowBlank || v.length){
14894             this.markValid();
14895             return true;
14896         }
14897         
14898         this.markInvalid();
14899         return false;
14900     },
14901     
14902     tickableInputEl : function()
14903     {
14904         if(!this.tickable || !this.editable){
14905             return this.inputEl();
14906         }
14907         
14908         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14909     },
14910     
14911     
14912     getAutoCreateTouchView : function()
14913     {
14914         var id = Roo.id();
14915         
14916         var cfg = {
14917             cls: 'form-group' //input-group
14918         };
14919         
14920         var input =  {
14921             tag: 'input',
14922             id : id,
14923             type : this.inputType,
14924             cls : 'form-control x-combo-noedit',
14925             autocomplete: 'new-password',
14926             placeholder : this.placeholder || '',
14927             readonly : true
14928         };
14929         
14930         if (this.name) {
14931             input.name = this.name;
14932         }
14933         
14934         if (this.size) {
14935             input.cls += ' input-' + this.size;
14936         }
14937         
14938         if (this.disabled) {
14939             input.disabled = true;
14940         }
14941         
14942         var inputblock = {
14943             cls : '',
14944             cn : [
14945                 input
14946             ]
14947         };
14948         
14949         if(this.before){
14950             inputblock.cls += ' input-group';
14951             
14952             inputblock.cn.unshift({
14953                 tag :'span',
14954                 cls : 'input-group-addon',
14955                 html : this.before
14956             });
14957         }
14958         
14959         if(this.removable && !this.multiple){
14960             inputblock.cls += ' roo-removable';
14961             
14962             inputblock.cn.push({
14963                 tag: 'button',
14964                 html : 'x',
14965                 cls : 'roo-combo-removable-btn close'
14966             });
14967         }
14968
14969         if(this.hasFeedback && !this.allowBlank){
14970             
14971             inputblock.cls += ' has-feedback';
14972             
14973             inputblock.cn.push({
14974                 tag: 'span',
14975                 cls: 'glyphicon form-control-feedback'
14976             });
14977             
14978         }
14979         
14980         if (this.after) {
14981             
14982             inputblock.cls += (this.before) ? '' : ' input-group';
14983             
14984             inputblock.cn.push({
14985                 tag :'span',
14986                 cls : 'input-group-addon',
14987                 html : this.after
14988             });
14989         }
14990
14991         var box = {
14992             tag: 'div',
14993             cn: [
14994                 {
14995                     tag: 'input',
14996                     type : 'hidden',
14997                     cls: 'form-hidden-field'
14998                 },
14999                 inputblock
15000             ]
15001             
15002         };
15003         
15004         if(this.multiple){
15005             box = {
15006                 tag: 'div',
15007                 cn: [
15008                     {
15009                         tag: 'input',
15010                         type : 'hidden',
15011                         cls: 'form-hidden-field'
15012                     },
15013                     {
15014                         tag: 'ul',
15015                         cls: 'roo-select2-choices',
15016                         cn:[
15017                             {
15018                                 tag: 'li',
15019                                 cls: 'roo-select2-search-field',
15020                                 cn: [
15021
15022                                     inputblock
15023                                 ]
15024                             }
15025                         ]
15026                     }
15027                 ]
15028             }
15029         };
15030         
15031         var combobox = {
15032             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15033             cn: [
15034                 box
15035             ]
15036         };
15037         
15038         if(!this.multiple && this.showToggleBtn){
15039             
15040             var caret = {
15041                         tag: 'span',
15042                         cls: 'caret'
15043             };
15044             
15045             if (this.caret != false) {
15046                 caret = {
15047                      tag: 'i',
15048                      cls: 'fa fa-' + this.caret
15049                 };
15050                 
15051             }
15052             
15053             combobox.cn.push({
15054                 tag :'span',
15055                 cls : 'input-group-addon btn dropdown-toggle',
15056                 cn : [
15057                     caret,
15058                     {
15059                         tag: 'span',
15060                         cls: 'combobox-clear',
15061                         cn  : [
15062                             {
15063                                 tag : 'i',
15064                                 cls: 'icon-remove'
15065                             }
15066                         ]
15067                     }
15068                 ]
15069
15070             })
15071         }
15072         
15073         if(this.multiple){
15074             combobox.cls += ' roo-select2-container-multi';
15075         }
15076         
15077         var align = this.labelAlign || this.parentLabelAlign();
15078         
15079         if (align ==='left' && this.fieldLabel.length) {
15080
15081             cfg.cn = [
15082                 {
15083                    tag : 'i',
15084                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15085                    tooltip : 'This field is required'
15086                 },
15087                 {
15088                     tag: 'label',
15089                     cls : 'control-label',
15090                     html : this.fieldLabel
15091
15092                 },
15093                 {
15094                     cls : '', 
15095                     cn: [
15096                         combobox
15097                     ]
15098                 }
15099             ];
15100             
15101             var labelCfg = cfg.cn[1];
15102             var contentCfg = cfg.cn[2];
15103             
15104
15105             if(this.indicatorpos == 'right'){
15106                 cfg.cn = [
15107                     {
15108                         tag: 'label',
15109                         'for' :  id,
15110                         cls : 'control-label',
15111                         cn : [
15112                             {
15113                                 tag : 'span',
15114                                 html : this.fieldLabel
15115                             },
15116                             {
15117                                 tag : 'i',
15118                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15119                                 tooltip : 'This field is required'
15120                             }
15121                         ]
15122                     },
15123                     {
15124                         cls : "",
15125                         cn: [
15126                             combobox
15127                         ]
15128                     }
15129
15130                 ];
15131                 
15132                 labelCfg = cfg.cn[0];
15133                 contentCfg = cfg.cn[1];
15134             }
15135             
15136            
15137             
15138             if(this.labelWidth > 12){
15139                 labelCfg.style = "width: " + this.labelWidth + 'px';
15140             }
15141             
15142             if(this.labelWidth < 13 && this.labelmd == 0){
15143                 this.labelmd = this.labelWidth;
15144             }
15145             
15146             if(this.labellg > 0){
15147                 labelCfg.cls += ' col-lg-' + this.labellg;
15148                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15149             }
15150             
15151             if(this.labelmd > 0){
15152                 labelCfg.cls += ' col-md-' + this.labelmd;
15153                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15154             }
15155             
15156             if(this.labelsm > 0){
15157                 labelCfg.cls += ' col-sm-' + this.labelsm;
15158                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15159             }
15160             
15161             if(this.labelxs > 0){
15162                 labelCfg.cls += ' col-xs-' + this.labelxs;
15163                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15164             }
15165                 
15166                 
15167         } else if ( this.fieldLabel.length) {
15168             cfg.cn = [
15169                 {
15170                    tag : 'i',
15171                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15172                    tooltip : 'This field is required'
15173                 },
15174                 {
15175                     tag: 'label',
15176                     cls : 'control-label',
15177                     html : this.fieldLabel
15178
15179                 },
15180                 {
15181                     cls : '', 
15182                     cn: [
15183                         combobox
15184                     ]
15185                 }
15186             ];
15187             
15188             if(this.indicatorpos == 'right'){
15189                 cfg.cn = [
15190                     {
15191                         tag: 'label',
15192                         cls : 'control-label',
15193                         html : this.fieldLabel,
15194                         cn : [
15195                             {
15196                                tag : 'i',
15197                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15198                                tooltip : 'This field is required'
15199                             }
15200                         ]
15201                     },
15202                     {
15203                         cls : '', 
15204                         cn: [
15205                             combobox
15206                         ]
15207                     }
15208                 ];
15209             }
15210         } else {
15211             cfg.cn = combobox;    
15212         }
15213         
15214         
15215         var settings = this;
15216         
15217         ['xs','sm','md','lg'].map(function(size){
15218             if (settings[size]) {
15219                 cfg.cls += ' col-' + size + '-' + settings[size];
15220             }
15221         });
15222         
15223         return cfg;
15224     },
15225     
15226     initTouchView : function()
15227     {
15228         this.renderTouchView();
15229         
15230         this.touchViewEl.on('scroll', function(){
15231             this.el.dom.scrollTop = 0;
15232         }, this);
15233         
15234         this.originalValue = this.getValue();
15235         
15236         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15237         
15238         this.inputEl().on("click", this.showTouchView, this);
15239         if (this.triggerEl) {
15240             this.triggerEl.on("click", this.showTouchView, this);
15241         }
15242         
15243         
15244         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15245         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15246         
15247         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15248         
15249         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15250         this.store.on('load', this.onTouchViewLoad, this);
15251         this.store.on('loadexception', this.onTouchViewLoadException, this);
15252         
15253         if(this.hiddenName){
15254             
15255             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15256             
15257             this.hiddenField.dom.value =
15258                 this.hiddenValue !== undefined ? this.hiddenValue :
15259                 this.value !== undefined ? this.value : '';
15260         
15261             this.el.dom.removeAttribute('name');
15262             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15263         }
15264         
15265         if(this.multiple){
15266             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15267             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15268         }
15269         
15270         if(this.removable && !this.multiple){
15271             var close = this.closeTriggerEl();
15272             if(close){
15273                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15274                 close.on('click', this.removeBtnClick, this, close);
15275             }
15276         }
15277         /*
15278          * fix the bug in Safari iOS8
15279          */
15280         this.inputEl().on("focus", function(e){
15281             document.activeElement.blur();
15282         }, this);
15283         
15284         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15285         
15286         return;
15287         
15288         
15289     },
15290     
15291     renderTouchView : function()
15292     {
15293         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15294         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15295         
15296         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15297         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15298         
15299         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15300         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15301         this.touchViewBodyEl.setStyle('overflow', 'auto');
15302         
15303         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15304         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15305         
15306         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15307         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15308         
15309     },
15310     
15311     showTouchView : function()
15312     {
15313         if(this.disabled){
15314             return;
15315         }
15316         
15317         this.touchViewHeaderEl.hide();
15318
15319         if(this.modalTitle.length){
15320             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15321             this.touchViewHeaderEl.show();
15322         }
15323
15324         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15325         this.touchViewEl.show();
15326
15327         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15328         
15329         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15330         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15331
15332         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15333
15334         if(this.modalTitle.length){
15335             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15336         }
15337         
15338         this.touchViewBodyEl.setHeight(bodyHeight);
15339
15340         if(this.animate){
15341             var _this = this;
15342             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15343         }else{
15344             this.touchViewEl.addClass('in');
15345         }
15346         
15347         if(this._touchViewMask){
15348             Roo.get(document.body).addClass("x-body-masked");
15349             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15350             this._touchViewMask.setStyle('z-index', 10000);
15351             this._touchViewMask.addClass('show');
15352         }
15353         
15354         this.doTouchViewQuery();
15355         
15356     },
15357     
15358     hideTouchView : function()
15359     {
15360         this.touchViewEl.removeClass('in');
15361
15362         if(this.animate){
15363             var _this = this;
15364             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15365         }else{
15366             this.touchViewEl.setStyle('display', 'none');
15367         }
15368         
15369         if(this._touchViewMask){
15370             this._touchViewMask.removeClass('show');
15371             Roo.get(document.body).removeClass("x-body-masked");
15372         }
15373     },
15374     
15375     setTouchViewValue : function()
15376     {
15377         if(this.multiple){
15378             this.clearItem();
15379         
15380             var _this = this;
15381
15382             Roo.each(this.tickItems, function(o){
15383                 this.addItem(o);
15384             }, this);
15385         }
15386         
15387         this.hideTouchView();
15388     },
15389     
15390     doTouchViewQuery : function()
15391     {
15392         var qe = {
15393             query: '',
15394             forceAll: true,
15395             combo: this,
15396             cancel:false
15397         };
15398         
15399         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15400             return false;
15401         }
15402         
15403         if(!this.alwaysQuery || this.mode == 'local'){
15404             this.onTouchViewLoad();
15405             return;
15406         }
15407         
15408         this.store.load();
15409     },
15410     
15411     onTouchViewBeforeLoad : function(combo,opts)
15412     {
15413         return;
15414     },
15415
15416     // private
15417     onTouchViewLoad : function()
15418     {
15419         if(this.store.getCount() < 1){
15420             this.onTouchViewEmptyResults();
15421             return;
15422         }
15423         
15424         this.clearTouchView();
15425         
15426         var rawValue = this.getRawValue();
15427         
15428         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15429         
15430         this.tickItems = [];
15431         
15432         this.store.data.each(function(d, rowIndex){
15433             var row = this.touchViewListGroup.createChild(template);
15434             
15435             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15436                 row.addClass(d.data.cls);
15437             }
15438             
15439             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15440                 var cfg = {
15441                     data : d.data,
15442                     html : d.data[this.displayField]
15443                 };
15444                 
15445                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15446                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15447                 }
15448             }
15449             row.removeClass('selected');
15450             if(!this.multiple && this.valueField &&
15451                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15452             {
15453                 // radio buttons..
15454                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15455                 row.addClass('selected');
15456             }
15457             
15458             if(this.multiple && this.valueField &&
15459                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15460             {
15461                 
15462                 // checkboxes...
15463                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15464                 this.tickItems.push(d.data);
15465             }
15466             
15467             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15468             
15469         }, this);
15470         
15471         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15472         
15473         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15474
15475         if(this.modalTitle.length){
15476             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15477         }
15478
15479         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15480         
15481         if(this.mobile_restrict_height && listHeight < bodyHeight){
15482             this.touchViewBodyEl.setHeight(listHeight);
15483         }
15484         
15485         var _this = this;
15486         
15487         if(firstChecked && listHeight > bodyHeight){
15488             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15489         }
15490         
15491     },
15492     
15493     onTouchViewLoadException : function()
15494     {
15495         this.hideTouchView();
15496     },
15497     
15498     onTouchViewEmptyResults : function()
15499     {
15500         this.clearTouchView();
15501         
15502         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15503         
15504         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15505         
15506     },
15507     
15508     clearTouchView : function()
15509     {
15510         this.touchViewListGroup.dom.innerHTML = '';
15511     },
15512     
15513     onTouchViewClick : function(e, el, o)
15514     {
15515         e.preventDefault();
15516         
15517         var row = o.row;
15518         var rowIndex = o.rowIndex;
15519         
15520         var r = this.store.getAt(rowIndex);
15521         
15522         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15523             
15524             if(!this.multiple){
15525                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15526                     c.dom.removeAttribute('checked');
15527                 }, this);
15528
15529                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15530
15531                 this.setFromData(r.data);
15532
15533                 var close = this.closeTriggerEl();
15534
15535                 if(close){
15536                     close.show();
15537                 }
15538
15539                 this.hideTouchView();
15540
15541                 this.fireEvent('select', this, r, rowIndex);
15542
15543                 return;
15544             }
15545
15546             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15547                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15548                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15549                 return;
15550             }
15551
15552             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15553             this.addItem(r.data);
15554             this.tickItems.push(r.data);
15555         }
15556     },
15557     
15558     getAutoCreateNativeIOS : function()
15559     {
15560         var cfg = {
15561             cls: 'form-group' //input-group,
15562         };
15563         
15564         var combobox =  {
15565             tag: 'select',
15566             cls : 'roo-ios-select'
15567         };
15568         
15569         if (this.name) {
15570             combobox.name = this.name;
15571         }
15572         
15573         if (this.disabled) {
15574             combobox.disabled = true;
15575         }
15576         
15577         var settings = this;
15578         
15579         ['xs','sm','md','lg'].map(function(size){
15580             if (settings[size]) {
15581                 cfg.cls += ' col-' + size + '-' + settings[size];
15582             }
15583         });
15584         
15585         cfg.cn = combobox;
15586         
15587         return cfg;
15588         
15589     },
15590     
15591     initIOSView : function()
15592     {
15593         this.store.on('load', this.onIOSViewLoad, this);
15594         
15595         return;
15596     },
15597     
15598     onIOSViewLoad : function()
15599     {
15600         if(this.store.getCount() < 1){
15601             return;
15602         }
15603         
15604         this.clearIOSView();
15605         
15606         if(this.allowBlank) {
15607             
15608             var default_text = '-- SELECT --';
15609             
15610             if(this.placeholder.length){
15611                 default_text = this.placeholder;
15612             }
15613             
15614             if(this.emptyTitle.length){
15615                 default_text += ' - ' + this.emptyTitle + ' -';
15616             }
15617             
15618             var opt = this.inputEl().createChild({
15619                 tag: 'option',
15620                 value : 0,
15621                 html : default_text
15622             });
15623             
15624             var o = {};
15625             o[this.valueField] = 0;
15626             o[this.displayField] = default_text;
15627             
15628             this.ios_options.push({
15629                 data : o,
15630                 el : opt
15631             });
15632             
15633         }
15634         
15635         this.store.data.each(function(d, rowIndex){
15636             
15637             var html = '';
15638             
15639             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15640                 html = d.data[this.displayField];
15641             }
15642             
15643             var value = '';
15644             
15645             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15646                 value = d.data[this.valueField];
15647             }
15648             
15649             var option = {
15650                 tag: 'option',
15651                 value : value,
15652                 html : html
15653             };
15654             
15655             if(this.value == d.data[this.valueField]){
15656                 option['selected'] = true;
15657             }
15658             
15659             var opt = this.inputEl().createChild(option);
15660             
15661             this.ios_options.push({
15662                 data : d.data,
15663                 el : opt
15664             });
15665             
15666         }, this);
15667         
15668         this.inputEl().on('change', function(){
15669            this.fireEvent('select', this);
15670         }, this);
15671         
15672     },
15673     
15674     clearIOSView: function()
15675     {
15676         this.inputEl().dom.innerHTML = '';
15677         
15678         this.ios_options = [];
15679     },
15680     
15681     setIOSValue: function(v)
15682     {
15683         this.value = v;
15684         
15685         if(!this.ios_options){
15686             return;
15687         }
15688         
15689         Roo.each(this.ios_options, function(opts){
15690            
15691            opts.el.dom.removeAttribute('selected');
15692            
15693            if(opts.data[this.valueField] != v){
15694                return;
15695            }
15696            
15697            opts.el.dom.setAttribute('selected', true);
15698            
15699         }, this);
15700     }
15701
15702     /** 
15703     * @cfg {Boolean} grow 
15704     * @hide 
15705     */
15706     /** 
15707     * @cfg {Number} growMin 
15708     * @hide 
15709     */
15710     /** 
15711     * @cfg {Number} growMax 
15712     * @hide 
15713     */
15714     /**
15715      * @hide
15716      * @method autoSize
15717      */
15718 });
15719
15720 Roo.apply(Roo.bootstrap.ComboBox,  {
15721     
15722     header : {
15723         tag: 'div',
15724         cls: 'modal-header',
15725         cn: [
15726             {
15727                 tag: 'h4',
15728                 cls: 'modal-title'
15729             }
15730         ]
15731     },
15732     
15733     body : {
15734         tag: 'div',
15735         cls: 'modal-body',
15736         cn: [
15737             {
15738                 tag: 'ul',
15739                 cls: 'list-group'
15740             }
15741         ]
15742     },
15743     
15744     listItemRadio : {
15745         tag: 'li',
15746         cls: 'list-group-item',
15747         cn: [
15748             {
15749                 tag: 'span',
15750                 cls: 'roo-combobox-list-group-item-value'
15751             },
15752             {
15753                 tag: 'div',
15754                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15755                 cn: [
15756                     {
15757                         tag: 'input',
15758                         type: 'radio'
15759                     },
15760                     {
15761                         tag: 'label'
15762                     }
15763                 ]
15764             }
15765         ]
15766     },
15767     
15768     listItemCheckbox : {
15769         tag: 'li',
15770         cls: 'list-group-item',
15771         cn: [
15772             {
15773                 tag: 'span',
15774                 cls: 'roo-combobox-list-group-item-value'
15775             },
15776             {
15777                 tag: 'div',
15778                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15779                 cn: [
15780                     {
15781                         tag: 'input',
15782                         type: 'checkbox'
15783                     },
15784                     {
15785                         tag: 'label'
15786                     }
15787                 ]
15788             }
15789         ]
15790     },
15791     
15792     emptyResult : {
15793         tag: 'div',
15794         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15795     },
15796     
15797     footer : {
15798         tag: 'div',
15799         cls: 'modal-footer',
15800         cn: [
15801             {
15802                 tag: 'div',
15803                 cls: 'row',
15804                 cn: [
15805                     {
15806                         tag: 'div',
15807                         cls: 'col-xs-6 text-left',
15808                         cn: {
15809                             tag: 'button',
15810                             cls: 'btn btn-danger roo-touch-view-cancel',
15811                             html: 'Cancel'
15812                         }
15813                     },
15814                     {
15815                         tag: 'div',
15816                         cls: 'col-xs-6 text-right',
15817                         cn: {
15818                             tag: 'button',
15819                             cls: 'btn btn-success roo-touch-view-ok',
15820                             html: 'OK'
15821                         }
15822                     }
15823                 ]
15824             }
15825         ]
15826         
15827     }
15828 });
15829
15830 Roo.apply(Roo.bootstrap.ComboBox,  {
15831     
15832     touchViewTemplate : {
15833         tag: 'div',
15834         cls: 'modal fade roo-combobox-touch-view',
15835         cn: [
15836             {
15837                 tag: 'div',
15838                 cls: 'modal-dialog',
15839                 style : 'position:fixed', // we have to fix position....
15840                 cn: [
15841                     {
15842                         tag: 'div',
15843                         cls: 'modal-content',
15844                         cn: [
15845                             Roo.bootstrap.ComboBox.header,
15846                             Roo.bootstrap.ComboBox.body,
15847                             Roo.bootstrap.ComboBox.footer
15848                         ]
15849                     }
15850                 ]
15851             }
15852         ]
15853     }
15854 });/*
15855  * Based on:
15856  * Ext JS Library 1.1.1
15857  * Copyright(c) 2006-2007, Ext JS, LLC.
15858  *
15859  * Originally Released Under LGPL - original licence link has changed is not relivant.
15860  *
15861  * Fork - LGPL
15862  * <script type="text/javascript">
15863  */
15864
15865 /**
15866  * @class Roo.View
15867  * @extends Roo.util.Observable
15868  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15869  * This class also supports single and multi selection modes. <br>
15870  * Create a data model bound view:
15871  <pre><code>
15872  var store = new Roo.data.Store(...);
15873
15874  var view = new Roo.View({
15875     el : "my-element",
15876     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15877  
15878     singleSelect: true,
15879     selectedClass: "ydataview-selected",
15880     store: store
15881  });
15882
15883  // listen for node click?
15884  view.on("click", function(vw, index, node, e){
15885  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15886  });
15887
15888  // load XML data
15889  dataModel.load("foobar.xml");
15890  </code></pre>
15891  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15892  * <br><br>
15893  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15894  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15895  * 
15896  * Note: old style constructor is still suported (container, template, config)
15897  * 
15898  * @constructor
15899  * Create a new View
15900  * @param {Object} config The config object
15901  * 
15902  */
15903 Roo.View = function(config, depreciated_tpl, depreciated_config){
15904     
15905     this.parent = false;
15906     
15907     if (typeof(depreciated_tpl) == 'undefined') {
15908         // new way.. - universal constructor.
15909         Roo.apply(this, config);
15910         this.el  = Roo.get(this.el);
15911     } else {
15912         // old format..
15913         this.el  = Roo.get(config);
15914         this.tpl = depreciated_tpl;
15915         Roo.apply(this, depreciated_config);
15916     }
15917     this.wrapEl  = this.el.wrap().wrap();
15918     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15919     
15920     
15921     if(typeof(this.tpl) == "string"){
15922         this.tpl = new Roo.Template(this.tpl);
15923     } else {
15924         // support xtype ctors..
15925         this.tpl = new Roo.factory(this.tpl, Roo);
15926     }
15927     
15928     
15929     this.tpl.compile();
15930     
15931     /** @private */
15932     this.addEvents({
15933         /**
15934          * @event beforeclick
15935          * Fires before a click is processed. Returns false to cancel the default action.
15936          * @param {Roo.View} this
15937          * @param {Number} index The index of the target node
15938          * @param {HTMLElement} node The target node
15939          * @param {Roo.EventObject} e The raw event object
15940          */
15941             "beforeclick" : true,
15942         /**
15943          * @event click
15944          * Fires when a template node is clicked.
15945          * @param {Roo.View} this
15946          * @param {Number} index The index of the target node
15947          * @param {HTMLElement} node The target node
15948          * @param {Roo.EventObject} e The raw event object
15949          */
15950             "click" : true,
15951         /**
15952          * @event dblclick
15953          * Fires when a template node is double clicked.
15954          * @param {Roo.View} this
15955          * @param {Number} index The index of the target node
15956          * @param {HTMLElement} node The target node
15957          * @param {Roo.EventObject} e The raw event object
15958          */
15959             "dblclick" : true,
15960         /**
15961          * @event contextmenu
15962          * Fires when a template node is right clicked.
15963          * @param {Roo.View} this
15964          * @param {Number} index The index of the target node
15965          * @param {HTMLElement} node The target node
15966          * @param {Roo.EventObject} e The raw event object
15967          */
15968             "contextmenu" : true,
15969         /**
15970          * @event selectionchange
15971          * Fires when the selected nodes change.
15972          * @param {Roo.View} this
15973          * @param {Array} selections Array of the selected nodes
15974          */
15975             "selectionchange" : true,
15976     
15977         /**
15978          * @event beforeselect
15979          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15980          * @param {Roo.View} this
15981          * @param {HTMLElement} node The node to be selected
15982          * @param {Array} selections Array of currently selected nodes
15983          */
15984             "beforeselect" : true,
15985         /**
15986          * @event preparedata
15987          * Fires on every row to render, to allow you to change the data.
15988          * @param {Roo.View} this
15989          * @param {Object} data to be rendered (change this)
15990          */
15991           "preparedata" : true
15992           
15993           
15994         });
15995
15996
15997
15998     this.el.on({
15999         "click": this.onClick,
16000         "dblclick": this.onDblClick,
16001         "contextmenu": this.onContextMenu,
16002         scope:this
16003     });
16004
16005     this.selections = [];
16006     this.nodes = [];
16007     this.cmp = new Roo.CompositeElementLite([]);
16008     if(this.store){
16009         this.store = Roo.factory(this.store, Roo.data);
16010         this.setStore(this.store, true);
16011     }
16012     
16013     if ( this.footer && this.footer.xtype) {
16014            
16015          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16016         
16017         this.footer.dataSource = this.store;
16018         this.footer.container = fctr;
16019         this.footer = Roo.factory(this.footer, Roo);
16020         fctr.insertFirst(this.el);
16021         
16022         // this is a bit insane - as the paging toolbar seems to detach the el..
16023 //        dom.parentNode.parentNode.parentNode
16024          // they get detached?
16025     }
16026     
16027     
16028     Roo.View.superclass.constructor.call(this);
16029     
16030     
16031 };
16032
16033 Roo.extend(Roo.View, Roo.util.Observable, {
16034     
16035      /**
16036      * @cfg {Roo.data.Store} store Data store to load data from.
16037      */
16038     store : false,
16039     
16040     /**
16041      * @cfg {String|Roo.Element} el The container element.
16042      */
16043     el : '',
16044     
16045     /**
16046      * @cfg {String|Roo.Template} tpl The template used by this View 
16047      */
16048     tpl : false,
16049     /**
16050      * @cfg {String} dataName the named area of the template to use as the data area
16051      *                          Works with domtemplates roo-name="name"
16052      */
16053     dataName: false,
16054     /**
16055      * @cfg {String} selectedClass The css class to add to selected nodes
16056      */
16057     selectedClass : "x-view-selected",
16058      /**
16059      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16060      */
16061     emptyText : "",
16062     
16063     /**
16064      * @cfg {String} text to display on mask (default Loading)
16065      */
16066     mask : false,
16067     /**
16068      * @cfg {Boolean} multiSelect Allow multiple selection
16069      */
16070     multiSelect : false,
16071     /**
16072      * @cfg {Boolean} singleSelect Allow single selection
16073      */
16074     singleSelect:  false,
16075     
16076     /**
16077      * @cfg {Boolean} toggleSelect - selecting 
16078      */
16079     toggleSelect : false,
16080     
16081     /**
16082      * @cfg {Boolean} tickable - selecting 
16083      */
16084     tickable : false,
16085     
16086     /**
16087      * Returns the element this view is bound to.
16088      * @return {Roo.Element}
16089      */
16090     getEl : function(){
16091         return this.wrapEl;
16092     },
16093     
16094     
16095
16096     /**
16097      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16098      */
16099     refresh : function(){
16100         //Roo.log('refresh');
16101         var t = this.tpl;
16102         
16103         // if we are using something like 'domtemplate', then
16104         // the what gets used is:
16105         // t.applySubtemplate(NAME, data, wrapping data..)
16106         // the outer template then get' applied with
16107         //     the store 'extra data'
16108         // and the body get's added to the
16109         //      roo-name="data" node?
16110         //      <span class='roo-tpl-{name}'></span> ?????
16111         
16112         
16113         
16114         this.clearSelections();
16115         this.el.update("");
16116         var html = [];
16117         var records = this.store.getRange();
16118         if(records.length < 1) {
16119             
16120             // is this valid??  = should it render a template??
16121             
16122             this.el.update(this.emptyText);
16123             return;
16124         }
16125         var el = this.el;
16126         if (this.dataName) {
16127             this.el.update(t.apply(this.store.meta)); //????
16128             el = this.el.child('.roo-tpl-' + this.dataName);
16129         }
16130         
16131         for(var i = 0, len = records.length; i < len; i++){
16132             var data = this.prepareData(records[i].data, i, records[i]);
16133             this.fireEvent("preparedata", this, data, i, records[i]);
16134             
16135             var d = Roo.apply({}, data);
16136             
16137             if(this.tickable){
16138                 Roo.apply(d, {'roo-id' : Roo.id()});
16139                 
16140                 var _this = this;
16141             
16142                 Roo.each(this.parent.item, function(item){
16143                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16144                         return;
16145                     }
16146                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16147                 });
16148             }
16149             
16150             html[html.length] = Roo.util.Format.trim(
16151                 this.dataName ?
16152                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16153                     t.apply(d)
16154             );
16155         }
16156         
16157         
16158         
16159         el.update(html.join(""));
16160         this.nodes = el.dom.childNodes;
16161         this.updateIndexes(0);
16162     },
16163     
16164
16165     /**
16166      * Function to override to reformat the data that is sent to
16167      * the template for each node.
16168      * DEPRICATED - use the preparedata event handler.
16169      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16170      * a JSON object for an UpdateManager bound view).
16171      */
16172     prepareData : function(data, index, record)
16173     {
16174         this.fireEvent("preparedata", this, data, index, record);
16175         return data;
16176     },
16177
16178     onUpdate : function(ds, record){
16179         // Roo.log('on update');   
16180         this.clearSelections();
16181         var index = this.store.indexOf(record);
16182         var n = this.nodes[index];
16183         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16184         n.parentNode.removeChild(n);
16185         this.updateIndexes(index, index);
16186     },
16187
16188     
16189     
16190 // --------- FIXME     
16191     onAdd : function(ds, records, index)
16192     {
16193         //Roo.log(['on Add', ds, records, index] );        
16194         this.clearSelections();
16195         if(this.nodes.length == 0){
16196             this.refresh();
16197             return;
16198         }
16199         var n = this.nodes[index];
16200         for(var i = 0, len = records.length; i < len; i++){
16201             var d = this.prepareData(records[i].data, i, records[i]);
16202             if(n){
16203                 this.tpl.insertBefore(n, d);
16204             }else{
16205                 
16206                 this.tpl.append(this.el, d);
16207             }
16208         }
16209         this.updateIndexes(index);
16210     },
16211
16212     onRemove : function(ds, record, index){
16213        // Roo.log('onRemove');
16214         this.clearSelections();
16215         var el = this.dataName  ?
16216             this.el.child('.roo-tpl-' + this.dataName) :
16217             this.el; 
16218         
16219         el.dom.removeChild(this.nodes[index]);
16220         this.updateIndexes(index);
16221     },
16222
16223     /**
16224      * Refresh an individual node.
16225      * @param {Number} index
16226      */
16227     refreshNode : function(index){
16228         this.onUpdate(this.store, this.store.getAt(index));
16229     },
16230
16231     updateIndexes : function(startIndex, endIndex){
16232         var ns = this.nodes;
16233         startIndex = startIndex || 0;
16234         endIndex = endIndex || ns.length - 1;
16235         for(var i = startIndex; i <= endIndex; i++){
16236             ns[i].nodeIndex = i;
16237         }
16238     },
16239
16240     /**
16241      * Changes the data store this view uses and refresh the view.
16242      * @param {Store} store
16243      */
16244     setStore : function(store, initial){
16245         if(!initial && this.store){
16246             this.store.un("datachanged", this.refresh);
16247             this.store.un("add", this.onAdd);
16248             this.store.un("remove", this.onRemove);
16249             this.store.un("update", this.onUpdate);
16250             this.store.un("clear", this.refresh);
16251             this.store.un("beforeload", this.onBeforeLoad);
16252             this.store.un("load", this.onLoad);
16253             this.store.un("loadexception", this.onLoad);
16254         }
16255         if(store){
16256           
16257             store.on("datachanged", this.refresh, this);
16258             store.on("add", this.onAdd, this);
16259             store.on("remove", this.onRemove, this);
16260             store.on("update", this.onUpdate, this);
16261             store.on("clear", this.refresh, this);
16262             store.on("beforeload", this.onBeforeLoad, this);
16263             store.on("load", this.onLoad, this);
16264             store.on("loadexception", this.onLoad, this);
16265         }
16266         
16267         if(store){
16268             this.refresh();
16269         }
16270     },
16271     /**
16272      * onbeforeLoad - masks the loading area.
16273      *
16274      */
16275     onBeforeLoad : function(store,opts)
16276     {
16277          //Roo.log('onBeforeLoad');   
16278         if (!opts.add) {
16279             this.el.update("");
16280         }
16281         this.el.mask(this.mask ? this.mask : "Loading" ); 
16282     },
16283     onLoad : function ()
16284     {
16285         this.el.unmask();
16286     },
16287     
16288
16289     /**
16290      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16291      * @param {HTMLElement} node
16292      * @return {HTMLElement} The template node
16293      */
16294     findItemFromChild : function(node){
16295         var el = this.dataName  ?
16296             this.el.child('.roo-tpl-' + this.dataName,true) :
16297             this.el.dom; 
16298         
16299         if(!node || node.parentNode == el){
16300                     return node;
16301             }
16302             var p = node.parentNode;
16303             while(p && p != el){
16304             if(p.parentNode == el){
16305                 return p;
16306             }
16307             p = p.parentNode;
16308         }
16309             return null;
16310     },
16311
16312     /** @ignore */
16313     onClick : function(e){
16314         var item = this.findItemFromChild(e.getTarget());
16315         if(item){
16316             var index = this.indexOf(item);
16317             if(this.onItemClick(item, index, e) !== false){
16318                 this.fireEvent("click", this, index, item, e);
16319             }
16320         }else{
16321             this.clearSelections();
16322         }
16323     },
16324
16325     /** @ignore */
16326     onContextMenu : function(e){
16327         var item = this.findItemFromChild(e.getTarget());
16328         if(item){
16329             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16330         }
16331     },
16332
16333     /** @ignore */
16334     onDblClick : function(e){
16335         var item = this.findItemFromChild(e.getTarget());
16336         if(item){
16337             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16338         }
16339     },
16340
16341     onItemClick : function(item, index, e)
16342     {
16343         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16344             return false;
16345         }
16346         if (this.toggleSelect) {
16347             var m = this.isSelected(item) ? 'unselect' : 'select';
16348             //Roo.log(m);
16349             var _t = this;
16350             _t[m](item, true, false);
16351             return true;
16352         }
16353         if(this.multiSelect || this.singleSelect){
16354             if(this.multiSelect && e.shiftKey && this.lastSelection){
16355                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16356             }else{
16357                 this.select(item, this.multiSelect && e.ctrlKey);
16358                 this.lastSelection = item;
16359             }
16360             
16361             if(!this.tickable){
16362                 e.preventDefault();
16363             }
16364             
16365         }
16366         return true;
16367     },
16368
16369     /**
16370      * Get the number of selected nodes.
16371      * @return {Number}
16372      */
16373     getSelectionCount : function(){
16374         return this.selections.length;
16375     },
16376
16377     /**
16378      * Get the currently selected nodes.
16379      * @return {Array} An array of HTMLElements
16380      */
16381     getSelectedNodes : function(){
16382         return this.selections;
16383     },
16384
16385     /**
16386      * Get the indexes of the selected nodes.
16387      * @return {Array}
16388      */
16389     getSelectedIndexes : function(){
16390         var indexes = [], s = this.selections;
16391         for(var i = 0, len = s.length; i < len; i++){
16392             indexes.push(s[i].nodeIndex);
16393         }
16394         return indexes;
16395     },
16396
16397     /**
16398      * Clear all selections
16399      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16400      */
16401     clearSelections : function(suppressEvent){
16402         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16403             this.cmp.elements = this.selections;
16404             this.cmp.removeClass(this.selectedClass);
16405             this.selections = [];
16406             if(!suppressEvent){
16407                 this.fireEvent("selectionchange", this, this.selections);
16408             }
16409         }
16410     },
16411
16412     /**
16413      * Returns true if the passed node is selected
16414      * @param {HTMLElement/Number} node The node or node index
16415      * @return {Boolean}
16416      */
16417     isSelected : function(node){
16418         var s = this.selections;
16419         if(s.length < 1){
16420             return false;
16421         }
16422         node = this.getNode(node);
16423         return s.indexOf(node) !== -1;
16424     },
16425
16426     /**
16427      * Selects nodes.
16428      * @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
16429      * @param {Boolean} keepExisting (optional) true to keep existing selections
16430      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16431      */
16432     select : function(nodeInfo, keepExisting, suppressEvent){
16433         if(nodeInfo instanceof Array){
16434             if(!keepExisting){
16435                 this.clearSelections(true);
16436             }
16437             for(var i = 0, len = nodeInfo.length; i < len; i++){
16438                 this.select(nodeInfo[i], true, true);
16439             }
16440             return;
16441         } 
16442         var node = this.getNode(nodeInfo);
16443         if(!node || this.isSelected(node)){
16444             return; // already selected.
16445         }
16446         if(!keepExisting){
16447             this.clearSelections(true);
16448         }
16449         
16450         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16451             Roo.fly(node).addClass(this.selectedClass);
16452             this.selections.push(node);
16453             if(!suppressEvent){
16454                 this.fireEvent("selectionchange", this, this.selections);
16455             }
16456         }
16457         
16458         
16459     },
16460       /**
16461      * Unselects nodes.
16462      * @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
16463      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16464      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16465      */
16466     unselect : function(nodeInfo, keepExisting, suppressEvent)
16467     {
16468         if(nodeInfo instanceof Array){
16469             Roo.each(this.selections, function(s) {
16470                 this.unselect(s, nodeInfo);
16471             }, this);
16472             return;
16473         }
16474         var node = this.getNode(nodeInfo);
16475         if(!node || !this.isSelected(node)){
16476             //Roo.log("not selected");
16477             return; // not selected.
16478         }
16479         // fireevent???
16480         var ns = [];
16481         Roo.each(this.selections, function(s) {
16482             if (s == node ) {
16483                 Roo.fly(node).removeClass(this.selectedClass);
16484
16485                 return;
16486             }
16487             ns.push(s);
16488         },this);
16489         
16490         this.selections= ns;
16491         this.fireEvent("selectionchange", this, this.selections);
16492     },
16493
16494     /**
16495      * Gets a template node.
16496      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16497      * @return {HTMLElement} The node or null if it wasn't found
16498      */
16499     getNode : function(nodeInfo){
16500         if(typeof nodeInfo == "string"){
16501             return document.getElementById(nodeInfo);
16502         }else if(typeof nodeInfo == "number"){
16503             return this.nodes[nodeInfo];
16504         }
16505         return nodeInfo;
16506     },
16507
16508     /**
16509      * Gets a range template nodes.
16510      * @param {Number} startIndex
16511      * @param {Number} endIndex
16512      * @return {Array} An array of nodes
16513      */
16514     getNodes : function(start, end){
16515         var ns = this.nodes;
16516         start = start || 0;
16517         end = typeof end == "undefined" ? ns.length - 1 : end;
16518         var nodes = [];
16519         if(start <= end){
16520             for(var i = start; i <= end; i++){
16521                 nodes.push(ns[i]);
16522             }
16523         } else{
16524             for(var i = start; i >= end; i--){
16525                 nodes.push(ns[i]);
16526             }
16527         }
16528         return nodes;
16529     },
16530
16531     /**
16532      * Finds the index of the passed node
16533      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16534      * @return {Number} The index of the node or -1
16535      */
16536     indexOf : function(node){
16537         node = this.getNode(node);
16538         if(typeof node.nodeIndex == "number"){
16539             return node.nodeIndex;
16540         }
16541         var ns = this.nodes;
16542         for(var i = 0, len = ns.length; i < len; i++){
16543             if(ns[i] == node){
16544                 return i;
16545             }
16546         }
16547         return -1;
16548     }
16549 });
16550 /*
16551  * - LGPL
16552  *
16553  * based on jquery fullcalendar
16554  * 
16555  */
16556
16557 Roo.bootstrap = Roo.bootstrap || {};
16558 /**
16559  * @class Roo.bootstrap.Calendar
16560  * @extends Roo.bootstrap.Component
16561  * Bootstrap Calendar class
16562  * @cfg {Boolean} loadMask (true|false) default false
16563  * @cfg {Object} header generate the user specific header of the calendar, default false
16564
16565  * @constructor
16566  * Create a new Container
16567  * @param {Object} config The config object
16568  */
16569
16570
16571
16572 Roo.bootstrap.Calendar = function(config){
16573     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16574      this.addEvents({
16575         /**
16576              * @event select
16577              * Fires when a date is selected
16578              * @param {DatePicker} this
16579              * @param {Date} date The selected date
16580              */
16581         'select': true,
16582         /**
16583              * @event monthchange
16584              * Fires when the displayed month changes 
16585              * @param {DatePicker} this
16586              * @param {Date} date The selected month
16587              */
16588         'monthchange': true,
16589         /**
16590              * @event evententer
16591              * Fires when mouse over an event
16592              * @param {Calendar} this
16593              * @param {event} Event
16594              */
16595         'evententer': true,
16596         /**
16597              * @event eventleave
16598              * Fires when the mouse leaves an
16599              * @param {Calendar} this
16600              * @param {event}
16601              */
16602         'eventleave': true,
16603         /**
16604              * @event eventclick
16605              * Fires when the mouse click an
16606              * @param {Calendar} this
16607              * @param {event}
16608              */
16609         'eventclick': true
16610         
16611     });
16612
16613 };
16614
16615 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16616     
16617      /**
16618      * @cfg {Number} startDay
16619      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16620      */
16621     startDay : 0,
16622     
16623     loadMask : false,
16624     
16625     header : false,
16626       
16627     getAutoCreate : function(){
16628         
16629         
16630         var fc_button = function(name, corner, style, content ) {
16631             return Roo.apply({},{
16632                 tag : 'span',
16633                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16634                          (corner.length ?
16635                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16636                             ''
16637                         ),
16638                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16639                 unselectable: 'on'
16640             });
16641         };
16642         
16643         var header = {};
16644         
16645         if(!this.header){
16646             header = {
16647                 tag : 'table',
16648                 cls : 'fc-header',
16649                 style : 'width:100%',
16650                 cn : [
16651                     {
16652                         tag: 'tr',
16653                         cn : [
16654                             {
16655                                 tag : 'td',
16656                                 cls : 'fc-header-left',
16657                                 cn : [
16658                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16659                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16660                                     { tag: 'span', cls: 'fc-header-space' },
16661                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16662
16663
16664                                 ]
16665                             },
16666
16667                             {
16668                                 tag : 'td',
16669                                 cls : 'fc-header-center',
16670                                 cn : [
16671                                     {
16672                                         tag: 'span',
16673                                         cls: 'fc-header-title',
16674                                         cn : {
16675                                             tag: 'H2',
16676                                             html : 'month / year'
16677                                         }
16678                                     }
16679
16680                                 ]
16681                             },
16682                             {
16683                                 tag : 'td',
16684                                 cls : 'fc-header-right',
16685                                 cn : [
16686                               /*      fc_button('month', 'left', '', 'month' ),
16687                                     fc_button('week', '', '', 'week' ),
16688                                     fc_button('day', 'right', '', 'day' )
16689                                 */    
16690
16691                                 ]
16692                             }
16693
16694                         ]
16695                     }
16696                 ]
16697             };
16698         }
16699         
16700         header = this.header;
16701         
16702        
16703         var cal_heads = function() {
16704             var ret = [];
16705             // fixme - handle this.
16706             
16707             for (var i =0; i < Date.dayNames.length; i++) {
16708                 var d = Date.dayNames[i];
16709                 ret.push({
16710                     tag: 'th',
16711                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16712                     html : d.substring(0,3)
16713                 });
16714                 
16715             }
16716             ret[0].cls += ' fc-first';
16717             ret[6].cls += ' fc-last';
16718             return ret;
16719         };
16720         var cal_cell = function(n) {
16721             return  {
16722                 tag: 'td',
16723                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16724                 cn : [
16725                     {
16726                         cn : [
16727                             {
16728                                 cls: 'fc-day-number',
16729                                 html: 'D'
16730                             },
16731                             {
16732                                 cls: 'fc-day-content',
16733                              
16734                                 cn : [
16735                                      {
16736                                         style: 'position: relative;' // height: 17px;
16737                                     }
16738                                 ]
16739                             }
16740                             
16741                             
16742                         ]
16743                     }
16744                 ]
16745                 
16746             }
16747         };
16748         var cal_rows = function() {
16749             
16750             var ret = [];
16751             for (var r = 0; r < 6; r++) {
16752                 var row= {
16753                     tag : 'tr',
16754                     cls : 'fc-week',
16755                     cn : []
16756                 };
16757                 
16758                 for (var i =0; i < Date.dayNames.length; i++) {
16759                     var d = Date.dayNames[i];
16760                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16761
16762                 }
16763                 row.cn[0].cls+=' fc-first';
16764                 row.cn[0].cn[0].style = 'min-height:90px';
16765                 row.cn[6].cls+=' fc-last';
16766                 ret.push(row);
16767                 
16768             }
16769             ret[0].cls += ' fc-first';
16770             ret[4].cls += ' fc-prev-last';
16771             ret[5].cls += ' fc-last';
16772             return ret;
16773             
16774         };
16775         
16776         var cal_table = {
16777             tag: 'table',
16778             cls: 'fc-border-separate',
16779             style : 'width:100%',
16780             cellspacing  : 0,
16781             cn : [
16782                 { 
16783                     tag: 'thead',
16784                     cn : [
16785                         { 
16786                             tag: 'tr',
16787                             cls : 'fc-first fc-last',
16788                             cn : cal_heads()
16789                         }
16790                     ]
16791                 },
16792                 { 
16793                     tag: 'tbody',
16794                     cn : cal_rows()
16795                 }
16796                   
16797             ]
16798         };
16799          
16800          var cfg = {
16801             cls : 'fc fc-ltr',
16802             cn : [
16803                 header,
16804                 {
16805                     cls : 'fc-content',
16806                     style : "position: relative;",
16807                     cn : [
16808                         {
16809                             cls : 'fc-view fc-view-month fc-grid',
16810                             style : 'position: relative',
16811                             unselectable : 'on',
16812                             cn : [
16813                                 {
16814                                     cls : 'fc-event-container',
16815                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16816                                 },
16817                                 cal_table
16818                             ]
16819                         }
16820                     ]
16821     
16822                 }
16823            ] 
16824             
16825         };
16826         
16827          
16828         
16829         return cfg;
16830     },
16831     
16832     
16833     initEvents : function()
16834     {
16835         if(!this.store){
16836             throw "can not find store for calendar";
16837         }
16838         
16839         var mark = {
16840             tag: "div",
16841             cls:"x-dlg-mask",
16842             style: "text-align:center",
16843             cn: [
16844                 {
16845                     tag: "div",
16846                     style: "background-color:white;width:50%;margin:250 auto",
16847                     cn: [
16848                         {
16849                             tag: "img",
16850                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16851                         },
16852                         {
16853                             tag: "span",
16854                             html: "Loading"
16855                         }
16856                         
16857                     ]
16858                 }
16859             ]
16860         };
16861         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16862         
16863         var size = this.el.select('.fc-content', true).first().getSize();
16864         this.maskEl.setSize(size.width, size.height);
16865         this.maskEl.enableDisplayMode("block");
16866         if(!this.loadMask){
16867             this.maskEl.hide();
16868         }
16869         
16870         this.store = Roo.factory(this.store, Roo.data);
16871         this.store.on('load', this.onLoad, this);
16872         this.store.on('beforeload', this.onBeforeLoad, this);
16873         
16874         this.resize();
16875         
16876         this.cells = this.el.select('.fc-day',true);
16877         //Roo.log(this.cells);
16878         this.textNodes = this.el.query('.fc-day-number');
16879         this.cells.addClassOnOver('fc-state-hover');
16880         
16881         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16882         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16883         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16884         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16885         
16886         this.on('monthchange', this.onMonthChange, this);
16887         
16888         this.update(new Date().clearTime());
16889     },
16890     
16891     resize : function() {
16892         var sz  = this.el.getSize();
16893         
16894         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16895         this.el.select('.fc-day-content div',true).setHeight(34);
16896     },
16897     
16898     
16899     // private
16900     showPrevMonth : function(e){
16901         this.update(this.activeDate.add("mo", -1));
16902     },
16903     showToday : function(e){
16904         this.update(new Date().clearTime());
16905     },
16906     // private
16907     showNextMonth : function(e){
16908         this.update(this.activeDate.add("mo", 1));
16909     },
16910
16911     // private
16912     showPrevYear : function(){
16913         this.update(this.activeDate.add("y", -1));
16914     },
16915
16916     // private
16917     showNextYear : function(){
16918         this.update(this.activeDate.add("y", 1));
16919     },
16920
16921     
16922    // private
16923     update : function(date)
16924     {
16925         var vd = this.activeDate;
16926         this.activeDate = date;
16927 //        if(vd && this.el){
16928 //            var t = date.getTime();
16929 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16930 //                Roo.log('using add remove');
16931 //                
16932 //                this.fireEvent('monthchange', this, date);
16933 //                
16934 //                this.cells.removeClass("fc-state-highlight");
16935 //                this.cells.each(function(c){
16936 //                   if(c.dateValue == t){
16937 //                       c.addClass("fc-state-highlight");
16938 //                       setTimeout(function(){
16939 //                            try{c.dom.firstChild.focus();}catch(e){}
16940 //                       }, 50);
16941 //                       return false;
16942 //                   }
16943 //                   return true;
16944 //                });
16945 //                return;
16946 //            }
16947 //        }
16948         
16949         var days = date.getDaysInMonth();
16950         
16951         var firstOfMonth = date.getFirstDateOfMonth();
16952         var startingPos = firstOfMonth.getDay()-this.startDay;
16953         
16954         if(startingPos < this.startDay){
16955             startingPos += 7;
16956         }
16957         
16958         var pm = date.add(Date.MONTH, -1);
16959         var prevStart = pm.getDaysInMonth()-startingPos;
16960 //        
16961         this.cells = this.el.select('.fc-day',true);
16962         this.textNodes = this.el.query('.fc-day-number');
16963         this.cells.addClassOnOver('fc-state-hover');
16964         
16965         var cells = this.cells.elements;
16966         var textEls = this.textNodes;
16967         
16968         Roo.each(cells, function(cell){
16969             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16970         });
16971         
16972         days += startingPos;
16973
16974         // convert everything to numbers so it's fast
16975         var day = 86400000;
16976         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16977         //Roo.log(d);
16978         //Roo.log(pm);
16979         //Roo.log(prevStart);
16980         
16981         var today = new Date().clearTime().getTime();
16982         var sel = date.clearTime().getTime();
16983         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16984         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16985         var ddMatch = this.disabledDatesRE;
16986         var ddText = this.disabledDatesText;
16987         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16988         var ddaysText = this.disabledDaysText;
16989         var format = this.format;
16990         
16991         var setCellClass = function(cal, cell){
16992             cell.row = 0;
16993             cell.events = [];
16994             cell.more = [];
16995             //Roo.log('set Cell Class');
16996             cell.title = "";
16997             var t = d.getTime();
16998             
16999             //Roo.log(d);
17000             
17001             cell.dateValue = t;
17002             if(t == today){
17003                 cell.className += " fc-today";
17004                 cell.className += " fc-state-highlight";
17005                 cell.title = cal.todayText;
17006             }
17007             if(t == sel){
17008                 // disable highlight in other month..
17009                 //cell.className += " fc-state-highlight";
17010                 
17011             }
17012             // disabling
17013             if(t < min) {
17014                 cell.className = " fc-state-disabled";
17015                 cell.title = cal.minText;
17016                 return;
17017             }
17018             if(t > max) {
17019                 cell.className = " fc-state-disabled";
17020                 cell.title = cal.maxText;
17021                 return;
17022             }
17023             if(ddays){
17024                 if(ddays.indexOf(d.getDay()) != -1){
17025                     cell.title = ddaysText;
17026                     cell.className = " fc-state-disabled";
17027                 }
17028             }
17029             if(ddMatch && format){
17030                 var fvalue = d.dateFormat(format);
17031                 if(ddMatch.test(fvalue)){
17032                     cell.title = ddText.replace("%0", fvalue);
17033                     cell.className = " fc-state-disabled";
17034                 }
17035             }
17036             
17037             if (!cell.initialClassName) {
17038                 cell.initialClassName = cell.dom.className;
17039             }
17040             
17041             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17042         };
17043
17044         var i = 0;
17045         
17046         for(; i < startingPos; i++) {
17047             textEls[i].innerHTML = (++prevStart);
17048             d.setDate(d.getDate()+1);
17049             
17050             cells[i].className = "fc-past fc-other-month";
17051             setCellClass(this, cells[i]);
17052         }
17053         
17054         var intDay = 0;
17055         
17056         for(; i < days; i++){
17057             intDay = i - startingPos + 1;
17058             textEls[i].innerHTML = (intDay);
17059             d.setDate(d.getDate()+1);
17060             
17061             cells[i].className = ''; // "x-date-active";
17062             setCellClass(this, cells[i]);
17063         }
17064         var extraDays = 0;
17065         
17066         for(; i < 42; i++) {
17067             textEls[i].innerHTML = (++extraDays);
17068             d.setDate(d.getDate()+1);
17069             
17070             cells[i].className = "fc-future fc-other-month";
17071             setCellClass(this, cells[i]);
17072         }
17073         
17074         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17075         
17076         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17077         
17078         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17079         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17080         
17081         if(totalRows != 6){
17082             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17083             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17084         }
17085         
17086         this.fireEvent('monthchange', this, date);
17087         
17088         
17089         /*
17090         if(!this.internalRender){
17091             var main = this.el.dom.firstChild;
17092             var w = main.offsetWidth;
17093             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17094             Roo.fly(main).setWidth(w);
17095             this.internalRender = true;
17096             // opera does not respect the auto grow header center column
17097             // then, after it gets a width opera refuses to recalculate
17098             // without a second pass
17099             if(Roo.isOpera && !this.secondPass){
17100                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17101                 this.secondPass = true;
17102                 this.update.defer(10, this, [date]);
17103             }
17104         }
17105         */
17106         
17107     },
17108     
17109     findCell : function(dt) {
17110         dt = dt.clearTime().getTime();
17111         var ret = false;
17112         this.cells.each(function(c){
17113             //Roo.log("check " +c.dateValue + '?=' + dt);
17114             if(c.dateValue == dt){
17115                 ret = c;
17116                 return false;
17117             }
17118             return true;
17119         });
17120         
17121         return ret;
17122     },
17123     
17124     findCells : function(ev) {
17125         var s = ev.start.clone().clearTime().getTime();
17126        // Roo.log(s);
17127         var e= ev.end.clone().clearTime().getTime();
17128        // Roo.log(e);
17129         var ret = [];
17130         this.cells.each(function(c){
17131              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17132             
17133             if(c.dateValue > e){
17134                 return ;
17135             }
17136             if(c.dateValue < s){
17137                 return ;
17138             }
17139             ret.push(c);
17140         });
17141         
17142         return ret;    
17143     },
17144     
17145 //    findBestRow: function(cells)
17146 //    {
17147 //        var ret = 0;
17148 //        
17149 //        for (var i =0 ; i < cells.length;i++) {
17150 //            ret  = Math.max(cells[i].rows || 0,ret);
17151 //        }
17152 //        return ret;
17153 //        
17154 //    },
17155     
17156     
17157     addItem : function(ev)
17158     {
17159         // look for vertical location slot in
17160         var cells = this.findCells(ev);
17161         
17162 //        ev.row = this.findBestRow(cells);
17163         
17164         // work out the location.
17165         
17166         var crow = false;
17167         var rows = [];
17168         for(var i =0; i < cells.length; i++) {
17169             
17170             cells[i].row = cells[0].row;
17171             
17172             if(i == 0){
17173                 cells[i].row = cells[i].row + 1;
17174             }
17175             
17176             if (!crow) {
17177                 crow = {
17178                     start : cells[i],
17179                     end :  cells[i]
17180                 };
17181                 continue;
17182             }
17183             if (crow.start.getY() == cells[i].getY()) {
17184                 // on same row.
17185                 crow.end = cells[i];
17186                 continue;
17187             }
17188             // different row.
17189             rows.push(crow);
17190             crow = {
17191                 start: cells[i],
17192                 end : cells[i]
17193             };
17194             
17195         }
17196         
17197         rows.push(crow);
17198         ev.els = [];
17199         ev.rows = rows;
17200         ev.cells = cells;
17201         
17202         cells[0].events.push(ev);
17203         
17204         this.calevents.push(ev);
17205     },
17206     
17207     clearEvents: function() {
17208         
17209         if(!this.calevents){
17210             return;
17211         }
17212         
17213         Roo.each(this.cells.elements, function(c){
17214             c.row = 0;
17215             c.events = [];
17216             c.more = [];
17217         });
17218         
17219         Roo.each(this.calevents, function(e) {
17220             Roo.each(e.els, function(el) {
17221                 el.un('mouseenter' ,this.onEventEnter, this);
17222                 el.un('mouseleave' ,this.onEventLeave, this);
17223                 el.remove();
17224             },this);
17225         },this);
17226         
17227         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17228             e.remove();
17229         });
17230         
17231     },
17232     
17233     renderEvents: function()
17234     {   
17235         var _this = this;
17236         
17237         this.cells.each(function(c) {
17238             
17239             if(c.row < 5){
17240                 return;
17241             }
17242             
17243             var ev = c.events;
17244             
17245             var r = 4;
17246             if(c.row != c.events.length){
17247                 r = 4 - (4 - (c.row - c.events.length));
17248             }
17249             
17250             c.events = ev.slice(0, r);
17251             c.more = ev.slice(r);
17252             
17253             if(c.more.length && c.more.length == 1){
17254                 c.events.push(c.more.pop());
17255             }
17256             
17257             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17258             
17259         });
17260             
17261         this.cells.each(function(c) {
17262             
17263             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17264             
17265             
17266             for (var e = 0; e < c.events.length; e++){
17267                 var ev = c.events[e];
17268                 var rows = ev.rows;
17269                 
17270                 for(var i = 0; i < rows.length; i++) {
17271                 
17272                     // how many rows should it span..
17273
17274                     var  cfg = {
17275                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17276                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17277
17278                         unselectable : "on",
17279                         cn : [
17280                             {
17281                                 cls: 'fc-event-inner',
17282                                 cn : [
17283     //                                {
17284     //                                  tag:'span',
17285     //                                  cls: 'fc-event-time',
17286     //                                  html : cells.length > 1 ? '' : ev.time
17287     //                                },
17288                                     {
17289                                       tag:'span',
17290                                       cls: 'fc-event-title',
17291                                       html : String.format('{0}', ev.title)
17292                                     }
17293
17294
17295                                 ]
17296                             },
17297                             {
17298                                 cls: 'ui-resizable-handle ui-resizable-e',
17299                                 html : '&nbsp;&nbsp;&nbsp'
17300                             }
17301
17302                         ]
17303                     };
17304
17305                     if (i == 0) {
17306                         cfg.cls += ' fc-event-start';
17307                     }
17308                     if ((i+1) == rows.length) {
17309                         cfg.cls += ' fc-event-end';
17310                     }
17311
17312                     var ctr = _this.el.select('.fc-event-container',true).first();
17313                     var cg = ctr.createChild(cfg);
17314
17315                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17316                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17317
17318                     var r = (c.more.length) ? 1 : 0;
17319                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17320                     cg.setWidth(ebox.right - sbox.x -2);
17321
17322                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17323                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17324                     cg.on('click', _this.onEventClick, _this, ev);
17325
17326                     ev.els.push(cg);
17327                     
17328                 }
17329                 
17330             }
17331             
17332             
17333             if(c.more.length){
17334                 var  cfg = {
17335                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17336                     style : 'position: absolute',
17337                     unselectable : "on",
17338                     cn : [
17339                         {
17340                             cls: 'fc-event-inner',
17341                             cn : [
17342                                 {
17343                                   tag:'span',
17344                                   cls: 'fc-event-title',
17345                                   html : 'More'
17346                                 }
17347
17348
17349                             ]
17350                         },
17351                         {
17352                             cls: 'ui-resizable-handle ui-resizable-e',
17353                             html : '&nbsp;&nbsp;&nbsp'
17354                         }
17355
17356                     ]
17357                 };
17358
17359                 var ctr = _this.el.select('.fc-event-container',true).first();
17360                 var cg = ctr.createChild(cfg);
17361
17362                 var sbox = c.select('.fc-day-content',true).first().getBox();
17363                 var ebox = c.select('.fc-day-content',true).first().getBox();
17364                 //Roo.log(cg);
17365                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17366                 cg.setWidth(ebox.right - sbox.x -2);
17367
17368                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17369                 
17370             }
17371             
17372         });
17373         
17374         
17375         
17376     },
17377     
17378     onEventEnter: function (e, el,event,d) {
17379         this.fireEvent('evententer', this, el, event);
17380     },
17381     
17382     onEventLeave: function (e, el,event,d) {
17383         this.fireEvent('eventleave', this, el, event);
17384     },
17385     
17386     onEventClick: function (e, el,event,d) {
17387         this.fireEvent('eventclick', this, el, event);
17388     },
17389     
17390     onMonthChange: function () {
17391         this.store.load();
17392     },
17393     
17394     onMoreEventClick: function(e, el, more)
17395     {
17396         var _this = this;
17397         
17398         this.calpopover.placement = 'right';
17399         this.calpopover.setTitle('More');
17400         
17401         this.calpopover.setContent('');
17402         
17403         var ctr = this.calpopover.el.select('.popover-content', true).first();
17404         
17405         Roo.each(more, function(m){
17406             var cfg = {
17407                 cls : 'fc-event-hori fc-event-draggable',
17408                 html : m.title
17409             };
17410             var cg = ctr.createChild(cfg);
17411             
17412             cg.on('click', _this.onEventClick, _this, m);
17413         });
17414         
17415         this.calpopover.show(el);
17416         
17417         
17418     },
17419     
17420     onLoad: function () 
17421     {   
17422         this.calevents = [];
17423         var cal = this;
17424         
17425         if(this.store.getCount() > 0){
17426             this.store.data.each(function(d){
17427                cal.addItem({
17428                     id : d.data.id,
17429                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17430                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17431                     time : d.data.start_time,
17432                     title : d.data.title,
17433                     description : d.data.description,
17434                     venue : d.data.venue
17435                 });
17436             });
17437         }
17438         
17439         this.renderEvents();
17440         
17441         if(this.calevents.length && this.loadMask){
17442             this.maskEl.hide();
17443         }
17444     },
17445     
17446     onBeforeLoad: function()
17447     {
17448         this.clearEvents();
17449         if(this.loadMask){
17450             this.maskEl.show();
17451         }
17452     }
17453 });
17454
17455  
17456  /*
17457  * - LGPL
17458  *
17459  * element
17460  * 
17461  */
17462
17463 /**
17464  * @class Roo.bootstrap.Popover
17465  * @extends Roo.bootstrap.Component
17466  * Bootstrap Popover class
17467  * @cfg {String} html contents of the popover   (or false to use children..)
17468  * @cfg {String} title of popover (or false to hide)
17469  * @cfg {String} placement how it is placed
17470  * @cfg {String} trigger click || hover (or false to trigger manually)
17471  * @cfg {String} over what (parent or false to trigger manually.)
17472  * @cfg {Number} delay - delay before showing
17473  
17474  * @constructor
17475  * Create a new Popover
17476  * @param {Object} config The config object
17477  */
17478
17479 Roo.bootstrap.Popover = function(config){
17480     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17481     
17482     this.addEvents({
17483         // raw events
17484          /**
17485          * @event show
17486          * After the popover show
17487          * 
17488          * @param {Roo.bootstrap.Popover} this
17489          */
17490         "show" : true,
17491         /**
17492          * @event hide
17493          * After the popover hide
17494          * 
17495          * @param {Roo.bootstrap.Popover} this
17496          */
17497         "hide" : true
17498     });
17499 };
17500
17501 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17502     
17503     title: 'Fill in a title',
17504     html: false,
17505     
17506     placement : 'right',
17507     trigger : 'hover', // hover
17508     
17509     delay : 0,
17510     
17511     over: 'parent',
17512     
17513     can_build_overlaid : false,
17514     
17515     getChildContainer : function()
17516     {
17517         return this.el.select('.popover-content',true).first();
17518     },
17519     
17520     getAutoCreate : function(){
17521          
17522         var cfg = {
17523            cls : 'popover roo-dynamic',
17524            style: 'display:block',
17525            cn : [
17526                 {
17527                     cls : 'arrow'
17528                 },
17529                 {
17530                     cls : 'popover-inner',
17531                     cn : [
17532                         {
17533                             tag: 'h3',
17534                             cls: 'popover-title',
17535                             html : this.title
17536                         },
17537                         {
17538                             cls : 'popover-content',
17539                             html : this.html
17540                         }
17541                     ]
17542                     
17543                 }
17544            ]
17545         };
17546         
17547         return cfg;
17548     },
17549     setTitle: function(str)
17550     {
17551         this.title = str;
17552         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17553     },
17554     setContent: function(str)
17555     {
17556         this.html = str;
17557         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17558     },
17559     // as it get's added to the bottom of the page.
17560     onRender : function(ct, position)
17561     {
17562         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17563         if(!this.el){
17564             var cfg = Roo.apply({},  this.getAutoCreate());
17565             cfg.id = Roo.id();
17566             
17567             if (this.cls) {
17568                 cfg.cls += ' ' + this.cls;
17569             }
17570             if (this.style) {
17571                 cfg.style = this.style;
17572             }
17573             //Roo.log("adding to ");
17574             this.el = Roo.get(document.body).createChild(cfg, position);
17575 //            Roo.log(this.el);
17576         }
17577         this.initEvents();
17578     },
17579     
17580     initEvents : function()
17581     {
17582         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17583         this.el.enableDisplayMode('block');
17584         this.el.hide();
17585         if (this.over === false) {
17586             return; 
17587         }
17588         if (this.triggers === false) {
17589             return;
17590         }
17591         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17592         var triggers = this.trigger ? this.trigger.split(' ') : [];
17593         Roo.each(triggers, function(trigger) {
17594         
17595             if (trigger == 'click') {
17596                 on_el.on('click', this.toggle, this);
17597             } else if (trigger != 'manual') {
17598                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17599                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17600       
17601                 on_el.on(eventIn  ,this.enter, this);
17602                 on_el.on(eventOut, this.leave, this);
17603             }
17604         }, this);
17605         
17606     },
17607     
17608     
17609     // private
17610     timeout : null,
17611     hoverState : null,
17612     
17613     toggle : function () {
17614         this.hoverState == 'in' ? this.leave() : this.enter();
17615     },
17616     
17617     enter : function () {
17618         
17619         clearTimeout(this.timeout);
17620     
17621         this.hoverState = 'in';
17622     
17623         if (!this.delay || !this.delay.show) {
17624             this.show();
17625             return;
17626         }
17627         var _t = this;
17628         this.timeout = setTimeout(function () {
17629             if (_t.hoverState == 'in') {
17630                 _t.show();
17631             }
17632         }, this.delay.show)
17633     },
17634     
17635     leave : function() {
17636         clearTimeout(this.timeout);
17637     
17638         this.hoverState = 'out';
17639     
17640         if (!this.delay || !this.delay.hide) {
17641             this.hide();
17642             return;
17643         }
17644         var _t = this;
17645         this.timeout = setTimeout(function () {
17646             if (_t.hoverState == 'out') {
17647                 _t.hide();
17648             }
17649         }, this.delay.hide)
17650     },
17651     
17652     show : function (on_el)
17653     {
17654         if (!on_el) {
17655             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17656         }
17657         
17658         // set content.
17659         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17660         if (this.html !== false) {
17661             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17662         }
17663         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17664         if (!this.title.length) {
17665             this.el.select('.popover-title',true).hide();
17666         }
17667         
17668         var placement = typeof this.placement == 'function' ?
17669             this.placement.call(this, this.el, on_el) :
17670             this.placement;
17671             
17672         var autoToken = /\s?auto?\s?/i;
17673         var autoPlace = autoToken.test(placement);
17674         if (autoPlace) {
17675             placement = placement.replace(autoToken, '') || 'top';
17676         }
17677         
17678         //this.el.detach()
17679         //this.el.setXY([0,0]);
17680         this.el.show();
17681         this.el.dom.style.display='block';
17682         this.el.addClass(placement);
17683         
17684         //this.el.appendTo(on_el);
17685         
17686         var p = this.getPosition();
17687         var box = this.el.getBox();
17688         
17689         if (autoPlace) {
17690             // fixme..
17691         }
17692         var align = Roo.bootstrap.Popover.alignment[placement];
17693         
17694 //        Roo.log(align);
17695         this.el.alignTo(on_el, align[0],align[1]);
17696         //var arrow = this.el.select('.arrow',true).first();
17697         //arrow.set(align[2], 
17698         
17699         this.el.addClass('in');
17700         
17701         
17702         if (this.el.hasClass('fade')) {
17703             // fade it?
17704         }
17705         
17706         this.hoverState = 'in';
17707         
17708         this.fireEvent('show', this);
17709         
17710     },
17711     hide : function()
17712     {
17713         this.el.setXY([0,0]);
17714         this.el.removeClass('in');
17715         this.el.hide();
17716         this.hoverState = null;
17717         
17718         this.fireEvent('hide', this);
17719     }
17720     
17721 });
17722
17723 Roo.bootstrap.Popover.alignment = {
17724     'left' : ['r-l', [-10,0], 'right'],
17725     'right' : ['l-r', [10,0], 'left'],
17726     'bottom' : ['t-b', [0,10], 'top'],
17727     'top' : [ 'b-t', [0,-10], 'bottom']
17728 };
17729
17730  /*
17731  * - LGPL
17732  *
17733  * Progress
17734  * 
17735  */
17736
17737 /**
17738  * @class Roo.bootstrap.Progress
17739  * @extends Roo.bootstrap.Component
17740  * Bootstrap Progress class
17741  * @cfg {Boolean} striped striped of the progress bar
17742  * @cfg {Boolean} active animated of the progress bar
17743  * 
17744  * 
17745  * @constructor
17746  * Create a new Progress
17747  * @param {Object} config The config object
17748  */
17749
17750 Roo.bootstrap.Progress = function(config){
17751     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17752 };
17753
17754 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17755     
17756     striped : false,
17757     active: false,
17758     
17759     getAutoCreate : function(){
17760         var cfg = {
17761             tag: 'div',
17762             cls: 'progress'
17763         };
17764         
17765         
17766         if(this.striped){
17767             cfg.cls += ' progress-striped';
17768         }
17769       
17770         if(this.active){
17771             cfg.cls += ' active';
17772         }
17773         
17774         
17775         return cfg;
17776     }
17777    
17778 });
17779
17780  
17781
17782  /*
17783  * - LGPL
17784  *
17785  * ProgressBar
17786  * 
17787  */
17788
17789 /**
17790  * @class Roo.bootstrap.ProgressBar
17791  * @extends Roo.bootstrap.Component
17792  * Bootstrap ProgressBar class
17793  * @cfg {Number} aria_valuenow aria-value now
17794  * @cfg {Number} aria_valuemin aria-value min
17795  * @cfg {Number} aria_valuemax aria-value max
17796  * @cfg {String} label label for the progress bar
17797  * @cfg {String} panel (success | info | warning | danger )
17798  * @cfg {String} role role of the progress bar
17799  * @cfg {String} sr_only text
17800  * 
17801  * 
17802  * @constructor
17803  * Create a new ProgressBar
17804  * @param {Object} config The config object
17805  */
17806
17807 Roo.bootstrap.ProgressBar = function(config){
17808     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17809 };
17810
17811 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17812     
17813     aria_valuenow : 0,
17814     aria_valuemin : 0,
17815     aria_valuemax : 100,
17816     label : false,
17817     panel : false,
17818     role : false,
17819     sr_only: false,
17820     
17821     getAutoCreate : function()
17822     {
17823         
17824         var cfg = {
17825             tag: 'div',
17826             cls: 'progress-bar',
17827             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17828         };
17829         
17830         if(this.sr_only){
17831             cfg.cn = {
17832                 tag: 'span',
17833                 cls: 'sr-only',
17834                 html: this.sr_only
17835             }
17836         }
17837         
17838         if(this.role){
17839             cfg.role = this.role;
17840         }
17841         
17842         if(this.aria_valuenow){
17843             cfg['aria-valuenow'] = this.aria_valuenow;
17844         }
17845         
17846         if(this.aria_valuemin){
17847             cfg['aria-valuemin'] = this.aria_valuemin;
17848         }
17849         
17850         if(this.aria_valuemax){
17851             cfg['aria-valuemax'] = this.aria_valuemax;
17852         }
17853         
17854         if(this.label && !this.sr_only){
17855             cfg.html = this.label;
17856         }
17857         
17858         if(this.panel){
17859             cfg.cls += ' progress-bar-' + this.panel;
17860         }
17861         
17862         return cfg;
17863     },
17864     
17865     update : function(aria_valuenow)
17866     {
17867         this.aria_valuenow = aria_valuenow;
17868         
17869         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17870     }
17871    
17872 });
17873
17874  
17875
17876  /*
17877  * - LGPL
17878  *
17879  * column
17880  * 
17881  */
17882
17883 /**
17884  * @class Roo.bootstrap.TabGroup
17885  * @extends Roo.bootstrap.Column
17886  * Bootstrap Column class
17887  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17888  * @cfg {Boolean} carousel true to make the group behave like a carousel
17889  * @cfg {Boolean} bullets show bullets for the panels
17890  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17891  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17892  * @cfg {Boolean} showarrow (true|false) show arrow default true
17893  * 
17894  * @constructor
17895  * Create a new TabGroup
17896  * @param {Object} config The config object
17897  */
17898
17899 Roo.bootstrap.TabGroup = function(config){
17900     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17901     if (!this.navId) {
17902         this.navId = Roo.id();
17903     }
17904     this.tabs = [];
17905     Roo.bootstrap.TabGroup.register(this);
17906     
17907 };
17908
17909 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17910     
17911     carousel : false,
17912     transition : false,
17913     bullets : 0,
17914     timer : 0,
17915     autoslide : false,
17916     slideFn : false,
17917     slideOnTouch : false,
17918     showarrow : true,
17919     
17920     getAutoCreate : function()
17921     {
17922         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17923         
17924         cfg.cls += ' tab-content';
17925         
17926         if (this.carousel) {
17927             cfg.cls += ' carousel slide';
17928             
17929             cfg.cn = [{
17930                cls : 'carousel-inner',
17931                cn : []
17932             }];
17933         
17934             if(this.bullets  && !Roo.isTouch){
17935                 
17936                 var bullets = {
17937                     cls : 'carousel-bullets',
17938                     cn : []
17939                 };
17940                
17941                 if(this.bullets_cls){
17942                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17943                 }
17944                 
17945                 bullets.cn.push({
17946                     cls : 'clear'
17947                 });
17948                 
17949                 cfg.cn[0].cn.push(bullets);
17950             }
17951             
17952             if(this.showarrow){
17953                 cfg.cn[0].cn.push({
17954                     tag : 'div',
17955                     class : 'carousel-arrow',
17956                     cn : [
17957                         {
17958                             tag : 'div',
17959                             class : 'carousel-prev',
17960                             cn : [
17961                                 {
17962                                     tag : 'i',
17963                                     class : 'fa fa-chevron-left'
17964                                 }
17965                             ]
17966                         },
17967                         {
17968                             tag : 'div',
17969                             class : 'carousel-next',
17970                             cn : [
17971                                 {
17972                                     tag : 'i',
17973                                     class : 'fa fa-chevron-right'
17974                                 }
17975                             ]
17976                         }
17977                     ]
17978                 });
17979             }
17980             
17981         }
17982         
17983         return cfg;
17984     },
17985     
17986     initEvents:  function()
17987     {
17988 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17989 //            this.el.on("touchstart", this.onTouchStart, this);
17990 //        }
17991         
17992         if(this.autoslide){
17993             var _this = this;
17994             
17995             this.slideFn = window.setInterval(function() {
17996                 _this.showPanelNext();
17997             }, this.timer);
17998         }
17999         
18000         if(this.showarrow){
18001             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18002             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18003         }
18004         
18005         
18006     },
18007     
18008 //    onTouchStart : function(e, el, o)
18009 //    {
18010 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18011 //            return;
18012 //        }
18013 //        
18014 //        this.showPanelNext();
18015 //    },
18016     
18017     
18018     getChildContainer : function()
18019     {
18020         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18021     },
18022     
18023     /**
18024     * register a Navigation item
18025     * @param {Roo.bootstrap.NavItem} the navitem to add
18026     */
18027     register : function(item)
18028     {
18029         this.tabs.push( item);
18030         item.navId = this.navId; // not really needed..
18031         this.addBullet();
18032     
18033     },
18034     
18035     getActivePanel : function()
18036     {
18037         var r = false;
18038         Roo.each(this.tabs, function(t) {
18039             if (t.active) {
18040                 r = t;
18041                 return false;
18042             }
18043             return null;
18044         });
18045         return r;
18046         
18047     },
18048     getPanelByName : function(n)
18049     {
18050         var r = false;
18051         Roo.each(this.tabs, function(t) {
18052             if (t.tabId == n) {
18053                 r = t;
18054                 return false;
18055             }
18056             return null;
18057         });
18058         return r;
18059     },
18060     indexOfPanel : function(p)
18061     {
18062         var r = false;
18063         Roo.each(this.tabs, function(t,i) {
18064             if (t.tabId == p.tabId) {
18065                 r = i;
18066                 return false;
18067             }
18068             return null;
18069         });
18070         return r;
18071     },
18072     /**
18073      * show a specific panel
18074      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18075      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18076      */
18077     showPanel : function (pan)
18078     {
18079         if(this.transition || typeof(pan) == 'undefined'){
18080             Roo.log("waiting for the transitionend");
18081             return;
18082         }
18083         
18084         if (typeof(pan) == 'number') {
18085             pan = this.tabs[pan];
18086         }
18087         
18088         if (typeof(pan) == 'string') {
18089             pan = this.getPanelByName(pan);
18090         }
18091         
18092         var cur = this.getActivePanel();
18093         
18094         if(!pan || !cur){
18095             Roo.log('pan or acitve pan is undefined');
18096             return false;
18097         }
18098         
18099         if (pan.tabId == this.getActivePanel().tabId) {
18100             return true;
18101         }
18102         
18103         if (false === cur.fireEvent('beforedeactivate')) {
18104             return false;
18105         }
18106         
18107         if(this.bullets > 0 && !Roo.isTouch){
18108             this.setActiveBullet(this.indexOfPanel(pan));
18109         }
18110         
18111         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18112             
18113             this.transition = true;
18114             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18115             var lr = dir == 'next' ? 'left' : 'right';
18116             pan.el.addClass(dir); // or prev
18117             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18118             cur.el.addClass(lr); // or right
18119             pan.el.addClass(lr);
18120             
18121             var _this = this;
18122             cur.el.on('transitionend', function() {
18123                 Roo.log("trans end?");
18124                 
18125                 pan.el.removeClass([lr,dir]);
18126                 pan.setActive(true);
18127                 
18128                 cur.el.removeClass([lr]);
18129                 cur.setActive(false);
18130                 
18131                 _this.transition = false;
18132                 
18133             }, this, { single:  true } );
18134             
18135             return true;
18136         }
18137         
18138         cur.setActive(false);
18139         pan.setActive(true);
18140         
18141         return true;
18142         
18143     },
18144     showPanelNext : function()
18145     {
18146         var i = this.indexOfPanel(this.getActivePanel());
18147         
18148         if (i >= this.tabs.length - 1 && !this.autoslide) {
18149             return;
18150         }
18151         
18152         if (i >= this.tabs.length - 1 && this.autoslide) {
18153             i = -1;
18154         }
18155         
18156         this.showPanel(this.tabs[i+1]);
18157     },
18158     
18159     showPanelPrev : function()
18160     {
18161         var i = this.indexOfPanel(this.getActivePanel());
18162         
18163         if (i  < 1 && !this.autoslide) {
18164             return;
18165         }
18166         
18167         if (i < 1 && this.autoslide) {
18168             i = this.tabs.length;
18169         }
18170         
18171         this.showPanel(this.tabs[i-1]);
18172     },
18173     
18174     
18175     addBullet: function()
18176     {
18177         if(!this.bullets || Roo.isTouch){
18178             return;
18179         }
18180         var ctr = this.el.select('.carousel-bullets',true).first();
18181         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18182         var bullet = ctr.createChild({
18183             cls : 'bullet bullet-' + i
18184         },ctr.dom.lastChild);
18185         
18186         
18187         var _this = this;
18188         
18189         bullet.on('click', (function(e, el, o, ii, t){
18190
18191             e.preventDefault();
18192
18193             this.showPanel(ii);
18194
18195             if(this.autoslide && this.slideFn){
18196                 clearInterval(this.slideFn);
18197                 this.slideFn = window.setInterval(function() {
18198                     _this.showPanelNext();
18199                 }, this.timer);
18200             }
18201
18202         }).createDelegate(this, [i, bullet], true));
18203                 
18204         
18205     },
18206      
18207     setActiveBullet : function(i)
18208     {
18209         if(Roo.isTouch){
18210             return;
18211         }
18212         
18213         Roo.each(this.el.select('.bullet', true).elements, function(el){
18214             el.removeClass('selected');
18215         });
18216
18217         var bullet = this.el.select('.bullet-' + i, true).first();
18218         
18219         if(!bullet){
18220             return;
18221         }
18222         
18223         bullet.addClass('selected');
18224     }
18225     
18226     
18227   
18228 });
18229
18230  
18231
18232  
18233  
18234 Roo.apply(Roo.bootstrap.TabGroup, {
18235     
18236     groups: {},
18237      /**
18238     * register a Navigation Group
18239     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18240     */
18241     register : function(navgrp)
18242     {
18243         this.groups[navgrp.navId] = navgrp;
18244         
18245     },
18246     /**
18247     * fetch a Navigation Group based on the navigation ID
18248     * if one does not exist , it will get created.
18249     * @param {string} the navgroup to add
18250     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18251     */
18252     get: function(navId) {
18253         if (typeof(this.groups[navId]) == 'undefined') {
18254             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18255         }
18256         return this.groups[navId] ;
18257     }
18258     
18259     
18260     
18261 });
18262
18263  /*
18264  * - LGPL
18265  *
18266  * TabPanel
18267  * 
18268  */
18269
18270 /**
18271  * @class Roo.bootstrap.TabPanel
18272  * @extends Roo.bootstrap.Component
18273  * Bootstrap TabPanel class
18274  * @cfg {Boolean} active panel active
18275  * @cfg {String} html panel content
18276  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18277  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18278  * @cfg {String} href click to link..
18279  * 
18280  * 
18281  * @constructor
18282  * Create a new TabPanel
18283  * @param {Object} config The config object
18284  */
18285
18286 Roo.bootstrap.TabPanel = function(config){
18287     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18288     this.addEvents({
18289         /**
18290              * @event changed
18291              * Fires when the active status changes
18292              * @param {Roo.bootstrap.TabPanel} this
18293              * @param {Boolean} state the new state
18294             
18295          */
18296         'changed': true,
18297         /**
18298              * @event beforedeactivate
18299              * Fires before a tab is de-activated - can be used to do validation on a form.
18300              * @param {Roo.bootstrap.TabPanel} this
18301              * @return {Boolean} false if there is an error
18302             
18303          */
18304         'beforedeactivate': true
18305      });
18306     
18307     this.tabId = this.tabId || Roo.id();
18308   
18309 };
18310
18311 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18312     
18313     active: false,
18314     html: false,
18315     tabId: false,
18316     navId : false,
18317     href : '',
18318     
18319     getAutoCreate : function(){
18320         var cfg = {
18321             tag: 'div',
18322             // item is needed for carousel - not sure if it has any effect otherwise
18323             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18324             html: this.html || ''
18325         };
18326         
18327         if(this.active){
18328             cfg.cls += ' active';
18329         }
18330         
18331         if(this.tabId){
18332             cfg.tabId = this.tabId;
18333         }
18334         
18335         
18336         return cfg;
18337     },
18338     
18339     initEvents:  function()
18340     {
18341         var p = this.parent();
18342         
18343         this.navId = this.navId || p.navId;
18344         
18345         if (typeof(this.navId) != 'undefined') {
18346             // not really needed.. but just in case.. parent should be a NavGroup.
18347             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18348             
18349             tg.register(this);
18350             
18351             var i = tg.tabs.length - 1;
18352             
18353             if(this.active && tg.bullets > 0 && i < tg.bullets){
18354                 tg.setActiveBullet(i);
18355             }
18356         }
18357         
18358         this.el.on('click', this.onClick, this);
18359         
18360         if(Roo.isTouch){
18361             this.el.on("touchstart", this.onTouchStart, this);
18362             this.el.on("touchmove", this.onTouchMove, this);
18363             this.el.on("touchend", this.onTouchEnd, this);
18364         }
18365         
18366     },
18367     
18368     onRender : function(ct, position)
18369     {
18370         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18371     },
18372     
18373     setActive : function(state)
18374     {
18375         Roo.log("panel - set active " + this.tabId + "=" + state);
18376         
18377         this.active = state;
18378         if (!state) {
18379             this.el.removeClass('active');
18380             
18381         } else  if (!this.el.hasClass('active')) {
18382             this.el.addClass('active');
18383         }
18384         
18385         this.fireEvent('changed', this, state);
18386     },
18387     
18388     onClick : function(e)
18389     {
18390         e.preventDefault();
18391         
18392         if(!this.href.length){
18393             return;
18394         }
18395         
18396         window.location.href = this.href;
18397     },
18398     
18399     startX : 0,
18400     startY : 0,
18401     endX : 0,
18402     endY : 0,
18403     swiping : false,
18404     
18405     onTouchStart : function(e)
18406     {
18407         this.swiping = false;
18408         
18409         this.startX = e.browserEvent.touches[0].clientX;
18410         this.startY = e.browserEvent.touches[0].clientY;
18411     },
18412     
18413     onTouchMove : function(e)
18414     {
18415         this.swiping = true;
18416         
18417         this.endX = e.browserEvent.touches[0].clientX;
18418         this.endY = e.browserEvent.touches[0].clientY;
18419     },
18420     
18421     onTouchEnd : function(e)
18422     {
18423         if(!this.swiping){
18424             this.onClick(e);
18425             return;
18426         }
18427         
18428         var tabGroup = this.parent();
18429         
18430         if(this.endX > this.startX){ // swiping right
18431             tabGroup.showPanelPrev();
18432             return;
18433         }
18434         
18435         if(this.startX > this.endX){ // swiping left
18436             tabGroup.showPanelNext();
18437             return;
18438         }
18439     }
18440     
18441     
18442 });
18443  
18444
18445  
18446
18447  /*
18448  * - LGPL
18449  *
18450  * DateField
18451  * 
18452  */
18453
18454 /**
18455  * @class Roo.bootstrap.DateField
18456  * @extends Roo.bootstrap.Input
18457  * Bootstrap DateField class
18458  * @cfg {Number} weekStart default 0
18459  * @cfg {String} viewMode default empty, (months|years)
18460  * @cfg {String} minViewMode default empty, (months|years)
18461  * @cfg {Number} startDate default -Infinity
18462  * @cfg {Number} endDate default Infinity
18463  * @cfg {Boolean} todayHighlight default false
18464  * @cfg {Boolean} todayBtn default false
18465  * @cfg {Boolean} calendarWeeks default false
18466  * @cfg {Object} daysOfWeekDisabled default empty
18467  * @cfg {Boolean} singleMode default false (true | false)
18468  * 
18469  * @cfg {Boolean} keyboardNavigation default true
18470  * @cfg {String} language default en
18471  * 
18472  * @constructor
18473  * Create a new DateField
18474  * @param {Object} config The config object
18475  */
18476
18477 Roo.bootstrap.DateField = function(config){
18478     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18479      this.addEvents({
18480             /**
18481              * @event show
18482              * Fires when this field show.
18483              * @param {Roo.bootstrap.DateField} this
18484              * @param {Mixed} date The date value
18485              */
18486             show : true,
18487             /**
18488              * @event show
18489              * Fires when this field hide.
18490              * @param {Roo.bootstrap.DateField} this
18491              * @param {Mixed} date The date value
18492              */
18493             hide : true,
18494             /**
18495              * @event select
18496              * Fires when select a date.
18497              * @param {Roo.bootstrap.DateField} this
18498              * @param {Mixed} date The date value
18499              */
18500             select : true,
18501             /**
18502              * @event beforeselect
18503              * Fires when before select a date.
18504              * @param {Roo.bootstrap.DateField} this
18505              * @param {Mixed} date The date value
18506              */
18507             beforeselect : true
18508         });
18509 };
18510
18511 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18512     
18513     /**
18514      * @cfg {String} format
18515      * The default date format string which can be overriden for localization support.  The format must be
18516      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18517      */
18518     format : "m/d/y",
18519     /**
18520      * @cfg {String} altFormats
18521      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18522      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18523      */
18524     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18525     
18526     weekStart : 0,
18527     
18528     viewMode : '',
18529     
18530     minViewMode : '',
18531     
18532     todayHighlight : false,
18533     
18534     todayBtn: false,
18535     
18536     language: 'en',
18537     
18538     keyboardNavigation: true,
18539     
18540     calendarWeeks: false,
18541     
18542     startDate: -Infinity,
18543     
18544     endDate: Infinity,
18545     
18546     daysOfWeekDisabled: [],
18547     
18548     _events: [],
18549     
18550     singleMode : false,
18551     
18552     UTCDate: function()
18553     {
18554         return new Date(Date.UTC.apply(Date, arguments));
18555     },
18556     
18557     UTCToday: function()
18558     {
18559         var today = new Date();
18560         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18561     },
18562     
18563     getDate: function() {
18564             var d = this.getUTCDate();
18565             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18566     },
18567     
18568     getUTCDate: function() {
18569             return this.date;
18570     },
18571     
18572     setDate: function(d) {
18573             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18574     },
18575     
18576     setUTCDate: function(d) {
18577             this.date = d;
18578             this.setValue(this.formatDate(this.date));
18579     },
18580         
18581     onRender: function(ct, position)
18582     {
18583         
18584         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18585         
18586         this.language = this.language || 'en';
18587         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18588         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18589         
18590         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18591         this.format = this.format || 'm/d/y';
18592         this.isInline = false;
18593         this.isInput = true;
18594         this.component = this.el.select('.add-on', true).first() || false;
18595         this.component = (this.component && this.component.length === 0) ? false : this.component;
18596         this.hasInput = this.component && this.inputEl().length;
18597         
18598         if (typeof(this.minViewMode === 'string')) {
18599             switch (this.minViewMode) {
18600                 case 'months':
18601                     this.minViewMode = 1;
18602                     break;
18603                 case 'years':
18604                     this.minViewMode = 2;
18605                     break;
18606                 default:
18607                     this.minViewMode = 0;
18608                     break;
18609             }
18610         }
18611         
18612         if (typeof(this.viewMode === 'string')) {
18613             switch (this.viewMode) {
18614                 case 'months':
18615                     this.viewMode = 1;
18616                     break;
18617                 case 'years':
18618                     this.viewMode = 2;
18619                     break;
18620                 default:
18621                     this.viewMode = 0;
18622                     break;
18623             }
18624         }
18625                 
18626         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18627         
18628 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18629         
18630         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18631         
18632         this.picker().on('mousedown', this.onMousedown, this);
18633         this.picker().on('click', this.onClick, this);
18634         
18635         this.picker().addClass('datepicker-dropdown');
18636         
18637         this.startViewMode = this.viewMode;
18638         
18639         if(this.singleMode){
18640             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18641                 v.setVisibilityMode(Roo.Element.DISPLAY);
18642                 v.hide();
18643             });
18644             
18645             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18646                 v.setStyle('width', '189px');
18647             });
18648         }
18649         
18650         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18651             if(!this.calendarWeeks){
18652                 v.remove();
18653                 return;
18654             }
18655             
18656             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18657             v.attr('colspan', function(i, val){
18658                 return parseInt(val) + 1;
18659             });
18660         });
18661                         
18662         
18663         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18664         
18665         this.setStartDate(this.startDate);
18666         this.setEndDate(this.endDate);
18667         
18668         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18669         
18670         this.fillDow();
18671         this.fillMonths();
18672         this.update();
18673         this.showMode();
18674         
18675         if(this.isInline) {
18676             this.showPopup();
18677         }
18678     },
18679     
18680     picker : function()
18681     {
18682         return this.pickerEl;
18683 //        return this.el.select('.datepicker', true).first();
18684     },
18685     
18686     fillDow: function()
18687     {
18688         var dowCnt = this.weekStart;
18689         
18690         var dow = {
18691             tag: 'tr',
18692             cn: [
18693                 
18694             ]
18695         };
18696         
18697         if(this.calendarWeeks){
18698             dow.cn.push({
18699                 tag: 'th',
18700                 cls: 'cw',
18701                 html: '&nbsp;'
18702             })
18703         }
18704         
18705         while (dowCnt < this.weekStart + 7) {
18706             dow.cn.push({
18707                 tag: 'th',
18708                 cls: 'dow',
18709                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18710             });
18711         }
18712         
18713         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18714     },
18715     
18716     fillMonths: function()
18717     {    
18718         var i = 0;
18719         var months = this.picker().select('>.datepicker-months td', true).first();
18720         
18721         months.dom.innerHTML = '';
18722         
18723         while (i < 12) {
18724             var month = {
18725                 tag: 'span',
18726                 cls: 'month',
18727                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18728             };
18729             
18730             months.createChild(month);
18731         }
18732         
18733     },
18734     
18735     update: function()
18736     {
18737         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;
18738         
18739         if (this.date < this.startDate) {
18740             this.viewDate = new Date(this.startDate);
18741         } else if (this.date > this.endDate) {
18742             this.viewDate = new Date(this.endDate);
18743         } else {
18744             this.viewDate = new Date(this.date);
18745         }
18746         
18747         this.fill();
18748     },
18749     
18750     fill: function() 
18751     {
18752         var d = new Date(this.viewDate),
18753                 year = d.getUTCFullYear(),
18754                 month = d.getUTCMonth(),
18755                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18756                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18757                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18758                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18759                 currentDate = this.date && this.date.valueOf(),
18760                 today = this.UTCToday();
18761         
18762         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18763         
18764 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18765         
18766 //        this.picker.select('>tfoot th.today').
18767 //                                              .text(dates[this.language].today)
18768 //                                              .toggle(this.todayBtn !== false);
18769     
18770         this.updateNavArrows();
18771         this.fillMonths();
18772                                                 
18773         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18774         
18775         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18776          
18777         prevMonth.setUTCDate(day);
18778         
18779         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18780         
18781         var nextMonth = new Date(prevMonth);
18782         
18783         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18784         
18785         nextMonth = nextMonth.valueOf();
18786         
18787         var fillMonths = false;
18788         
18789         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18790         
18791         while(prevMonth.valueOf() <= nextMonth) {
18792             var clsName = '';
18793             
18794             if (prevMonth.getUTCDay() === this.weekStart) {
18795                 if(fillMonths){
18796                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18797                 }
18798                     
18799                 fillMonths = {
18800                     tag: 'tr',
18801                     cn: []
18802                 };
18803                 
18804                 if(this.calendarWeeks){
18805                     // ISO 8601: First week contains first thursday.
18806                     // ISO also states week starts on Monday, but we can be more abstract here.
18807                     var
18808                     // Start of current week: based on weekstart/current date
18809                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18810                     // Thursday of this week
18811                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18812                     // First Thursday of year, year from thursday
18813                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18814                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18815                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18816                     
18817                     fillMonths.cn.push({
18818                         tag: 'td',
18819                         cls: 'cw',
18820                         html: calWeek
18821                     });
18822                 }
18823             }
18824             
18825             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18826                 clsName += ' old';
18827             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18828                 clsName += ' new';
18829             }
18830             if (this.todayHighlight &&
18831                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18832                 prevMonth.getUTCMonth() == today.getMonth() &&
18833                 prevMonth.getUTCDate() == today.getDate()) {
18834                 clsName += ' today';
18835             }
18836             
18837             if (currentDate && prevMonth.valueOf() === currentDate) {
18838                 clsName += ' active';
18839             }
18840             
18841             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18842                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18843                     clsName += ' disabled';
18844             }
18845             
18846             fillMonths.cn.push({
18847                 tag: 'td',
18848                 cls: 'day ' + clsName,
18849                 html: prevMonth.getDate()
18850             });
18851             
18852             prevMonth.setDate(prevMonth.getDate()+1);
18853         }
18854           
18855         var currentYear = this.date && this.date.getUTCFullYear();
18856         var currentMonth = this.date && this.date.getUTCMonth();
18857         
18858         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18859         
18860         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18861             v.removeClass('active');
18862             
18863             if(currentYear === year && k === currentMonth){
18864                 v.addClass('active');
18865             }
18866             
18867             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18868                 v.addClass('disabled');
18869             }
18870             
18871         });
18872         
18873         
18874         year = parseInt(year/10, 10) * 10;
18875         
18876         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18877         
18878         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18879         
18880         year -= 1;
18881         for (var i = -1; i < 11; i++) {
18882             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18883                 tag: 'span',
18884                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18885                 html: year
18886             });
18887             
18888             year += 1;
18889         }
18890     },
18891     
18892     showMode: function(dir) 
18893     {
18894         if (dir) {
18895             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18896         }
18897         
18898         Roo.each(this.picker().select('>div',true).elements, function(v){
18899             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18900             v.hide();
18901         });
18902         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18903     },
18904     
18905     place: function()
18906     {
18907         if(this.isInline) {
18908             return;
18909         }
18910         
18911         this.picker().removeClass(['bottom', 'top']);
18912         
18913         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18914             /*
18915              * place to the top of element!
18916              *
18917              */
18918             
18919             this.picker().addClass('top');
18920             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18921             
18922             return;
18923         }
18924         
18925         this.picker().addClass('bottom');
18926         
18927         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18928     },
18929     
18930     parseDate : function(value)
18931     {
18932         if(!value || value instanceof Date){
18933             return value;
18934         }
18935         var v = Date.parseDate(value, this.format);
18936         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18937             v = Date.parseDate(value, 'Y-m-d');
18938         }
18939         if(!v && this.altFormats){
18940             if(!this.altFormatsArray){
18941                 this.altFormatsArray = this.altFormats.split("|");
18942             }
18943             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18944                 v = Date.parseDate(value, this.altFormatsArray[i]);
18945             }
18946         }
18947         return v;
18948     },
18949     
18950     formatDate : function(date, fmt)
18951     {   
18952         return (!date || !(date instanceof Date)) ?
18953         date : date.dateFormat(fmt || this.format);
18954     },
18955     
18956     onFocus : function()
18957     {
18958         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18959         this.showPopup();
18960     },
18961     
18962     onBlur : function()
18963     {
18964         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18965         
18966         var d = this.inputEl().getValue();
18967         
18968         this.setValue(d);
18969                 
18970         this.hidePopup();
18971     },
18972     
18973     showPopup : function()
18974     {
18975         this.picker().show();
18976         this.update();
18977         this.place();
18978         
18979         this.fireEvent('showpopup', this, this.date);
18980     },
18981     
18982     hidePopup : function()
18983     {
18984         if(this.isInline) {
18985             return;
18986         }
18987         this.picker().hide();
18988         this.viewMode = this.startViewMode;
18989         this.showMode();
18990         
18991         this.fireEvent('hidepopup', this, this.date);
18992         
18993     },
18994     
18995     onMousedown: function(e)
18996     {
18997         e.stopPropagation();
18998         e.preventDefault();
18999     },
19000     
19001     keyup: function(e)
19002     {
19003         Roo.bootstrap.DateField.superclass.keyup.call(this);
19004         this.update();
19005     },
19006
19007     setValue: function(v)
19008     {
19009         if(this.fireEvent('beforeselect', this, v) !== false){
19010             var d = new Date(this.parseDate(v) ).clearTime();
19011         
19012             if(isNaN(d.getTime())){
19013                 this.date = this.viewDate = '';
19014                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19015                 return;
19016             }
19017
19018             v = this.formatDate(d);
19019
19020             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19021
19022             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19023
19024             this.update();
19025
19026             this.fireEvent('select', this, this.date);
19027         }
19028     },
19029     
19030     getValue: function()
19031     {
19032         return this.formatDate(this.date);
19033     },
19034     
19035     fireKey: function(e)
19036     {
19037         if (!this.picker().isVisible()){
19038             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19039                 this.showPopup();
19040             }
19041             return;
19042         }
19043         
19044         var dateChanged = false,
19045         dir, day, month,
19046         newDate, newViewDate;
19047         
19048         switch(e.keyCode){
19049             case 27: // escape
19050                 this.hidePopup();
19051                 e.preventDefault();
19052                 break;
19053             case 37: // left
19054             case 39: // right
19055                 if (!this.keyboardNavigation) {
19056                     break;
19057                 }
19058                 dir = e.keyCode == 37 ? -1 : 1;
19059                 
19060                 if (e.ctrlKey){
19061                     newDate = this.moveYear(this.date, dir);
19062                     newViewDate = this.moveYear(this.viewDate, dir);
19063                 } else if (e.shiftKey){
19064                     newDate = this.moveMonth(this.date, dir);
19065                     newViewDate = this.moveMonth(this.viewDate, dir);
19066                 } else {
19067                     newDate = new Date(this.date);
19068                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19069                     newViewDate = new Date(this.viewDate);
19070                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19071                 }
19072                 if (this.dateWithinRange(newDate)){
19073                     this.date = newDate;
19074                     this.viewDate = newViewDate;
19075                     this.setValue(this.formatDate(this.date));
19076 //                    this.update();
19077                     e.preventDefault();
19078                     dateChanged = true;
19079                 }
19080                 break;
19081             case 38: // up
19082             case 40: // down
19083                 if (!this.keyboardNavigation) {
19084                     break;
19085                 }
19086                 dir = e.keyCode == 38 ? -1 : 1;
19087                 if (e.ctrlKey){
19088                     newDate = this.moveYear(this.date, dir);
19089                     newViewDate = this.moveYear(this.viewDate, dir);
19090                 } else if (e.shiftKey){
19091                     newDate = this.moveMonth(this.date, dir);
19092                     newViewDate = this.moveMonth(this.viewDate, dir);
19093                 } else {
19094                     newDate = new Date(this.date);
19095                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19096                     newViewDate = new Date(this.viewDate);
19097                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19098                 }
19099                 if (this.dateWithinRange(newDate)){
19100                     this.date = newDate;
19101                     this.viewDate = newViewDate;
19102                     this.setValue(this.formatDate(this.date));
19103 //                    this.update();
19104                     e.preventDefault();
19105                     dateChanged = true;
19106                 }
19107                 break;
19108             case 13: // enter
19109                 this.setValue(this.formatDate(this.date));
19110                 this.hidePopup();
19111                 e.preventDefault();
19112                 break;
19113             case 9: // tab
19114                 this.setValue(this.formatDate(this.date));
19115                 this.hidePopup();
19116                 break;
19117             case 16: // shift
19118             case 17: // ctrl
19119             case 18: // alt
19120                 break;
19121             default :
19122                 this.hide();
19123                 
19124         }
19125     },
19126     
19127     
19128     onClick: function(e) 
19129     {
19130         e.stopPropagation();
19131         e.preventDefault();
19132         
19133         var target = e.getTarget();
19134         
19135         if(target.nodeName.toLowerCase() === 'i'){
19136             target = Roo.get(target).dom.parentNode;
19137         }
19138         
19139         var nodeName = target.nodeName;
19140         var className = target.className;
19141         var html = target.innerHTML;
19142         //Roo.log(nodeName);
19143         
19144         switch(nodeName.toLowerCase()) {
19145             case 'th':
19146                 switch(className) {
19147                     case 'switch':
19148                         this.showMode(1);
19149                         break;
19150                     case 'prev':
19151                     case 'next':
19152                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19153                         switch(this.viewMode){
19154                                 case 0:
19155                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19156                                         break;
19157                                 case 1:
19158                                 case 2:
19159                                         this.viewDate = this.moveYear(this.viewDate, dir);
19160                                         break;
19161                         }
19162                         this.fill();
19163                         break;
19164                     case 'today':
19165                         var date = new Date();
19166                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19167 //                        this.fill()
19168                         this.setValue(this.formatDate(this.date));
19169                         
19170                         this.hidePopup();
19171                         break;
19172                 }
19173                 break;
19174             case 'span':
19175                 if (className.indexOf('disabled') < 0) {
19176                     this.viewDate.setUTCDate(1);
19177                     if (className.indexOf('month') > -1) {
19178                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19179                     } else {
19180                         var year = parseInt(html, 10) || 0;
19181                         this.viewDate.setUTCFullYear(year);
19182                         
19183                     }
19184                     
19185                     if(this.singleMode){
19186                         this.setValue(this.formatDate(this.viewDate));
19187                         this.hidePopup();
19188                         return;
19189                     }
19190                     
19191                     this.showMode(-1);
19192                     this.fill();
19193                 }
19194                 break;
19195                 
19196             case 'td':
19197                 //Roo.log(className);
19198                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19199                     var day = parseInt(html, 10) || 1;
19200                     var year = this.viewDate.getUTCFullYear(),
19201                         month = this.viewDate.getUTCMonth();
19202
19203                     if (className.indexOf('old') > -1) {
19204                         if(month === 0 ){
19205                             month = 11;
19206                             year -= 1;
19207                         }else{
19208                             month -= 1;
19209                         }
19210                     } else if (className.indexOf('new') > -1) {
19211                         if (month == 11) {
19212                             month = 0;
19213                             year += 1;
19214                         } else {
19215                             month += 1;
19216                         }
19217                     }
19218                     //Roo.log([year,month,day]);
19219                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19220                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19221 //                    this.fill();
19222                     //Roo.log(this.formatDate(this.date));
19223                     this.setValue(this.formatDate(this.date));
19224                     this.hidePopup();
19225                 }
19226                 break;
19227         }
19228     },
19229     
19230     setStartDate: function(startDate)
19231     {
19232         this.startDate = startDate || -Infinity;
19233         if (this.startDate !== -Infinity) {
19234             this.startDate = this.parseDate(this.startDate);
19235         }
19236         this.update();
19237         this.updateNavArrows();
19238     },
19239
19240     setEndDate: function(endDate)
19241     {
19242         this.endDate = endDate || Infinity;
19243         if (this.endDate !== Infinity) {
19244             this.endDate = this.parseDate(this.endDate);
19245         }
19246         this.update();
19247         this.updateNavArrows();
19248     },
19249     
19250     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19251     {
19252         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19253         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19254             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19255         }
19256         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19257             return parseInt(d, 10);
19258         });
19259         this.update();
19260         this.updateNavArrows();
19261     },
19262     
19263     updateNavArrows: function() 
19264     {
19265         if(this.singleMode){
19266             return;
19267         }
19268         
19269         var d = new Date(this.viewDate),
19270         year = d.getUTCFullYear(),
19271         month = d.getUTCMonth();
19272         
19273         Roo.each(this.picker().select('.prev', true).elements, function(v){
19274             v.show();
19275             switch (this.viewMode) {
19276                 case 0:
19277
19278                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19279                         v.hide();
19280                     }
19281                     break;
19282                 case 1:
19283                 case 2:
19284                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19285                         v.hide();
19286                     }
19287                     break;
19288             }
19289         });
19290         
19291         Roo.each(this.picker().select('.next', true).elements, function(v){
19292             v.show();
19293             switch (this.viewMode) {
19294                 case 0:
19295
19296                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19297                         v.hide();
19298                     }
19299                     break;
19300                 case 1:
19301                 case 2:
19302                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19303                         v.hide();
19304                     }
19305                     break;
19306             }
19307         })
19308     },
19309     
19310     moveMonth: function(date, dir)
19311     {
19312         if (!dir) {
19313             return date;
19314         }
19315         var new_date = new Date(date.valueOf()),
19316         day = new_date.getUTCDate(),
19317         month = new_date.getUTCMonth(),
19318         mag = Math.abs(dir),
19319         new_month, test;
19320         dir = dir > 0 ? 1 : -1;
19321         if (mag == 1){
19322             test = dir == -1
19323             // If going back one month, make sure month is not current month
19324             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19325             ? function(){
19326                 return new_date.getUTCMonth() == month;
19327             }
19328             // If going forward one month, make sure month is as expected
19329             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19330             : function(){
19331                 return new_date.getUTCMonth() != new_month;
19332             };
19333             new_month = month + dir;
19334             new_date.setUTCMonth(new_month);
19335             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19336             if (new_month < 0 || new_month > 11) {
19337                 new_month = (new_month + 12) % 12;
19338             }
19339         } else {
19340             // For magnitudes >1, move one month at a time...
19341             for (var i=0; i<mag; i++) {
19342                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19343                 new_date = this.moveMonth(new_date, dir);
19344             }
19345             // ...then reset the day, keeping it in the new month
19346             new_month = new_date.getUTCMonth();
19347             new_date.setUTCDate(day);
19348             test = function(){
19349                 return new_month != new_date.getUTCMonth();
19350             };
19351         }
19352         // Common date-resetting loop -- if date is beyond end of month, make it
19353         // end of month
19354         while (test()){
19355             new_date.setUTCDate(--day);
19356             new_date.setUTCMonth(new_month);
19357         }
19358         return new_date;
19359     },
19360
19361     moveYear: function(date, dir)
19362     {
19363         return this.moveMonth(date, dir*12);
19364     },
19365
19366     dateWithinRange: function(date)
19367     {
19368         return date >= this.startDate && date <= this.endDate;
19369     },
19370
19371     
19372     remove: function() 
19373     {
19374         this.picker().remove();
19375     },
19376     
19377     validateValue : function(value)
19378     {
19379         if(this.getVisibilityEl().hasClass('hidden')){
19380             return true;
19381         }
19382         
19383         if(value.length < 1)  {
19384             if(this.allowBlank){
19385                 return true;
19386             }
19387             return false;
19388         }
19389         
19390         if(value.length < this.minLength){
19391             return false;
19392         }
19393         if(value.length > this.maxLength){
19394             return false;
19395         }
19396         if(this.vtype){
19397             var vt = Roo.form.VTypes;
19398             if(!vt[this.vtype](value, this)){
19399                 return false;
19400             }
19401         }
19402         if(typeof this.validator == "function"){
19403             var msg = this.validator(value);
19404             if(msg !== true){
19405                 return false;
19406             }
19407         }
19408         
19409         if(this.regex && !this.regex.test(value)){
19410             return false;
19411         }
19412         
19413         if(typeof(this.parseDate(value)) == 'undefined'){
19414             return false;
19415         }
19416         
19417         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19418             return false;
19419         }      
19420         
19421         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19422             return false;
19423         } 
19424         
19425         
19426         return true;
19427     },
19428     
19429     reset : function()
19430     {
19431         this.date = this.viewDate = '';
19432         
19433         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19434     }
19435    
19436 });
19437
19438 Roo.apply(Roo.bootstrap.DateField,  {
19439     
19440     head : {
19441         tag: 'thead',
19442         cn: [
19443         {
19444             tag: 'tr',
19445             cn: [
19446             {
19447                 tag: 'th',
19448                 cls: 'prev',
19449                 html: '<i class="fa fa-arrow-left"/>'
19450             },
19451             {
19452                 tag: 'th',
19453                 cls: 'switch',
19454                 colspan: '5'
19455             },
19456             {
19457                 tag: 'th',
19458                 cls: 'next',
19459                 html: '<i class="fa fa-arrow-right"/>'
19460             }
19461
19462             ]
19463         }
19464         ]
19465     },
19466     
19467     content : {
19468         tag: 'tbody',
19469         cn: [
19470         {
19471             tag: 'tr',
19472             cn: [
19473             {
19474                 tag: 'td',
19475                 colspan: '7'
19476             }
19477             ]
19478         }
19479         ]
19480     },
19481     
19482     footer : {
19483         tag: 'tfoot',
19484         cn: [
19485         {
19486             tag: 'tr',
19487             cn: [
19488             {
19489                 tag: 'th',
19490                 colspan: '7',
19491                 cls: 'today'
19492             }
19493                     
19494             ]
19495         }
19496         ]
19497     },
19498     
19499     dates:{
19500         en: {
19501             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19502             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19503             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19504             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19505             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19506             today: "Today"
19507         }
19508     },
19509     
19510     modes: [
19511     {
19512         clsName: 'days',
19513         navFnc: 'Month',
19514         navStep: 1
19515     },
19516     {
19517         clsName: 'months',
19518         navFnc: 'FullYear',
19519         navStep: 1
19520     },
19521     {
19522         clsName: 'years',
19523         navFnc: 'FullYear',
19524         navStep: 10
19525     }]
19526 });
19527
19528 Roo.apply(Roo.bootstrap.DateField,  {
19529   
19530     template : {
19531         tag: 'div',
19532         cls: 'datepicker dropdown-menu roo-dynamic',
19533         cn: [
19534         {
19535             tag: 'div',
19536             cls: 'datepicker-days',
19537             cn: [
19538             {
19539                 tag: 'table',
19540                 cls: 'table-condensed',
19541                 cn:[
19542                 Roo.bootstrap.DateField.head,
19543                 {
19544                     tag: 'tbody'
19545                 },
19546                 Roo.bootstrap.DateField.footer
19547                 ]
19548             }
19549             ]
19550         },
19551         {
19552             tag: 'div',
19553             cls: 'datepicker-months',
19554             cn: [
19555             {
19556                 tag: 'table',
19557                 cls: 'table-condensed',
19558                 cn:[
19559                 Roo.bootstrap.DateField.head,
19560                 Roo.bootstrap.DateField.content,
19561                 Roo.bootstrap.DateField.footer
19562                 ]
19563             }
19564             ]
19565         },
19566         {
19567             tag: 'div',
19568             cls: 'datepicker-years',
19569             cn: [
19570             {
19571                 tag: 'table',
19572                 cls: 'table-condensed',
19573                 cn:[
19574                 Roo.bootstrap.DateField.head,
19575                 Roo.bootstrap.DateField.content,
19576                 Roo.bootstrap.DateField.footer
19577                 ]
19578             }
19579             ]
19580         }
19581         ]
19582     }
19583 });
19584
19585  
19586
19587  /*
19588  * - LGPL
19589  *
19590  * TimeField
19591  * 
19592  */
19593
19594 /**
19595  * @class Roo.bootstrap.TimeField
19596  * @extends Roo.bootstrap.Input
19597  * Bootstrap DateField class
19598  * 
19599  * 
19600  * @constructor
19601  * Create a new TimeField
19602  * @param {Object} config The config object
19603  */
19604
19605 Roo.bootstrap.TimeField = function(config){
19606     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19607     this.addEvents({
19608             /**
19609              * @event show
19610              * Fires when this field show.
19611              * @param {Roo.bootstrap.DateField} thisthis
19612              * @param {Mixed} date The date value
19613              */
19614             show : true,
19615             /**
19616              * @event show
19617              * Fires when this field hide.
19618              * @param {Roo.bootstrap.DateField} this
19619              * @param {Mixed} date The date value
19620              */
19621             hide : true,
19622             /**
19623              * @event select
19624              * Fires when select a date.
19625              * @param {Roo.bootstrap.DateField} this
19626              * @param {Mixed} date The date value
19627              */
19628             select : true
19629         });
19630 };
19631
19632 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19633     
19634     /**
19635      * @cfg {String} format
19636      * The default time format string which can be overriden for localization support.  The format must be
19637      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19638      */
19639     format : "H:i",
19640        
19641     onRender: function(ct, position)
19642     {
19643         
19644         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19645                 
19646         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19647         
19648         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19649         
19650         this.pop = this.picker().select('>.datepicker-time',true).first();
19651         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19652         
19653         this.picker().on('mousedown', this.onMousedown, this);
19654         this.picker().on('click', this.onClick, this);
19655         
19656         this.picker().addClass('datepicker-dropdown');
19657     
19658         this.fillTime();
19659         this.update();
19660             
19661         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19662         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19663         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19664         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19665         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19666         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19667
19668     },
19669     
19670     fireKey: function(e){
19671         if (!this.picker().isVisible()){
19672             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19673                 this.show();
19674             }
19675             return;
19676         }
19677
19678         e.preventDefault();
19679         
19680         switch(e.keyCode){
19681             case 27: // escape
19682                 this.hide();
19683                 break;
19684             case 37: // left
19685             case 39: // right
19686                 this.onTogglePeriod();
19687                 break;
19688             case 38: // up
19689                 this.onIncrementMinutes();
19690                 break;
19691             case 40: // down
19692                 this.onDecrementMinutes();
19693                 break;
19694             case 13: // enter
19695             case 9: // tab
19696                 this.setTime();
19697                 break;
19698         }
19699     },
19700     
19701     onClick: function(e) {
19702         e.stopPropagation();
19703         e.preventDefault();
19704     },
19705     
19706     picker : function()
19707     {
19708         return this.el.select('.datepicker', true).first();
19709     },
19710     
19711     fillTime: function()
19712     {    
19713         var time = this.pop.select('tbody', true).first();
19714         
19715         time.dom.innerHTML = '';
19716         
19717         time.createChild({
19718             tag: 'tr',
19719             cn: [
19720                 {
19721                     tag: 'td',
19722                     cn: [
19723                         {
19724                             tag: 'a',
19725                             href: '#',
19726                             cls: 'btn',
19727                             cn: [
19728                                 {
19729                                     tag: 'span',
19730                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19731                                 }
19732                             ]
19733                         } 
19734                     ]
19735                 },
19736                 {
19737                     tag: 'td',
19738                     cls: 'separator'
19739                 },
19740                 {
19741                     tag: 'td',
19742                     cn: [
19743                         {
19744                             tag: 'a',
19745                             href: '#',
19746                             cls: 'btn',
19747                             cn: [
19748                                 {
19749                                     tag: 'span',
19750                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19751                                 }
19752                             ]
19753                         }
19754                     ]
19755                 },
19756                 {
19757                     tag: 'td',
19758                     cls: 'separator'
19759                 }
19760             ]
19761         });
19762         
19763         time.createChild({
19764             tag: 'tr',
19765             cn: [
19766                 {
19767                     tag: 'td',
19768                     cn: [
19769                         {
19770                             tag: 'span',
19771                             cls: 'timepicker-hour',
19772                             html: '00'
19773                         }  
19774                     ]
19775                 },
19776                 {
19777                     tag: 'td',
19778                     cls: 'separator',
19779                     html: ':'
19780                 },
19781                 {
19782                     tag: 'td',
19783                     cn: [
19784                         {
19785                             tag: 'span',
19786                             cls: 'timepicker-minute',
19787                             html: '00'
19788                         }  
19789                     ]
19790                 },
19791                 {
19792                     tag: 'td',
19793                     cls: 'separator'
19794                 },
19795                 {
19796                     tag: 'td',
19797                     cn: [
19798                         {
19799                             tag: 'button',
19800                             type: 'button',
19801                             cls: 'btn btn-primary period',
19802                             html: 'AM'
19803                             
19804                         }
19805                     ]
19806                 }
19807             ]
19808         });
19809         
19810         time.createChild({
19811             tag: 'tr',
19812             cn: [
19813                 {
19814                     tag: 'td',
19815                     cn: [
19816                         {
19817                             tag: 'a',
19818                             href: '#',
19819                             cls: 'btn',
19820                             cn: [
19821                                 {
19822                                     tag: 'span',
19823                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19824                                 }
19825                             ]
19826                         }
19827                     ]
19828                 },
19829                 {
19830                     tag: 'td',
19831                     cls: 'separator'
19832                 },
19833                 {
19834                     tag: 'td',
19835                     cn: [
19836                         {
19837                             tag: 'a',
19838                             href: '#',
19839                             cls: 'btn',
19840                             cn: [
19841                                 {
19842                                     tag: 'span',
19843                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19844                                 }
19845                             ]
19846                         }
19847                     ]
19848                 },
19849                 {
19850                     tag: 'td',
19851                     cls: 'separator'
19852                 }
19853             ]
19854         });
19855         
19856     },
19857     
19858     update: function()
19859     {
19860         
19861         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19862         
19863         this.fill();
19864     },
19865     
19866     fill: function() 
19867     {
19868         var hours = this.time.getHours();
19869         var minutes = this.time.getMinutes();
19870         var period = 'AM';
19871         
19872         if(hours > 11){
19873             period = 'PM';
19874         }
19875         
19876         if(hours == 0){
19877             hours = 12;
19878         }
19879         
19880         
19881         if(hours > 12){
19882             hours = hours - 12;
19883         }
19884         
19885         if(hours < 10){
19886             hours = '0' + hours;
19887         }
19888         
19889         if(minutes < 10){
19890             minutes = '0' + minutes;
19891         }
19892         
19893         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19894         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19895         this.pop.select('button', true).first().dom.innerHTML = period;
19896         
19897     },
19898     
19899     place: function()
19900     {   
19901         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19902         
19903         var cls = ['bottom'];
19904         
19905         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19906             cls.pop();
19907             cls.push('top');
19908         }
19909         
19910         cls.push('right');
19911         
19912         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19913             cls.pop();
19914             cls.push('left');
19915         }
19916         
19917         this.picker().addClass(cls.join('-'));
19918         
19919         var _this = this;
19920         
19921         Roo.each(cls, function(c){
19922             if(c == 'bottom'){
19923                 _this.picker().setTop(_this.inputEl().getHeight());
19924                 return;
19925             }
19926             if(c == 'top'){
19927                 _this.picker().setTop(0 - _this.picker().getHeight());
19928                 return;
19929             }
19930             
19931             if(c == 'left'){
19932                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19933                 return;
19934             }
19935             if(c == 'right'){
19936                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19937                 return;
19938             }
19939         });
19940         
19941     },
19942   
19943     onFocus : function()
19944     {
19945         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19946         this.show();
19947     },
19948     
19949     onBlur : function()
19950     {
19951         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19952         this.hide();
19953     },
19954     
19955     show : function()
19956     {
19957         this.picker().show();
19958         this.pop.show();
19959         this.update();
19960         this.place();
19961         
19962         this.fireEvent('show', this, this.date);
19963     },
19964     
19965     hide : function()
19966     {
19967         this.picker().hide();
19968         this.pop.hide();
19969         
19970         this.fireEvent('hide', this, this.date);
19971     },
19972     
19973     setTime : function()
19974     {
19975         this.hide();
19976         this.setValue(this.time.format(this.format));
19977         
19978         this.fireEvent('select', this, this.date);
19979         
19980         
19981     },
19982     
19983     onMousedown: function(e){
19984         e.stopPropagation();
19985         e.preventDefault();
19986     },
19987     
19988     onIncrementHours: function()
19989     {
19990         Roo.log('onIncrementHours');
19991         this.time = this.time.add(Date.HOUR, 1);
19992         this.update();
19993         
19994     },
19995     
19996     onDecrementHours: function()
19997     {
19998         Roo.log('onDecrementHours');
19999         this.time = this.time.add(Date.HOUR, -1);
20000         this.update();
20001     },
20002     
20003     onIncrementMinutes: function()
20004     {
20005         Roo.log('onIncrementMinutes');
20006         this.time = this.time.add(Date.MINUTE, 1);
20007         this.update();
20008     },
20009     
20010     onDecrementMinutes: function()
20011     {
20012         Roo.log('onDecrementMinutes');
20013         this.time = this.time.add(Date.MINUTE, -1);
20014         this.update();
20015     },
20016     
20017     onTogglePeriod: function()
20018     {
20019         Roo.log('onTogglePeriod');
20020         this.time = this.time.add(Date.HOUR, 12);
20021         this.update();
20022     }
20023     
20024    
20025 });
20026
20027 Roo.apply(Roo.bootstrap.TimeField,  {
20028     
20029     content : {
20030         tag: 'tbody',
20031         cn: [
20032             {
20033                 tag: 'tr',
20034                 cn: [
20035                 {
20036                     tag: 'td',
20037                     colspan: '7'
20038                 }
20039                 ]
20040             }
20041         ]
20042     },
20043     
20044     footer : {
20045         tag: 'tfoot',
20046         cn: [
20047             {
20048                 tag: 'tr',
20049                 cn: [
20050                 {
20051                     tag: 'th',
20052                     colspan: '7',
20053                     cls: '',
20054                     cn: [
20055                         {
20056                             tag: 'button',
20057                             cls: 'btn btn-info ok',
20058                             html: 'OK'
20059                         }
20060                     ]
20061                 }
20062
20063                 ]
20064             }
20065         ]
20066     }
20067 });
20068
20069 Roo.apply(Roo.bootstrap.TimeField,  {
20070   
20071     template : {
20072         tag: 'div',
20073         cls: 'datepicker dropdown-menu',
20074         cn: [
20075             {
20076                 tag: 'div',
20077                 cls: 'datepicker-time',
20078                 cn: [
20079                 {
20080                     tag: 'table',
20081                     cls: 'table-condensed',
20082                     cn:[
20083                     Roo.bootstrap.TimeField.content,
20084                     Roo.bootstrap.TimeField.footer
20085                     ]
20086                 }
20087                 ]
20088             }
20089         ]
20090     }
20091 });
20092
20093  
20094
20095  /*
20096  * - LGPL
20097  *
20098  * MonthField
20099  * 
20100  */
20101
20102 /**
20103  * @class Roo.bootstrap.MonthField
20104  * @extends Roo.bootstrap.Input
20105  * Bootstrap MonthField class
20106  * 
20107  * @cfg {String} language default en
20108  * 
20109  * @constructor
20110  * Create a new MonthField
20111  * @param {Object} config The config object
20112  */
20113
20114 Roo.bootstrap.MonthField = function(config){
20115     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20116     
20117     this.addEvents({
20118         /**
20119          * @event show
20120          * Fires when this field show.
20121          * @param {Roo.bootstrap.MonthField} this
20122          * @param {Mixed} date The date value
20123          */
20124         show : true,
20125         /**
20126          * @event show
20127          * Fires when this field hide.
20128          * @param {Roo.bootstrap.MonthField} this
20129          * @param {Mixed} date The date value
20130          */
20131         hide : true,
20132         /**
20133          * @event select
20134          * Fires when select a date.
20135          * @param {Roo.bootstrap.MonthField} this
20136          * @param {String} oldvalue The old value
20137          * @param {String} newvalue The new value
20138          */
20139         select : true
20140     });
20141 };
20142
20143 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20144     
20145     onRender: function(ct, position)
20146     {
20147         
20148         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20149         
20150         this.language = this.language || 'en';
20151         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20152         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20153         
20154         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20155         this.isInline = false;
20156         this.isInput = true;
20157         this.component = this.el.select('.add-on', true).first() || false;
20158         this.component = (this.component && this.component.length === 0) ? false : this.component;
20159         this.hasInput = this.component && this.inputEL().length;
20160         
20161         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20162         
20163         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20164         
20165         this.picker().on('mousedown', this.onMousedown, this);
20166         this.picker().on('click', this.onClick, this);
20167         
20168         this.picker().addClass('datepicker-dropdown');
20169         
20170         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20171             v.setStyle('width', '189px');
20172         });
20173         
20174         this.fillMonths();
20175         
20176         this.update();
20177         
20178         if(this.isInline) {
20179             this.show();
20180         }
20181         
20182     },
20183     
20184     setValue: function(v, suppressEvent)
20185     {   
20186         var o = this.getValue();
20187         
20188         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20189         
20190         this.update();
20191
20192         if(suppressEvent !== true){
20193             this.fireEvent('select', this, o, v);
20194         }
20195         
20196     },
20197     
20198     getValue: function()
20199     {
20200         return this.value;
20201     },
20202     
20203     onClick: function(e) 
20204     {
20205         e.stopPropagation();
20206         e.preventDefault();
20207         
20208         var target = e.getTarget();
20209         
20210         if(target.nodeName.toLowerCase() === 'i'){
20211             target = Roo.get(target).dom.parentNode;
20212         }
20213         
20214         var nodeName = target.nodeName;
20215         var className = target.className;
20216         var html = target.innerHTML;
20217         
20218         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20219             return;
20220         }
20221         
20222         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20223         
20224         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20225         
20226         this.hide();
20227                         
20228     },
20229     
20230     picker : function()
20231     {
20232         return this.pickerEl;
20233     },
20234     
20235     fillMonths: function()
20236     {    
20237         var i = 0;
20238         var months = this.picker().select('>.datepicker-months td', true).first();
20239         
20240         months.dom.innerHTML = '';
20241         
20242         while (i < 12) {
20243             var month = {
20244                 tag: 'span',
20245                 cls: 'month',
20246                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20247             };
20248             
20249             months.createChild(month);
20250         }
20251         
20252     },
20253     
20254     update: function()
20255     {
20256         var _this = this;
20257         
20258         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20259             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20260         }
20261         
20262         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20263             e.removeClass('active');
20264             
20265             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20266                 e.addClass('active');
20267             }
20268         })
20269     },
20270     
20271     place: function()
20272     {
20273         if(this.isInline) {
20274             return;
20275         }
20276         
20277         this.picker().removeClass(['bottom', 'top']);
20278         
20279         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20280             /*
20281              * place to the top of element!
20282              *
20283              */
20284             
20285             this.picker().addClass('top');
20286             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20287             
20288             return;
20289         }
20290         
20291         this.picker().addClass('bottom');
20292         
20293         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20294     },
20295     
20296     onFocus : function()
20297     {
20298         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20299         this.show();
20300     },
20301     
20302     onBlur : function()
20303     {
20304         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20305         
20306         var d = this.inputEl().getValue();
20307         
20308         this.setValue(d);
20309                 
20310         this.hide();
20311     },
20312     
20313     show : function()
20314     {
20315         this.picker().show();
20316         this.picker().select('>.datepicker-months', true).first().show();
20317         this.update();
20318         this.place();
20319         
20320         this.fireEvent('show', this, this.date);
20321     },
20322     
20323     hide : function()
20324     {
20325         if(this.isInline) {
20326             return;
20327         }
20328         this.picker().hide();
20329         this.fireEvent('hide', this, this.date);
20330         
20331     },
20332     
20333     onMousedown: function(e)
20334     {
20335         e.stopPropagation();
20336         e.preventDefault();
20337     },
20338     
20339     keyup: function(e)
20340     {
20341         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20342         this.update();
20343     },
20344
20345     fireKey: function(e)
20346     {
20347         if (!this.picker().isVisible()){
20348             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20349                 this.show();
20350             }
20351             return;
20352         }
20353         
20354         var dir;
20355         
20356         switch(e.keyCode){
20357             case 27: // escape
20358                 this.hide();
20359                 e.preventDefault();
20360                 break;
20361             case 37: // left
20362             case 39: // right
20363                 dir = e.keyCode == 37 ? -1 : 1;
20364                 
20365                 this.vIndex = this.vIndex + dir;
20366                 
20367                 if(this.vIndex < 0){
20368                     this.vIndex = 0;
20369                 }
20370                 
20371                 if(this.vIndex > 11){
20372                     this.vIndex = 11;
20373                 }
20374                 
20375                 if(isNaN(this.vIndex)){
20376                     this.vIndex = 0;
20377                 }
20378                 
20379                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20380                 
20381                 break;
20382             case 38: // up
20383             case 40: // down
20384                 
20385                 dir = e.keyCode == 38 ? -1 : 1;
20386                 
20387                 this.vIndex = this.vIndex + dir * 4;
20388                 
20389                 if(this.vIndex < 0){
20390                     this.vIndex = 0;
20391                 }
20392                 
20393                 if(this.vIndex > 11){
20394                     this.vIndex = 11;
20395                 }
20396                 
20397                 if(isNaN(this.vIndex)){
20398                     this.vIndex = 0;
20399                 }
20400                 
20401                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20402                 break;
20403                 
20404             case 13: // enter
20405                 
20406                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20407                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20408                 }
20409                 
20410                 this.hide();
20411                 e.preventDefault();
20412                 break;
20413             case 9: // tab
20414                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20415                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20416                 }
20417                 this.hide();
20418                 break;
20419             case 16: // shift
20420             case 17: // ctrl
20421             case 18: // alt
20422                 break;
20423             default :
20424                 this.hide();
20425                 
20426         }
20427     },
20428     
20429     remove: function() 
20430     {
20431         this.picker().remove();
20432     }
20433    
20434 });
20435
20436 Roo.apply(Roo.bootstrap.MonthField,  {
20437     
20438     content : {
20439         tag: 'tbody',
20440         cn: [
20441         {
20442             tag: 'tr',
20443             cn: [
20444             {
20445                 tag: 'td',
20446                 colspan: '7'
20447             }
20448             ]
20449         }
20450         ]
20451     },
20452     
20453     dates:{
20454         en: {
20455             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20456             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20457         }
20458     }
20459 });
20460
20461 Roo.apply(Roo.bootstrap.MonthField,  {
20462   
20463     template : {
20464         tag: 'div',
20465         cls: 'datepicker dropdown-menu roo-dynamic',
20466         cn: [
20467             {
20468                 tag: 'div',
20469                 cls: 'datepicker-months',
20470                 cn: [
20471                 {
20472                     tag: 'table',
20473                     cls: 'table-condensed',
20474                     cn:[
20475                         Roo.bootstrap.DateField.content
20476                     ]
20477                 }
20478                 ]
20479             }
20480         ]
20481     }
20482 });
20483
20484  
20485
20486  
20487  /*
20488  * - LGPL
20489  *
20490  * CheckBox
20491  * 
20492  */
20493
20494 /**
20495  * @class Roo.bootstrap.CheckBox
20496  * @extends Roo.bootstrap.Input
20497  * Bootstrap CheckBox class
20498  * 
20499  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20500  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20501  * @cfg {String} boxLabel The text that appears beside the checkbox
20502  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20503  * @cfg {Boolean} checked initnal the element
20504  * @cfg {Boolean} inline inline the element (default false)
20505  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20506  * @cfg {String} tooltip label tooltip
20507  * 
20508  * @constructor
20509  * Create a new CheckBox
20510  * @param {Object} config The config object
20511  */
20512
20513 Roo.bootstrap.CheckBox = function(config){
20514     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20515    
20516     this.addEvents({
20517         /**
20518         * @event check
20519         * Fires when the element is checked or unchecked.
20520         * @param {Roo.bootstrap.CheckBox} this This input
20521         * @param {Boolean} checked The new checked value
20522         */
20523        check : true,
20524        /**
20525         * @event click
20526         * Fires when the element is click.
20527         * @param {Roo.bootstrap.CheckBox} this This input
20528         */
20529        click : true
20530     });
20531     
20532 };
20533
20534 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20535   
20536     inputType: 'checkbox',
20537     inputValue: 1,
20538     valueOff: 0,
20539     boxLabel: false,
20540     checked: false,
20541     weight : false,
20542     inline: false,
20543     tooltip : '',
20544     
20545     getAutoCreate : function()
20546     {
20547         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20548         
20549         var id = Roo.id();
20550         
20551         var cfg = {};
20552         
20553         cfg.cls = 'form-group ' + this.inputType; //input-group
20554         
20555         if(this.inline){
20556             cfg.cls += ' ' + this.inputType + '-inline';
20557         }
20558         
20559         var input =  {
20560             tag: 'input',
20561             id : id,
20562             type : this.inputType,
20563             value : this.inputValue,
20564             cls : 'roo-' + this.inputType, //'form-box',
20565             placeholder : this.placeholder || ''
20566             
20567         };
20568         
20569         if(this.inputType != 'radio'){
20570             var hidden =  {
20571                 tag: 'input',
20572                 type : 'hidden',
20573                 cls : 'roo-hidden-value',
20574                 value : this.checked ? this.inputValue : this.valueOff
20575             };
20576         }
20577         
20578             
20579         if (this.weight) { // Validity check?
20580             cfg.cls += " " + this.inputType + "-" + this.weight;
20581         }
20582         
20583         if (this.disabled) {
20584             input.disabled=true;
20585         }
20586         
20587         if(this.checked){
20588             input.checked = this.checked;
20589         }
20590         
20591         if (this.name) {
20592             
20593             input.name = this.name;
20594             
20595             if(this.inputType != 'radio'){
20596                 hidden.name = this.name;
20597                 input.name = '_hidden_' + this.name;
20598             }
20599         }
20600         
20601         if (this.size) {
20602             input.cls += ' input-' + this.size;
20603         }
20604         
20605         var settings=this;
20606         
20607         ['xs','sm','md','lg'].map(function(size){
20608             if (settings[size]) {
20609                 cfg.cls += ' col-' + size + '-' + settings[size];
20610             }
20611         });
20612         
20613         var inputblock = input;
20614          
20615         if (this.before || this.after) {
20616             
20617             inputblock = {
20618                 cls : 'input-group',
20619                 cn :  [] 
20620             };
20621             
20622             if (this.before) {
20623                 inputblock.cn.push({
20624                     tag :'span',
20625                     cls : 'input-group-addon',
20626                     html : this.before
20627                 });
20628             }
20629             
20630             inputblock.cn.push(input);
20631             
20632             if(this.inputType != 'radio'){
20633                 inputblock.cn.push(hidden);
20634             }
20635             
20636             if (this.after) {
20637                 inputblock.cn.push({
20638                     tag :'span',
20639                     cls : 'input-group-addon',
20640                     html : this.after
20641                 });
20642             }
20643             
20644         }
20645         
20646         if (align ==='left' && this.fieldLabel.length) {
20647 //                Roo.log("left and has label");
20648             cfg.cn = [
20649                 {
20650                     tag: 'label',
20651                     'for' :  id,
20652                     cls : 'control-label',
20653                     html : this.fieldLabel
20654                 },
20655                 {
20656                     cls : "", 
20657                     cn: [
20658                         inputblock
20659                     ]
20660                 }
20661             ];
20662             
20663             if(this.labelWidth > 12){
20664                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20665             }
20666             
20667             if(this.labelWidth < 13 && this.labelmd == 0){
20668                 this.labelmd = this.labelWidth;
20669             }
20670             
20671             if(this.labellg > 0){
20672                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20673                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20674             }
20675             
20676             if(this.labelmd > 0){
20677                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20678                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20679             }
20680             
20681             if(this.labelsm > 0){
20682                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20683                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20684             }
20685             
20686             if(this.labelxs > 0){
20687                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20688                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20689             }
20690             
20691         } else if ( this.fieldLabel.length) {
20692 //                Roo.log(" label");
20693                 cfg.cn = [
20694                    
20695                     {
20696                         tag: this.boxLabel ? 'span' : 'label',
20697                         'for': id,
20698                         cls: 'control-label box-input-label',
20699                         //cls : 'input-group-addon',
20700                         html : this.fieldLabel
20701                     },
20702                     
20703                     inputblock
20704                     
20705                 ];
20706
20707         } else {
20708             
20709 //                Roo.log(" no label && no align");
20710                 cfg.cn = [  inputblock ] ;
20711                 
20712                 
20713         }
20714         
20715         if(this.boxLabel){
20716              var boxLabelCfg = {
20717                 tag: 'label',
20718                 //'for': id, // box label is handled by onclick - so no for...
20719                 cls: 'box-label',
20720                 html: this.boxLabel
20721             };
20722             
20723             if(this.tooltip){
20724                 boxLabelCfg.tooltip = this.tooltip;
20725             }
20726              
20727             cfg.cn.push(boxLabelCfg);
20728         }
20729         
20730         if(this.inputType != 'radio'){
20731             cfg.cn.push(hidden);
20732         }
20733         
20734         return cfg;
20735         
20736     },
20737     
20738     /**
20739      * return the real input element.
20740      */
20741     inputEl: function ()
20742     {
20743         return this.el.select('input.roo-' + this.inputType,true).first();
20744     },
20745     hiddenEl: function ()
20746     {
20747         return this.el.select('input.roo-hidden-value',true).first();
20748     },
20749     
20750     labelEl: function()
20751     {
20752         return this.el.select('label.control-label',true).first();
20753     },
20754     /* depricated... */
20755     
20756     label: function()
20757     {
20758         return this.labelEl();
20759     },
20760     
20761     boxLabelEl: function()
20762     {
20763         return this.el.select('label.box-label',true).first();
20764     },
20765     
20766     initEvents : function()
20767     {
20768 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20769         
20770         this.inputEl().on('click', this.onClick,  this);
20771         
20772         if (this.boxLabel) { 
20773             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20774         }
20775         
20776         this.startValue = this.getValue();
20777         
20778         if(this.groupId){
20779             Roo.bootstrap.CheckBox.register(this);
20780         }
20781     },
20782     
20783     onClick : function(e)
20784     {   
20785         if(this.fireEvent('click', this, e) !== false){
20786             this.setChecked(!this.checked);
20787         }
20788         
20789     },
20790     
20791     setChecked : function(state,suppressEvent)
20792     {
20793         this.startValue = this.getValue();
20794
20795         if(this.inputType == 'radio'){
20796             
20797             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20798                 e.dom.checked = false;
20799             });
20800             
20801             this.inputEl().dom.checked = true;
20802             
20803             this.inputEl().dom.value = this.inputValue;
20804             
20805             if(suppressEvent !== true){
20806                 this.fireEvent('check', this, true);
20807             }
20808             
20809             this.validate();
20810             
20811             return;
20812         }
20813         
20814         this.checked = state;
20815         
20816         this.inputEl().dom.checked = state;
20817         
20818         
20819         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20820         
20821         if(suppressEvent !== true){
20822             this.fireEvent('check', this, state);
20823         }
20824         
20825         this.validate();
20826     },
20827     
20828     getValue : function()
20829     {
20830         if(this.inputType == 'radio'){
20831             return this.getGroupValue();
20832         }
20833         
20834         return this.hiddenEl().dom.value;
20835         
20836     },
20837     
20838     getGroupValue : function()
20839     {
20840         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20841             return '';
20842         }
20843         
20844         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20845     },
20846     
20847     setValue : function(v,suppressEvent)
20848     {
20849         if(this.inputType == 'radio'){
20850             this.setGroupValue(v, suppressEvent);
20851             return;
20852         }
20853         
20854         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20855         
20856         this.validate();
20857     },
20858     
20859     setGroupValue : function(v, suppressEvent)
20860     {
20861         this.startValue = this.getValue();
20862         
20863         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20864             e.dom.checked = false;
20865             
20866             if(e.dom.value == v){
20867                 e.dom.checked = true;
20868             }
20869         });
20870         
20871         if(suppressEvent !== true){
20872             this.fireEvent('check', this, true);
20873         }
20874
20875         this.validate();
20876         
20877         return;
20878     },
20879     
20880     validate : function()
20881     {
20882         if(this.getVisibilityEl().hasClass('hidden')){
20883             return true;
20884         }
20885         
20886         if(
20887                 this.disabled || 
20888                 (this.inputType == 'radio' && this.validateRadio()) ||
20889                 (this.inputType == 'checkbox' && this.validateCheckbox())
20890         ){
20891             this.markValid();
20892             return true;
20893         }
20894         
20895         this.markInvalid();
20896         return false;
20897     },
20898     
20899     validateRadio : function()
20900     {
20901         if(this.getVisibilityEl().hasClass('hidden')){
20902             return true;
20903         }
20904         
20905         if(this.allowBlank){
20906             return true;
20907         }
20908         
20909         var valid = false;
20910         
20911         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20912             if(!e.dom.checked){
20913                 return;
20914             }
20915             
20916             valid = true;
20917             
20918             return false;
20919         });
20920         
20921         return valid;
20922     },
20923     
20924     validateCheckbox : function()
20925     {
20926         if(!this.groupId){
20927             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20928             //return (this.getValue() == this.inputValue) ? true : false;
20929         }
20930         
20931         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20932         
20933         if(!group){
20934             return false;
20935         }
20936         
20937         var r = false;
20938         
20939         for(var i in group){
20940             if(group[i].el.isVisible(true)){
20941                 r = false;
20942                 break;
20943             }
20944             
20945             r = true;
20946         }
20947         
20948         for(var i in group){
20949             if(r){
20950                 break;
20951             }
20952             
20953             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20954         }
20955         
20956         return r;
20957     },
20958     
20959     /**
20960      * Mark this field as valid
20961      */
20962     markValid : function()
20963     {
20964         var _this = this;
20965         
20966         this.fireEvent('valid', this);
20967         
20968         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20969         
20970         if(this.groupId){
20971             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20972         }
20973         
20974         if(label){
20975             label.markValid();
20976         }
20977
20978         if(this.inputType == 'radio'){
20979             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20980                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20981                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20982             });
20983             
20984             return;
20985         }
20986
20987         if(!this.groupId){
20988             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20989             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20990             return;
20991         }
20992         
20993         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20994         
20995         if(!group){
20996             return;
20997         }
20998         
20999         for(var i in group){
21000             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21001             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21002         }
21003     },
21004     
21005      /**
21006      * Mark this field as invalid
21007      * @param {String} msg The validation message
21008      */
21009     markInvalid : function(msg)
21010     {
21011         if(this.allowBlank){
21012             return;
21013         }
21014         
21015         var _this = this;
21016         
21017         this.fireEvent('invalid', this, msg);
21018         
21019         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21020         
21021         if(this.groupId){
21022             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21023         }
21024         
21025         if(label){
21026             label.markInvalid();
21027         }
21028             
21029         if(this.inputType == 'radio'){
21030             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21031                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21032                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21033             });
21034             
21035             return;
21036         }
21037         
21038         if(!this.groupId){
21039             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21040             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21041             return;
21042         }
21043         
21044         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21045         
21046         if(!group){
21047             return;
21048         }
21049         
21050         for(var i in group){
21051             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21052             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21053         }
21054         
21055     },
21056     
21057     clearInvalid : function()
21058     {
21059         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21060         
21061         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21062         
21063         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21064         
21065         if (label && label.iconEl) {
21066             label.iconEl.removeClass(label.validClass);
21067             label.iconEl.removeClass(label.invalidClass);
21068         }
21069     },
21070     
21071     disable : function()
21072     {
21073         if(this.inputType != 'radio'){
21074             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21075             return;
21076         }
21077         
21078         var _this = this;
21079         
21080         if(this.rendered){
21081             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21082                 _this.getActionEl().addClass(this.disabledClass);
21083                 e.dom.disabled = true;
21084             });
21085         }
21086         
21087         this.disabled = true;
21088         this.fireEvent("disable", this);
21089         return this;
21090     },
21091
21092     enable : function()
21093     {
21094         if(this.inputType != 'radio'){
21095             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21096             return;
21097         }
21098         
21099         var _this = this;
21100         
21101         if(this.rendered){
21102             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21103                 _this.getActionEl().removeClass(this.disabledClass);
21104                 e.dom.disabled = false;
21105             });
21106         }
21107         
21108         this.disabled = false;
21109         this.fireEvent("enable", this);
21110         return this;
21111     },
21112     
21113     setBoxLabel : function(v)
21114     {
21115         this.boxLabel = v;
21116         
21117         if(this.rendered){
21118             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21119         }
21120     }
21121
21122 });
21123
21124 Roo.apply(Roo.bootstrap.CheckBox, {
21125     
21126     groups: {},
21127     
21128      /**
21129     * register a CheckBox Group
21130     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21131     */
21132     register : function(checkbox)
21133     {
21134         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21135             this.groups[checkbox.groupId] = {};
21136         }
21137         
21138         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21139             return;
21140         }
21141         
21142         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21143         
21144     },
21145     /**
21146     * fetch a CheckBox Group based on the group ID
21147     * @param {string} the group ID
21148     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21149     */
21150     get: function(groupId) {
21151         if (typeof(this.groups[groupId]) == 'undefined') {
21152             return false;
21153         }
21154         
21155         return this.groups[groupId] ;
21156     }
21157     
21158     
21159 });
21160 /*
21161  * - LGPL
21162  *
21163  * RadioItem
21164  * 
21165  */
21166
21167 /**
21168  * @class Roo.bootstrap.Radio
21169  * @extends Roo.bootstrap.Component
21170  * Bootstrap Radio class
21171  * @cfg {String} boxLabel - the label associated
21172  * @cfg {String} value - the value of radio
21173  * 
21174  * @constructor
21175  * Create a new Radio
21176  * @param {Object} config The config object
21177  */
21178 Roo.bootstrap.Radio = function(config){
21179     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21180     
21181 };
21182
21183 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21184     
21185     boxLabel : '',
21186     
21187     value : '',
21188     
21189     getAutoCreate : function()
21190     {
21191         var cfg = {
21192             tag : 'div',
21193             cls : 'form-group radio',
21194             cn : [
21195                 {
21196                     tag : 'label',
21197                     cls : 'box-label',
21198                     html : this.boxLabel
21199                 }
21200             ]
21201         };
21202         
21203         return cfg;
21204     },
21205     
21206     initEvents : function() 
21207     {
21208         this.parent().register(this);
21209         
21210         this.el.on('click', this.onClick, this);
21211         
21212     },
21213     
21214     onClick : function(e)
21215     {
21216         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21217             this.setChecked(true);
21218         }
21219     },
21220     
21221     setChecked : function(state, suppressEvent)
21222     {
21223         this.parent().setValue(this.value, suppressEvent);
21224         
21225     },
21226     
21227     setBoxLabel : function(v)
21228     {
21229         this.boxLabel = v;
21230         
21231         if(this.rendered){
21232             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21233         }
21234     }
21235     
21236 });
21237  
21238
21239  /*
21240  * - LGPL
21241  *
21242  * Input
21243  * 
21244  */
21245
21246 /**
21247  * @class Roo.bootstrap.SecurePass
21248  * @extends Roo.bootstrap.Input
21249  * Bootstrap SecurePass class
21250  *
21251  * 
21252  * @constructor
21253  * Create a new SecurePass
21254  * @param {Object} config The config object
21255  */
21256  
21257 Roo.bootstrap.SecurePass = function (config) {
21258     // these go here, so the translation tool can replace them..
21259     this.errors = {
21260         PwdEmpty: "Please type a password, and then retype it to confirm.",
21261         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21262         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21263         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21264         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21265         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21266         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21267         TooWeak: "Your password is Too Weak."
21268     },
21269     this.meterLabel = "Password strength:";
21270     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21271     this.meterClass = [
21272         "roo-password-meter-tooweak", 
21273         "roo-password-meter-weak", 
21274         "roo-password-meter-medium", 
21275         "roo-password-meter-strong", 
21276         "roo-password-meter-grey"
21277     ];
21278     
21279     this.errors = {};
21280     
21281     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21282 }
21283
21284 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21285     /**
21286      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21287      * {
21288      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21289      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21290      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21291      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21292      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21293      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21294      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21295      * })
21296      */
21297     // private
21298     
21299     meterWidth: 300,
21300     errorMsg :'',    
21301     errors: false,
21302     imageRoot: '/',
21303     /**
21304      * @cfg {String/Object} Label for the strength meter (defaults to
21305      * 'Password strength:')
21306      */
21307     // private
21308     meterLabel: '',
21309     /**
21310      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21311      * ['Weak', 'Medium', 'Strong'])
21312      */
21313     // private    
21314     pwdStrengths: false,    
21315     // private
21316     strength: 0,
21317     // private
21318     _lastPwd: null,
21319     // private
21320     kCapitalLetter: 0,
21321     kSmallLetter: 1,
21322     kDigit: 2,
21323     kPunctuation: 3,
21324     
21325     insecure: false,
21326     // private
21327     initEvents: function ()
21328     {
21329         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21330
21331         if (this.el.is('input[type=password]') && Roo.isSafari) {
21332             this.el.on('keydown', this.SafariOnKeyDown, this);
21333         }
21334
21335         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21336     },
21337     // private
21338     onRender: function (ct, position)
21339     {
21340         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21341         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21342         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21343
21344         this.trigger.createChild({
21345                    cn: [
21346                     {
21347                     //id: 'PwdMeter',
21348                     tag: 'div',
21349                     cls: 'roo-password-meter-grey col-xs-12',
21350                     style: {
21351                         //width: 0,
21352                         //width: this.meterWidth + 'px'                                                
21353                         }
21354                     },
21355                     {                            
21356                          cls: 'roo-password-meter-text'                          
21357                     }
21358                 ]            
21359         });
21360
21361          
21362         if (this.hideTrigger) {
21363             this.trigger.setDisplayed(false);
21364         }
21365         this.setSize(this.width || '', this.height || '');
21366     },
21367     // private
21368     onDestroy: function ()
21369     {
21370         if (this.trigger) {
21371             this.trigger.removeAllListeners();
21372             this.trigger.remove();
21373         }
21374         if (this.wrap) {
21375             this.wrap.remove();
21376         }
21377         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21378     },
21379     // private
21380     checkStrength: function ()
21381     {
21382         var pwd = this.inputEl().getValue();
21383         if (pwd == this._lastPwd) {
21384             return;
21385         }
21386
21387         var strength;
21388         if (this.ClientSideStrongPassword(pwd)) {
21389             strength = 3;
21390         } else if (this.ClientSideMediumPassword(pwd)) {
21391             strength = 2;
21392         } else if (this.ClientSideWeakPassword(pwd)) {
21393             strength = 1;
21394         } else {
21395             strength = 0;
21396         }
21397         
21398         Roo.log('strength1: ' + strength);
21399         
21400         //var pm = this.trigger.child('div/div/div').dom;
21401         var pm = this.trigger.child('div/div');
21402         pm.removeClass(this.meterClass);
21403         pm.addClass(this.meterClass[strength]);
21404                 
21405         
21406         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21407                 
21408         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21409         
21410         this._lastPwd = pwd;
21411     },
21412     reset: function ()
21413     {
21414         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21415         
21416         this._lastPwd = '';
21417         
21418         var pm = this.trigger.child('div/div');
21419         pm.removeClass(this.meterClass);
21420         pm.addClass('roo-password-meter-grey');        
21421         
21422         
21423         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21424         
21425         pt.innerHTML = '';
21426         this.inputEl().dom.type='password';
21427     },
21428     // private
21429     validateValue: function (value)
21430     {
21431         
21432         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21433             return false;
21434         }
21435         if (value.length == 0) {
21436             if (this.allowBlank) {
21437                 this.clearInvalid();
21438                 return true;
21439             }
21440
21441             this.markInvalid(this.errors.PwdEmpty);
21442             this.errorMsg = this.errors.PwdEmpty;
21443             return false;
21444         }
21445         
21446         if(this.insecure){
21447             return true;
21448         }
21449         
21450         if ('[\x21-\x7e]*'.match(value)) {
21451             this.markInvalid(this.errors.PwdBadChar);
21452             this.errorMsg = this.errors.PwdBadChar;
21453             return false;
21454         }
21455         if (value.length < 6) {
21456             this.markInvalid(this.errors.PwdShort);
21457             this.errorMsg = this.errors.PwdShort;
21458             return false;
21459         }
21460         if (value.length > 16) {
21461             this.markInvalid(this.errors.PwdLong);
21462             this.errorMsg = this.errors.PwdLong;
21463             return false;
21464         }
21465         var strength;
21466         if (this.ClientSideStrongPassword(value)) {
21467             strength = 3;
21468         } else if (this.ClientSideMediumPassword(value)) {
21469             strength = 2;
21470         } else if (this.ClientSideWeakPassword(value)) {
21471             strength = 1;
21472         } else {
21473             strength = 0;
21474         }
21475
21476         
21477         if (strength < 2) {
21478             //this.markInvalid(this.errors.TooWeak);
21479             this.errorMsg = this.errors.TooWeak;
21480             //return false;
21481         }
21482         
21483         
21484         console.log('strength2: ' + strength);
21485         
21486         //var pm = this.trigger.child('div/div/div').dom;
21487         
21488         var pm = this.trigger.child('div/div');
21489         pm.removeClass(this.meterClass);
21490         pm.addClass(this.meterClass[strength]);
21491                 
21492         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21493                 
21494         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21495         
21496         this.errorMsg = ''; 
21497         return true;
21498     },
21499     // private
21500     CharacterSetChecks: function (type)
21501     {
21502         this.type = type;
21503         this.fResult = false;
21504     },
21505     // private
21506     isctype: function (character, type)
21507     {
21508         switch (type) {  
21509             case this.kCapitalLetter:
21510                 if (character >= 'A' && character <= 'Z') {
21511                     return true;
21512                 }
21513                 break;
21514             
21515             case this.kSmallLetter:
21516                 if (character >= 'a' && character <= 'z') {
21517                     return true;
21518                 }
21519                 break;
21520             
21521             case this.kDigit:
21522                 if (character >= '0' && character <= '9') {
21523                     return true;
21524                 }
21525                 break;
21526             
21527             case this.kPunctuation:
21528                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21529                     return true;
21530                 }
21531                 break;
21532             
21533             default:
21534                 return false;
21535         }
21536
21537     },
21538     // private
21539     IsLongEnough: function (pwd, size)
21540     {
21541         return !(pwd == null || isNaN(size) || pwd.length < size);
21542     },
21543     // private
21544     SpansEnoughCharacterSets: function (word, nb)
21545     {
21546         if (!this.IsLongEnough(word, nb))
21547         {
21548             return false;
21549         }
21550
21551         var characterSetChecks = new Array(
21552             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21553             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21554         );
21555         
21556         for (var index = 0; index < word.length; ++index) {
21557             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21558                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21559                     characterSetChecks[nCharSet].fResult = true;
21560                     break;
21561                 }
21562             }
21563         }
21564
21565         var nCharSets = 0;
21566         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21567             if (characterSetChecks[nCharSet].fResult) {
21568                 ++nCharSets;
21569             }
21570         }
21571
21572         if (nCharSets < nb) {
21573             return false;
21574         }
21575         return true;
21576     },
21577     // private
21578     ClientSideStrongPassword: function (pwd)
21579     {
21580         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21581     },
21582     // private
21583     ClientSideMediumPassword: function (pwd)
21584     {
21585         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21586     },
21587     // private
21588     ClientSideWeakPassword: function (pwd)
21589     {
21590         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21591     }
21592           
21593 })//<script type="text/javascript">
21594
21595 /*
21596  * Based  Ext JS Library 1.1.1
21597  * Copyright(c) 2006-2007, Ext JS, LLC.
21598  * LGPL
21599  *
21600  */
21601  
21602 /**
21603  * @class Roo.HtmlEditorCore
21604  * @extends Roo.Component
21605  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21606  *
21607  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21608  */
21609
21610 Roo.HtmlEditorCore = function(config){
21611     
21612     
21613     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21614     
21615     
21616     this.addEvents({
21617         /**
21618          * @event initialize
21619          * Fires when the editor is fully initialized (including the iframe)
21620          * @param {Roo.HtmlEditorCore} this
21621          */
21622         initialize: true,
21623         /**
21624          * @event activate
21625          * Fires when the editor is first receives the focus. Any insertion must wait
21626          * until after this event.
21627          * @param {Roo.HtmlEditorCore} this
21628          */
21629         activate: true,
21630          /**
21631          * @event beforesync
21632          * Fires before the textarea is updated with content from the editor iframe. Return false
21633          * to cancel the sync.
21634          * @param {Roo.HtmlEditorCore} this
21635          * @param {String} html
21636          */
21637         beforesync: true,
21638          /**
21639          * @event beforepush
21640          * Fires before the iframe editor is updated with content from the textarea. Return false
21641          * to cancel the push.
21642          * @param {Roo.HtmlEditorCore} this
21643          * @param {String} html
21644          */
21645         beforepush: true,
21646          /**
21647          * @event sync
21648          * Fires when the textarea is updated with content from the editor iframe.
21649          * @param {Roo.HtmlEditorCore} this
21650          * @param {String} html
21651          */
21652         sync: true,
21653          /**
21654          * @event push
21655          * Fires when the iframe editor is updated with content from the textarea.
21656          * @param {Roo.HtmlEditorCore} this
21657          * @param {String} html
21658          */
21659         push: true,
21660         
21661         /**
21662          * @event editorevent
21663          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21664          * @param {Roo.HtmlEditorCore} this
21665          */
21666         editorevent: true
21667         
21668     });
21669     
21670     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21671     
21672     // defaults : white / black...
21673     this.applyBlacklists();
21674     
21675     
21676     
21677 };
21678
21679
21680 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21681
21682
21683      /**
21684      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21685      */
21686     
21687     owner : false,
21688     
21689      /**
21690      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21691      *                        Roo.resizable.
21692      */
21693     resizable : false,
21694      /**
21695      * @cfg {Number} height (in pixels)
21696      */   
21697     height: 300,
21698    /**
21699      * @cfg {Number} width (in pixels)
21700      */   
21701     width: 500,
21702     
21703     /**
21704      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21705      * 
21706      */
21707     stylesheets: false,
21708     
21709     // id of frame..
21710     frameId: false,
21711     
21712     // private properties
21713     validationEvent : false,
21714     deferHeight: true,
21715     initialized : false,
21716     activated : false,
21717     sourceEditMode : false,
21718     onFocus : Roo.emptyFn,
21719     iframePad:3,
21720     hideMode:'offsets',
21721     
21722     clearUp: true,
21723     
21724     // blacklist + whitelisted elements..
21725     black: false,
21726     white: false,
21727      
21728     bodyCls : '',
21729
21730     /**
21731      * Protected method that will not generally be called directly. It
21732      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21733      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21734      */
21735     getDocMarkup : function(){
21736         // body styles..
21737         var st = '';
21738         
21739         // inherit styels from page...?? 
21740         if (this.stylesheets === false) {
21741             
21742             Roo.get(document.head).select('style').each(function(node) {
21743                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21744             });
21745             
21746             Roo.get(document.head).select('link').each(function(node) { 
21747                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21748             });
21749             
21750         } else if (!this.stylesheets.length) {
21751                 // simple..
21752                 st = '<style type="text/css">' +
21753                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21754                    '</style>';
21755         } else { 
21756             st = '<style type="text/css">' +
21757                     this.stylesheets +
21758                 '</style>';
21759         }
21760         
21761         st +=  '<style type="text/css">' +
21762             'IMG { cursor: pointer } ' +
21763         '</style>';
21764
21765         var cls = 'roo-htmleditor-body';
21766         
21767         if(this.bodyCls.length){
21768             cls += ' ' + this.bodyCls;
21769         }
21770         
21771         return '<html><head>' + st  +
21772             //<style type="text/css">' +
21773             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21774             //'</style>' +
21775             ' </head><body class="' +  cls + '"></body></html>';
21776     },
21777
21778     // private
21779     onRender : function(ct, position)
21780     {
21781         var _t = this;
21782         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21783         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21784         
21785         
21786         this.el.dom.style.border = '0 none';
21787         this.el.dom.setAttribute('tabIndex', -1);
21788         this.el.addClass('x-hidden hide');
21789         
21790         
21791         
21792         if(Roo.isIE){ // fix IE 1px bogus margin
21793             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21794         }
21795        
21796         
21797         this.frameId = Roo.id();
21798         
21799          
21800         
21801         var iframe = this.owner.wrap.createChild({
21802             tag: 'iframe',
21803             cls: 'form-control', // bootstrap..
21804             id: this.frameId,
21805             name: this.frameId,
21806             frameBorder : 'no',
21807             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21808         }, this.el
21809         );
21810         
21811         
21812         this.iframe = iframe.dom;
21813
21814          this.assignDocWin();
21815         
21816         this.doc.designMode = 'on';
21817        
21818         this.doc.open();
21819         this.doc.write(this.getDocMarkup());
21820         this.doc.close();
21821
21822         
21823         var task = { // must defer to wait for browser to be ready
21824             run : function(){
21825                 //console.log("run task?" + this.doc.readyState);
21826                 this.assignDocWin();
21827                 if(this.doc.body || this.doc.readyState == 'complete'){
21828                     try {
21829                         this.doc.designMode="on";
21830                     } catch (e) {
21831                         return;
21832                     }
21833                     Roo.TaskMgr.stop(task);
21834                     this.initEditor.defer(10, this);
21835                 }
21836             },
21837             interval : 10,
21838             duration: 10000,
21839             scope: this
21840         };
21841         Roo.TaskMgr.start(task);
21842
21843     },
21844
21845     // private
21846     onResize : function(w, h)
21847     {
21848          Roo.log('resize: ' +w + ',' + h );
21849         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21850         if(!this.iframe){
21851             return;
21852         }
21853         if(typeof w == 'number'){
21854             
21855             this.iframe.style.width = w + 'px';
21856         }
21857         if(typeof h == 'number'){
21858             
21859             this.iframe.style.height = h + 'px';
21860             if(this.doc){
21861                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21862             }
21863         }
21864         
21865     },
21866
21867     /**
21868      * Toggles the editor between standard and source edit mode.
21869      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21870      */
21871     toggleSourceEdit : function(sourceEditMode){
21872         
21873         this.sourceEditMode = sourceEditMode === true;
21874         
21875         if(this.sourceEditMode){
21876  
21877             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21878             
21879         }else{
21880             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21881             //this.iframe.className = '';
21882             this.deferFocus();
21883         }
21884         //this.setSize(this.owner.wrap.getSize());
21885         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21886     },
21887
21888     
21889   
21890
21891     /**
21892      * Protected method that will not generally be called directly. If you need/want
21893      * custom HTML cleanup, this is the method you should override.
21894      * @param {String} html The HTML to be cleaned
21895      * return {String} The cleaned HTML
21896      */
21897     cleanHtml : function(html){
21898         html = String(html);
21899         if(html.length > 5){
21900             if(Roo.isSafari){ // strip safari nonsense
21901                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21902             }
21903         }
21904         if(html == '&nbsp;'){
21905             html = '';
21906         }
21907         return html;
21908     },
21909
21910     /**
21911      * HTML Editor -> Textarea
21912      * Protected method that will not generally be called directly. Syncs the contents
21913      * of the editor iframe with the textarea.
21914      */
21915     syncValue : function(){
21916         if(this.initialized){
21917             var bd = (this.doc.body || this.doc.documentElement);
21918             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21919             var html = bd.innerHTML;
21920             if(Roo.isSafari){
21921                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21922                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21923                 if(m && m[1]){
21924                     html = '<div style="'+m[0]+'">' + html + '</div>';
21925                 }
21926             }
21927             html = this.cleanHtml(html);
21928             // fix up the special chars.. normaly like back quotes in word...
21929             // however we do not want to do this with chinese..
21930             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21931                 var cc = b.charCodeAt();
21932                 if (
21933                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21934                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21935                     (cc >= 0xf900 && cc < 0xfb00 )
21936                 ) {
21937                         return b;
21938                 }
21939                 return "&#"+cc+";" 
21940             });
21941             if(this.owner.fireEvent('beforesync', this, html) !== false){
21942                 this.el.dom.value = html;
21943                 this.owner.fireEvent('sync', this, html);
21944             }
21945         }
21946     },
21947
21948     /**
21949      * Protected method that will not generally be called directly. Pushes the value of the textarea
21950      * into the iframe editor.
21951      */
21952     pushValue : function(){
21953         if(this.initialized){
21954             var v = this.el.dom.value.trim();
21955             
21956 //            if(v.length < 1){
21957 //                v = '&#160;';
21958 //            }
21959             
21960             if(this.owner.fireEvent('beforepush', this, v) !== false){
21961                 var d = (this.doc.body || this.doc.documentElement);
21962                 d.innerHTML = v;
21963                 this.cleanUpPaste();
21964                 this.el.dom.value = d.innerHTML;
21965                 this.owner.fireEvent('push', this, v);
21966             }
21967         }
21968     },
21969
21970     // private
21971     deferFocus : function(){
21972         this.focus.defer(10, this);
21973     },
21974
21975     // doc'ed in Field
21976     focus : function(){
21977         if(this.win && !this.sourceEditMode){
21978             this.win.focus();
21979         }else{
21980             this.el.focus();
21981         }
21982     },
21983     
21984     assignDocWin: function()
21985     {
21986         var iframe = this.iframe;
21987         
21988          if(Roo.isIE){
21989             this.doc = iframe.contentWindow.document;
21990             this.win = iframe.contentWindow;
21991         } else {
21992 //            if (!Roo.get(this.frameId)) {
21993 //                return;
21994 //            }
21995 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21996 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21997             
21998             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21999                 return;
22000             }
22001             
22002             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22003             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22004         }
22005     },
22006     
22007     // private
22008     initEditor : function(){
22009         //console.log("INIT EDITOR");
22010         this.assignDocWin();
22011         
22012         
22013         
22014         this.doc.designMode="on";
22015         this.doc.open();
22016         this.doc.write(this.getDocMarkup());
22017         this.doc.close();
22018         
22019         var dbody = (this.doc.body || this.doc.documentElement);
22020         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22021         // this copies styles from the containing element into thsi one..
22022         // not sure why we need all of this..
22023         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22024         
22025         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22026         //ss['background-attachment'] = 'fixed'; // w3c
22027         dbody.bgProperties = 'fixed'; // ie
22028         //Roo.DomHelper.applyStyles(dbody, ss);
22029         Roo.EventManager.on(this.doc, {
22030             //'mousedown': this.onEditorEvent,
22031             'mouseup': this.onEditorEvent,
22032             'dblclick': this.onEditorEvent,
22033             'click': this.onEditorEvent,
22034             'keyup': this.onEditorEvent,
22035             buffer:100,
22036             scope: this
22037         });
22038         if(Roo.isGecko){
22039             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22040         }
22041         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22042             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22043         }
22044         this.initialized = true;
22045
22046         this.owner.fireEvent('initialize', this);
22047         this.pushValue();
22048     },
22049
22050     // private
22051     onDestroy : function(){
22052         
22053         
22054         
22055         if(this.rendered){
22056             
22057             //for (var i =0; i < this.toolbars.length;i++) {
22058             //    // fixme - ask toolbars for heights?
22059             //    this.toolbars[i].onDestroy();
22060            // }
22061             
22062             //this.wrap.dom.innerHTML = '';
22063             //this.wrap.remove();
22064         }
22065     },
22066
22067     // private
22068     onFirstFocus : function(){
22069         
22070         this.assignDocWin();
22071         
22072         
22073         this.activated = true;
22074          
22075     
22076         if(Roo.isGecko){ // prevent silly gecko errors
22077             this.win.focus();
22078             var s = this.win.getSelection();
22079             if(!s.focusNode || s.focusNode.nodeType != 3){
22080                 var r = s.getRangeAt(0);
22081                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22082                 r.collapse(true);
22083                 this.deferFocus();
22084             }
22085             try{
22086                 this.execCmd('useCSS', true);
22087                 this.execCmd('styleWithCSS', false);
22088             }catch(e){}
22089         }
22090         this.owner.fireEvent('activate', this);
22091     },
22092
22093     // private
22094     adjustFont: function(btn){
22095         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22096         //if(Roo.isSafari){ // safari
22097         //    adjust *= 2;
22098        // }
22099         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22100         if(Roo.isSafari){ // safari
22101             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22102             v =  (v < 10) ? 10 : v;
22103             v =  (v > 48) ? 48 : v;
22104             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22105             
22106         }
22107         
22108         
22109         v = Math.max(1, v+adjust);
22110         
22111         this.execCmd('FontSize', v  );
22112     },
22113
22114     onEditorEvent : function(e)
22115     {
22116         this.owner.fireEvent('editorevent', this, e);
22117       //  this.updateToolbar();
22118         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22119     },
22120
22121     insertTag : function(tg)
22122     {
22123         // could be a bit smarter... -> wrap the current selected tRoo..
22124         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22125             
22126             range = this.createRange(this.getSelection());
22127             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22128             wrappingNode.appendChild(range.extractContents());
22129             range.insertNode(wrappingNode);
22130
22131             return;
22132             
22133             
22134             
22135         }
22136         this.execCmd("formatblock",   tg);
22137         
22138     },
22139     
22140     insertText : function(txt)
22141     {
22142         
22143         
22144         var range = this.createRange();
22145         range.deleteContents();
22146                //alert(Sender.getAttribute('label'));
22147                
22148         range.insertNode(this.doc.createTextNode(txt));
22149     } ,
22150     
22151      
22152
22153     /**
22154      * Executes a Midas editor command on the editor document and performs necessary focus and
22155      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22156      * @param {String} cmd The Midas command
22157      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22158      */
22159     relayCmd : function(cmd, value){
22160         this.win.focus();
22161         this.execCmd(cmd, value);
22162         this.owner.fireEvent('editorevent', this);
22163         //this.updateToolbar();
22164         this.owner.deferFocus();
22165     },
22166
22167     /**
22168      * Executes a Midas editor command directly on the editor document.
22169      * For visual commands, you should use {@link #relayCmd} instead.
22170      * <b>This should only be called after the editor is initialized.</b>
22171      * @param {String} cmd The Midas command
22172      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22173      */
22174     execCmd : function(cmd, value){
22175         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22176         this.syncValue();
22177     },
22178  
22179  
22180    
22181     /**
22182      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22183      * to insert tRoo.
22184      * @param {String} text | dom node.. 
22185      */
22186     insertAtCursor : function(text)
22187     {
22188         
22189         if(!this.activated){
22190             return;
22191         }
22192         /*
22193         if(Roo.isIE){
22194             this.win.focus();
22195             var r = this.doc.selection.createRange();
22196             if(r){
22197                 r.collapse(true);
22198                 r.pasteHTML(text);
22199                 this.syncValue();
22200                 this.deferFocus();
22201             
22202             }
22203             return;
22204         }
22205         */
22206         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22207             this.win.focus();
22208             
22209             
22210             // from jquery ui (MIT licenced)
22211             var range, node;
22212             var win = this.win;
22213             
22214             if (win.getSelection && win.getSelection().getRangeAt) {
22215                 range = win.getSelection().getRangeAt(0);
22216                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22217                 range.insertNode(node);
22218             } else if (win.document.selection && win.document.selection.createRange) {
22219                 // no firefox support
22220                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22221                 win.document.selection.createRange().pasteHTML(txt);
22222             } else {
22223                 // no firefox support
22224                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22225                 this.execCmd('InsertHTML', txt);
22226             } 
22227             
22228             this.syncValue();
22229             
22230             this.deferFocus();
22231         }
22232     },
22233  // private
22234     mozKeyPress : function(e){
22235         if(e.ctrlKey){
22236             var c = e.getCharCode(), cmd;
22237           
22238             if(c > 0){
22239                 c = String.fromCharCode(c).toLowerCase();
22240                 switch(c){
22241                     case 'b':
22242                         cmd = 'bold';
22243                         break;
22244                     case 'i':
22245                         cmd = 'italic';
22246                         break;
22247                     
22248                     case 'u':
22249                         cmd = 'underline';
22250                         break;
22251                     
22252                     case 'v':
22253                         this.cleanUpPaste.defer(100, this);
22254                         return;
22255                         
22256                 }
22257                 if(cmd){
22258                     this.win.focus();
22259                     this.execCmd(cmd);
22260                     this.deferFocus();
22261                     e.preventDefault();
22262                 }
22263                 
22264             }
22265         }
22266     },
22267
22268     // private
22269     fixKeys : function(){ // load time branching for fastest keydown performance
22270         if(Roo.isIE){
22271             return function(e){
22272                 var k = e.getKey(), r;
22273                 if(k == e.TAB){
22274                     e.stopEvent();
22275                     r = this.doc.selection.createRange();
22276                     if(r){
22277                         r.collapse(true);
22278                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22279                         this.deferFocus();
22280                     }
22281                     return;
22282                 }
22283                 
22284                 if(k == e.ENTER){
22285                     r = this.doc.selection.createRange();
22286                     if(r){
22287                         var target = r.parentElement();
22288                         if(!target || target.tagName.toLowerCase() != 'li'){
22289                             e.stopEvent();
22290                             r.pasteHTML('<br />');
22291                             r.collapse(false);
22292                             r.select();
22293                         }
22294                     }
22295                 }
22296                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22297                     this.cleanUpPaste.defer(100, this);
22298                     return;
22299                 }
22300                 
22301                 
22302             };
22303         }else if(Roo.isOpera){
22304             return function(e){
22305                 var k = e.getKey();
22306                 if(k == e.TAB){
22307                     e.stopEvent();
22308                     this.win.focus();
22309                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22310                     this.deferFocus();
22311                 }
22312                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22313                     this.cleanUpPaste.defer(100, this);
22314                     return;
22315                 }
22316                 
22317             };
22318         }else if(Roo.isSafari){
22319             return function(e){
22320                 var k = e.getKey();
22321                 
22322                 if(k == e.TAB){
22323                     e.stopEvent();
22324                     this.execCmd('InsertText','\t');
22325                     this.deferFocus();
22326                     return;
22327                 }
22328                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22329                     this.cleanUpPaste.defer(100, this);
22330                     return;
22331                 }
22332                 
22333              };
22334         }
22335     }(),
22336     
22337     getAllAncestors: function()
22338     {
22339         var p = this.getSelectedNode();
22340         var a = [];
22341         if (!p) {
22342             a.push(p); // push blank onto stack..
22343             p = this.getParentElement();
22344         }
22345         
22346         
22347         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22348             a.push(p);
22349             p = p.parentNode;
22350         }
22351         a.push(this.doc.body);
22352         return a;
22353     },
22354     lastSel : false,
22355     lastSelNode : false,
22356     
22357     
22358     getSelection : function() 
22359     {
22360         this.assignDocWin();
22361         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22362     },
22363     
22364     getSelectedNode: function() 
22365     {
22366         // this may only work on Gecko!!!
22367         
22368         // should we cache this!!!!
22369         
22370         
22371         
22372          
22373         var range = this.createRange(this.getSelection()).cloneRange();
22374         
22375         if (Roo.isIE) {
22376             var parent = range.parentElement();
22377             while (true) {
22378                 var testRange = range.duplicate();
22379                 testRange.moveToElementText(parent);
22380                 if (testRange.inRange(range)) {
22381                     break;
22382                 }
22383                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22384                     break;
22385                 }
22386                 parent = parent.parentElement;
22387             }
22388             return parent;
22389         }
22390         
22391         // is ancestor a text element.
22392         var ac =  range.commonAncestorContainer;
22393         if (ac.nodeType == 3) {
22394             ac = ac.parentNode;
22395         }
22396         
22397         var ar = ac.childNodes;
22398          
22399         var nodes = [];
22400         var other_nodes = [];
22401         var has_other_nodes = false;
22402         for (var i=0;i<ar.length;i++) {
22403             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22404                 continue;
22405             }
22406             // fullly contained node.
22407             
22408             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22409                 nodes.push(ar[i]);
22410                 continue;
22411             }
22412             
22413             // probably selected..
22414             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22415                 other_nodes.push(ar[i]);
22416                 continue;
22417             }
22418             // outer..
22419             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22420                 continue;
22421             }
22422             
22423             
22424             has_other_nodes = true;
22425         }
22426         if (!nodes.length && other_nodes.length) {
22427             nodes= other_nodes;
22428         }
22429         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22430             return false;
22431         }
22432         
22433         return nodes[0];
22434     },
22435     createRange: function(sel)
22436     {
22437         // this has strange effects when using with 
22438         // top toolbar - not sure if it's a great idea.
22439         //this.editor.contentWindow.focus();
22440         if (typeof sel != "undefined") {
22441             try {
22442                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22443             } catch(e) {
22444                 return this.doc.createRange();
22445             }
22446         } else {
22447             return this.doc.createRange();
22448         }
22449     },
22450     getParentElement: function()
22451     {
22452         
22453         this.assignDocWin();
22454         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22455         
22456         var range = this.createRange(sel);
22457          
22458         try {
22459             var p = range.commonAncestorContainer;
22460             while (p.nodeType == 3) { // text node
22461                 p = p.parentNode;
22462             }
22463             return p;
22464         } catch (e) {
22465             return null;
22466         }
22467     
22468     },
22469     /***
22470      *
22471      * Range intersection.. the hard stuff...
22472      *  '-1' = before
22473      *  '0' = hits..
22474      *  '1' = after.
22475      *         [ -- selected range --- ]
22476      *   [fail]                        [fail]
22477      *
22478      *    basically..
22479      *      if end is before start or  hits it. fail.
22480      *      if start is after end or hits it fail.
22481      *
22482      *   if either hits (but other is outside. - then it's not 
22483      *   
22484      *    
22485      **/
22486     
22487     
22488     // @see http://www.thismuchiknow.co.uk/?p=64.
22489     rangeIntersectsNode : function(range, node)
22490     {
22491         var nodeRange = node.ownerDocument.createRange();
22492         try {
22493             nodeRange.selectNode(node);
22494         } catch (e) {
22495             nodeRange.selectNodeContents(node);
22496         }
22497     
22498         var rangeStartRange = range.cloneRange();
22499         rangeStartRange.collapse(true);
22500     
22501         var rangeEndRange = range.cloneRange();
22502         rangeEndRange.collapse(false);
22503     
22504         var nodeStartRange = nodeRange.cloneRange();
22505         nodeStartRange.collapse(true);
22506     
22507         var nodeEndRange = nodeRange.cloneRange();
22508         nodeEndRange.collapse(false);
22509     
22510         return rangeStartRange.compareBoundaryPoints(
22511                  Range.START_TO_START, nodeEndRange) == -1 &&
22512                rangeEndRange.compareBoundaryPoints(
22513                  Range.START_TO_START, nodeStartRange) == 1;
22514         
22515          
22516     },
22517     rangeCompareNode : function(range, node)
22518     {
22519         var nodeRange = node.ownerDocument.createRange();
22520         try {
22521             nodeRange.selectNode(node);
22522         } catch (e) {
22523             nodeRange.selectNodeContents(node);
22524         }
22525         
22526         
22527         range.collapse(true);
22528     
22529         nodeRange.collapse(true);
22530      
22531         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22532         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22533          
22534         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22535         
22536         var nodeIsBefore   =  ss == 1;
22537         var nodeIsAfter    = ee == -1;
22538         
22539         if (nodeIsBefore && nodeIsAfter) {
22540             return 0; // outer
22541         }
22542         if (!nodeIsBefore && nodeIsAfter) {
22543             return 1; //right trailed.
22544         }
22545         
22546         if (nodeIsBefore && !nodeIsAfter) {
22547             return 2;  // left trailed.
22548         }
22549         // fully contined.
22550         return 3;
22551     },
22552
22553     // private? - in a new class?
22554     cleanUpPaste :  function()
22555     {
22556         // cleans up the whole document..
22557         Roo.log('cleanuppaste');
22558         
22559         this.cleanUpChildren(this.doc.body);
22560         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22561         if (clean != this.doc.body.innerHTML) {
22562             this.doc.body.innerHTML = clean;
22563         }
22564         
22565     },
22566     
22567     cleanWordChars : function(input) {// change the chars to hex code
22568         var he = Roo.HtmlEditorCore;
22569         
22570         var output = input;
22571         Roo.each(he.swapCodes, function(sw) { 
22572             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22573             
22574             output = output.replace(swapper, sw[1]);
22575         });
22576         
22577         return output;
22578     },
22579     
22580     
22581     cleanUpChildren : function (n)
22582     {
22583         if (!n.childNodes.length) {
22584             return;
22585         }
22586         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22587            this.cleanUpChild(n.childNodes[i]);
22588         }
22589     },
22590     
22591     
22592         
22593     
22594     cleanUpChild : function (node)
22595     {
22596         var ed = this;
22597         //console.log(node);
22598         if (node.nodeName == "#text") {
22599             // clean up silly Windows -- stuff?
22600             return; 
22601         }
22602         if (node.nodeName == "#comment") {
22603             node.parentNode.removeChild(node);
22604             // clean up silly Windows -- stuff?
22605             return; 
22606         }
22607         var lcname = node.tagName.toLowerCase();
22608         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22609         // whitelist of tags..
22610         
22611         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22612             // remove node.
22613             node.parentNode.removeChild(node);
22614             return;
22615             
22616         }
22617         
22618         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22619         
22620         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22621         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22622         
22623         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22624         //    remove_keep_children = true;
22625         //}
22626         
22627         if (remove_keep_children) {
22628             this.cleanUpChildren(node);
22629             // inserts everything just before this node...
22630             while (node.childNodes.length) {
22631                 var cn = node.childNodes[0];
22632                 node.removeChild(cn);
22633                 node.parentNode.insertBefore(cn, node);
22634             }
22635             node.parentNode.removeChild(node);
22636             return;
22637         }
22638         
22639         if (!node.attributes || !node.attributes.length) {
22640             this.cleanUpChildren(node);
22641             return;
22642         }
22643         
22644         function cleanAttr(n,v)
22645         {
22646             
22647             if (v.match(/^\./) || v.match(/^\//)) {
22648                 return;
22649             }
22650             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22651                 return;
22652             }
22653             if (v.match(/^#/)) {
22654                 return;
22655             }
22656 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22657             node.removeAttribute(n);
22658             
22659         }
22660         
22661         var cwhite = this.cwhite;
22662         var cblack = this.cblack;
22663             
22664         function cleanStyle(n,v)
22665         {
22666             if (v.match(/expression/)) { //XSS?? should we even bother..
22667                 node.removeAttribute(n);
22668                 return;
22669             }
22670             
22671             var parts = v.split(/;/);
22672             var clean = [];
22673             
22674             Roo.each(parts, function(p) {
22675                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22676                 if (!p.length) {
22677                     return true;
22678                 }
22679                 var l = p.split(':').shift().replace(/\s+/g,'');
22680                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22681                 
22682                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22683 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22684                     //node.removeAttribute(n);
22685                     return true;
22686                 }
22687                 //Roo.log()
22688                 // only allow 'c whitelisted system attributes'
22689                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22690 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22691                     //node.removeAttribute(n);
22692                     return true;
22693                 }
22694                 
22695                 
22696                  
22697                 
22698                 clean.push(p);
22699                 return true;
22700             });
22701             if (clean.length) { 
22702                 node.setAttribute(n, clean.join(';'));
22703             } else {
22704                 node.removeAttribute(n);
22705             }
22706             
22707         }
22708         
22709         
22710         for (var i = node.attributes.length-1; i > -1 ; i--) {
22711             var a = node.attributes[i];
22712             //console.log(a);
22713             
22714             if (a.name.toLowerCase().substr(0,2)=='on')  {
22715                 node.removeAttribute(a.name);
22716                 continue;
22717             }
22718             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22719                 node.removeAttribute(a.name);
22720                 continue;
22721             }
22722             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22723                 cleanAttr(a.name,a.value); // fixme..
22724                 continue;
22725             }
22726             if (a.name == 'style') {
22727                 cleanStyle(a.name,a.value);
22728                 continue;
22729             }
22730             /// clean up MS crap..
22731             // tecnically this should be a list of valid class'es..
22732             
22733             
22734             if (a.name == 'class') {
22735                 if (a.value.match(/^Mso/)) {
22736                     node.className = '';
22737                 }
22738                 
22739                 if (a.value.match(/^body$/)) {
22740                     node.className = '';
22741                 }
22742                 continue;
22743             }
22744             
22745             // style cleanup!?
22746             // class cleanup?
22747             
22748         }
22749         
22750         
22751         this.cleanUpChildren(node);
22752         
22753         
22754     },
22755     
22756     /**
22757      * Clean up MS wordisms...
22758      */
22759     cleanWord : function(node)
22760     {
22761         
22762         
22763         if (!node) {
22764             this.cleanWord(this.doc.body);
22765             return;
22766         }
22767         if (node.nodeName == "#text") {
22768             // clean up silly Windows -- stuff?
22769             return; 
22770         }
22771         if (node.nodeName == "#comment") {
22772             node.parentNode.removeChild(node);
22773             // clean up silly Windows -- stuff?
22774             return; 
22775         }
22776         
22777         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22778             node.parentNode.removeChild(node);
22779             return;
22780         }
22781         
22782         // remove - but keep children..
22783         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22784             while (node.childNodes.length) {
22785                 var cn = node.childNodes[0];
22786                 node.removeChild(cn);
22787                 node.parentNode.insertBefore(cn, node);
22788             }
22789             node.parentNode.removeChild(node);
22790             this.iterateChildren(node, this.cleanWord);
22791             return;
22792         }
22793         // clean styles
22794         if (node.className.length) {
22795             
22796             var cn = node.className.split(/\W+/);
22797             var cna = [];
22798             Roo.each(cn, function(cls) {
22799                 if (cls.match(/Mso[a-zA-Z]+/)) {
22800                     return;
22801                 }
22802                 cna.push(cls);
22803             });
22804             node.className = cna.length ? cna.join(' ') : '';
22805             if (!cna.length) {
22806                 node.removeAttribute("class");
22807             }
22808         }
22809         
22810         if (node.hasAttribute("lang")) {
22811             node.removeAttribute("lang");
22812         }
22813         
22814         if (node.hasAttribute("style")) {
22815             
22816             var styles = node.getAttribute("style").split(";");
22817             var nstyle = [];
22818             Roo.each(styles, function(s) {
22819                 if (!s.match(/:/)) {
22820                     return;
22821                 }
22822                 var kv = s.split(":");
22823                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22824                     return;
22825                 }
22826                 // what ever is left... we allow.
22827                 nstyle.push(s);
22828             });
22829             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22830             if (!nstyle.length) {
22831                 node.removeAttribute('style');
22832             }
22833         }
22834         this.iterateChildren(node, this.cleanWord);
22835         
22836         
22837         
22838     },
22839     /**
22840      * iterateChildren of a Node, calling fn each time, using this as the scole..
22841      * @param {DomNode} node node to iterate children of.
22842      * @param {Function} fn method of this class to call on each item.
22843      */
22844     iterateChildren : function(node, fn)
22845     {
22846         if (!node.childNodes.length) {
22847                 return;
22848         }
22849         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22850            fn.call(this, node.childNodes[i])
22851         }
22852     },
22853     
22854     
22855     /**
22856      * cleanTableWidths.
22857      *
22858      * Quite often pasting from word etc.. results in tables with column and widths.
22859      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22860      *
22861      */
22862     cleanTableWidths : function(node)
22863     {
22864          
22865          
22866         if (!node) {
22867             this.cleanTableWidths(this.doc.body);
22868             return;
22869         }
22870         
22871         // ignore list...
22872         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22873             return; 
22874         }
22875         Roo.log(node.tagName);
22876         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22877             this.iterateChildren(node, this.cleanTableWidths);
22878             return;
22879         }
22880         if (node.hasAttribute('width')) {
22881             node.removeAttribute('width');
22882         }
22883         
22884          
22885         if (node.hasAttribute("style")) {
22886             // pretty basic...
22887             
22888             var styles = node.getAttribute("style").split(";");
22889             var nstyle = [];
22890             Roo.each(styles, function(s) {
22891                 if (!s.match(/:/)) {
22892                     return;
22893                 }
22894                 var kv = s.split(":");
22895                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22896                     return;
22897                 }
22898                 // what ever is left... we allow.
22899                 nstyle.push(s);
22900             });
22901             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22902             if (!nstyle.length) {
22903                 node.removeAttribute('style');
22904             }
22905         }
22906         
22907         this.iterateChildren(node, this.cleanTableWidths);
22908         
22909         
22910     },
22911     
22912     
22913     
22914     
22915     domToHTML : function(currentElement, depth, nopadtext) {
22916         
22917         depth = depth || 0;
22918         nopadtext = nopadtext || false;
22919     
22920         if (!currentElement) {
22921             return this.domToHTML(this.doc.body);
22922         }
22923         
22924         //Roo.log(currentElement);
22925         var j;
22926         var allText = false;
22927         var nodeName = currentElement.nodeName;
22928         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22929         
22930         if  (nodeName == '#text') {
22931             
22932             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22933         }
22934         
22935         
22936         var ret = '';
22937         if (nodeName != 'BODY') {
22938              
22939             var i = 0;
22940             // Prints the node tagName, such as <A>, <IMG>, etc
22941             if (tagName) {
22942                 var attr = [];
22943                 for(i = 0; i < currentElement.attributes.length;i++) {
22944                     // quoting?
22945                     var aname = currentElement.attributes.item(i).name;
22946                     if (!currentElement.attributes.item(i).value.length) {
22947                         continue;
22948                     }
22949                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22950                 }
22951                 
22952                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22953             } 
22954             else {
22955                 
22956                 // eack
22957             }
22958         } else {
22959             tagName = false;
22960         }
22961         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22962             return ret;
22963         }
22964         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22965             nopadtext = true;
22966         }
22967         
22968         
22969         // Traverse the tree
22970         i = 0;
22971         var currentElementChild = currentElement.childNodes.item(i);
22972         var allText = true;
22973         var innerHTML  = '';
22974         lastnode = '';
22975         while (currentElementChild) {
22976             // Formatting code (indent the tree so it looks nice on the screen)
22977             var nopad = nopadtext;
22978             if (lastnode == 'SPAN') {
22979                 nopad  = true;
22980             }
22981             // text
22982             if  (currentElementChild.nodeName == '#text') {
22983                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22984                 toadd = nopadtext ? toadd : toadd.trim();
22985                 if (!nopad && toadd.length > 80) {
22986                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22987                 }
22988                 innerHTML  += toadd;
22989                 
22990                 i++;
22991                 currentElementChild = currentElement.childNodes.item(i);
22992                 lastNode = '';
22993                 continue;
22994             }
22995             allText = false;
22996             
22997             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22998                 
22999             // Recursively traverse the tree structure of the child node
23000             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23001             lastnode = currentElementChild.nodeName;
23002             i++;
23003             currentElementChild=currentElement.childNodes.item(i);
23004         }
23005         
23006         ret += innerHTML;
23007         
23008         if (!allText) {
23009                 // The remaining code is mostly for formatting the tree
23010             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23011         }
23012         
23013         
23014         if (tagName) {
23015             ret+= "</"+tagName+">";
23016         }
23017         return ret;
23018         
23019     },
23020         
23021     applyBlacklists : function()
23022     {
23023         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23024         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23025         
23026         this.white = [];
23027         this.black = [];
23028         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23029             if (b.indexOf(tag) > -1) {
23030                 return;
23031             }
23032             this.white.push(tag);
23033             
23034         }, this);
23035         
23036         Roo.each(w, function(tag) {
23037             if (b.indexOf(tag) > -1) {
23038                 return;
23039             }
23040             if (this.white.indexOf(tag) > -1) {
23041                 return;
23042             }
23043             this.white.push(tag);
23044             
23045         }, this);
23046         
23047         
23048         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23049             if (w.indexOf(tag) > -1) {
23050                 return;
23051             }
23052             this.black.push(tag);
23053             
23054         }, this);
23055         
23056         Roo.each(b, function(tag) {
23057             if (w.indexOf(tag) > -1) {
23058                 return;
23059             }
23060             if (this.black.indexOf(tag) > -1) {
23061                 return;
23062             }
23063             this.black.push(tag);
23064             
23065         }, this);
23066         
23067         
23068         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23069         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23070         
23071         this.cwhite = [];
23072         this.cblack = [];
23073         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23074             if (b.indexOf(tag) > -1) {
23075                 return;
23076             }
23077             this.cwhite.push(tag);
23078             
23079         }, this);
23080         
23081         Roo.each(w, function(tag) {
23082             if (b.indexOf(tag) > -1) {
23083                 return;
23084             }
23085             if (this.cwhite.indexOf(tag) > -1) {
23086                 return;
23087             }
23088             this.cwhite.push(tag);
23089             
23090         }, this);
23091         
23092         
23093         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23094             if (w.indexOf(tag) > -1) {
23095                 return;
23096             }
23097             this.cblack.push(tag);
23098             
23099         }, this);
23100         
23101         Roo.each(b, function(tag) {
23102             if (w.indexOf(tag) > -1) {
23103                 return;
23104             }
23105             if (this.cblack.indexOf(tag) > -1) {
23106                 return;
23107             }
23108             this.cblack.push(tag);
23109             
23110         }, this);
23111     },
23112     
23113     setStylesheets : function(stylesheets)
23114     {
23115         if(typeof(stylesheets) == 'string'){
23116             Roo.get(this.iframe.contentDocument.head).createChild({
23117                 tag : 'link',
23118                 rel : 'stylesheet',
23119                 type : 'text/css',
23120                 href : stylesheets
23121             });
23122             
23123             return;
23124         }
23125         var _this = this;
23126      
23127         Roo.each(stylesheets, function(s) {
23128             if(!s.length){
23129                 return;
23130             }
23131             
23132             Roo.get(_this.iframe.contentDocument.head).createChild({
23133                 tag : 'link',
23134                 rel : 'stylesheet',
23135                 type : 'text/css',
23136                 href : s
23137             });
23138         });
23139
23140         
23141     },
23142     
23143     removeStylesheets : function()
23144     {
23145         var _this = this;
23146         
23147         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23148             s.remove();
23149         });
23150     },
23151     
23152     setStyle : function(style)
23153     {
23154         Roo.get(this.iframe.contentDocument.head).createChild({
23155             tag : 'style',
23156             type : 'text/css',
23157             html : style
23158         });
23159
23160         return;
23161     }
23162     
23163     // hide stuff that is not compatible
23164     /**
23165      * @event blur
23166      * @hide
23167      */
23168     /**
23169      * @event change
23170      * @hide
23171      */
23172     /**
23173      * @event focus
23174      * @hide
23175      */
23176     /**
23177      * @event specialkey
23178      * @hide
23179      */
23180     /**
23181      * @cfg {String} fieldClass @hide
23182      */
23183     /**
23184      * @cfg {String} focusClass @hide
23185      */
23186     /**
23187      * @cfg {String} autoCreate @hide
23188      */
23189     /**
23190      * @cfg {String} inputType @hide
23191      */
23192     /**
23193      * @cfg {String} invalidClass @hide
23194      */
23195     /**
23196      * @cfg {String} invalidText @hide
23197      */
23198     /**
23199      * @cfg {String} msgFx @hide
23200      */
23201     /**
23202      * @cfg {String} validateOnBlur @hide
23203      */
23204 });
23205
23206 Roo.HtmlEditorCore.white = [
23207         'area', 'br', 'img', 'input', 'hr', 'wbr',
23208         
23209        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23210        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23211        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23212        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23213        'table',   'ul',         'xmp', 
23214        
23215        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23216       'thead',   'tr', 
23217      
23218       'dir', 'menu', 'ol', 'ul', 'dl',
23219        
23220       'embed',  'object'
23221 ];
23222
23223
23224 Roo.HtmlEditorCore.black = [
23225     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23226         'applet', // 
23227         'base',   'basefont', 'bgsound', 'blink',  'body', 
23228         'frame',  'frameset', 'head',    'html',   'ilayer', 
23229         'iframe', 'layer',  'link',     'meta',    'object',   
23230         'script', 'style' ,'title',  'xml' // clean later..
23231 ];
23232 Roo.HtmlEditorCore.clean = [
23233     'script', 'style', 'title', 'xml'
23234 ];
23235 Roo.HtmlEditorCore.remove = [
23236     'font'
23237 ];
23238 // attributes..
23239
23240 Roo.HtmlEditorCore.ablack = [
23241     'on'
23242 ];
23243     
23244 Roo.HtmlEditorCore.aclean = [ 
23245     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23246 ];
23247
23248 // protocols..
23249 Roo.HtmlEditorCore.pwhite= [
23250         'http',  'https',  'mailto'
23251 ];
23252
23253 // white listed style attributes.
23254 Roo.HtmlEditorCore.cwhite= [
23255       //  'text-align', /// default is to allow most things..
23256       
23257          
23258 //        'font-size'//??
23259 ];
23260
23261 // black listed style attributes.
23262 Roo.HtmlEditorCore.cblack= [
23263       //  'font-size' -- this can be set by the project 
23264 ];
23265
23266
23267 Roo.HtmlEditorCore.swapCodes   =[ 
23268     [    8211, "--" ], 
23269     [    8212, "--" ], 
23270     [    8216,  "'" ],  
23271     [    8217, "'" ],  
23272     [    8220, '"' ],  
23273     [    8221, '"' ],  
23274     [    8226, "*" ],  
23275     [    8230, "..." ]
23276 ]; 
23277
23278     /*
23279  * - LGPL
23280  *
23281  * HtmlEditor
23282  * 
23283  */
23284
23285 /**
23286  * @class Roo.bootstrap.HtmlEditor
23287  * @extends Roo.bootstrap.TextArea
23288  * Bootstrap HtmlEditor class
23289
23290  * @constructor
23291  * Create a new HtmlEditor
23292  * @param {Object} config The config object
23293  */
23294
23295 Roo.bootstrap.HtmlEditor = function(config){
23296     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23297     if (!this.toolbars) {
23298         this.toolbars = [];
23299     }
23300     
23301     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23302     this.addEvents({
23303             /**
23304              * @event initialize
23305              * Fires when the editor is fully initialized (including the iframe)
23306              * @param {HtmlEditor} this
23307              */
23308             initialize: true,
23309             /**
23310              * @event activate
23311              * Fires when the editor is first receives the focus. Any insertion must wait
23312              * until after this event.
23313              * @param {HtmlEditor} this
23314              */
23315             activate: true,
23316              /**
23317              * @event beforesync
23318              * Fires before the textarea is updated with content from the editor iframe. Return false
23319              * to cancel the sync.
23320              * @param {HtmlEditor} this
23321              * @param {String} html
23322              */
23323             beforesync: true,
23324              /**
23325              * @event beforepush
23326              * Fires before the iframe editor is updated with content from the textarea. Return false
23327              * to cancel the push.
23328              * @param {HtmlEditor} this
23329              * @param {String} html
23330              */
23331             beforepush: true,
23332              /**
23333              * @event sync
23334              * Fires when the textarea is updated with content from the editor iframe.
23335              * @param {HtmlEditor} this
23336              * @param {String} html
23337              */
23338             sync: true,
23339              /**
23340              * @event push
23341              * Fires when the iframe editor is updated with content from the textarea.
23342              * @param {HtmlEditor} this
23343              * @param {String} html
23344              */
23345             push: true,
23346              /**
23347              * @event editmodechange
23348              * Fires when the editor switches edit modes
23349              * @param {HtmlEditor} this
23350              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23351              */
23352             editmodechange: true,
23353             /**
23354              * @event editorevent
23355              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23356              * @param {HtmlEditor} this
23357              */
23358             editorevent: true,
23359             /**
23360              * @event firstfocus
23361              * Fires when on first focus - needed by toolbars..
23362              * @param {HtmlEditor} this
23363              */
23364             firstfocus: true,
23365             /**
23366              * @event autosave
23367              * Auto save the htmlEditor value as a file into Events
23368              * @param {HtmlEditor} this
23369              */
23370             autosave: true,
23371             /**
23372              * @event savedpreview
23373              * preview the saved version of htmlEditor
23374              * @param {HtmlEditor} this
23375              */
23376             savedpreview: true
23377         });
23378 };
23379
23380
23381 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23382     
23383     
23384       /**
23385      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23386      */
23387     toolbars : false,
23388     
23389      /**
23390     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23391     */
23392     btns : [],
23393    
23394      /**
23395      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23396      *                        Roo.resizable.
23397      */
23398     resizable : false,
23399      /**
23400      * @cfg {Number} height (in pixels)
23401      */   
23402     height: 300,
23403    /**
23404      * @cfg {Number} width (in pixels)
23405      */   
23406     width: false,
23407     
23408     /**
23409      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23410      * 
23411      */
23412     stylesheets: false,
23413     
23414     // id of frame..
23415     frameId: false,
23416     
23417     // private properties
23418     validationEvent : false,
23419     deferHeight: true,
23420     initialized : false,
23421     activated : false,
23422     
23423     onFocus : Roo.emptyFn,
23424     iframePad:3,
23425     hideMode:'offsets',
23426     
23427     tbContainer : false,
23428     
23429     bodyCls : '',
23430     
23431     toolbarContainer :function() {
23432         return this.wrap.select('.x-html-editor-tb',true).first();
23433     },
23434
23435     /**
23436      * Protected method that will not generally be called directly. It
23437      * is called when the editor creates its toolbar. Override this method if you need to
23438      * add custom toolbar buttons.
23439      * @param {HtmlEditor} editor
23440      */
23441     createToolbar : function(){
23442         Roo.log('renewing');
23443         Roo.log("create toolbars");
23444         
23445         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23446         this.toolbars[0].render(this.toolbarContainer());
23447         
23448         return;
23449         
23450 //        if (!editor.toolbars || !editor.toolbars.length) {
23451 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23452 //        }
23453 //        
23454 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23455 //            editor.toolbars[i] = Roo.factory(
23456 //                    typeof(editor.toolbars[i]) == 'string' ?
23457 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23458 //                Roo.bootstrap.HtmlEditor);
23459 //            editor.toolbars[i].init(editor);
23460 //        }
23461     },
23462
23463      
23464     // private
23465     onRender : function(ct, position)
23466     {
23467        // Roo.log("Call onRender: " + this.xtype);
23468         var _t = this;
23469         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23470       
23471         this.wrap = this.inputEl().wrap({
23472             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23473         });
23474         
23475         this.editorcore.onRender(ct, position);
23476          
23477         if (this.resizable) {
23478             this.resizeEl = new Roo.Resizable(this.wrap, {
23479                 pinned : true,
23480                 wrap: true,
23481                 dynamic : true,
23482                 minHeight : this.height,
23483                 height: this.height,
23484                 handles : this.resizable,
23485                 width: this.width,
23486                 listeners : {
23487                     resize : function(r, w, h) {
23488                         _t.onResize(w,h); // -something
23489                     }
23490                 }
23491             });
23492             
23493         }
23494         this.createToolbar(this);
23495        
23496         
23497         if(!this.width && this.resizable){
23498             this.setSize(this.wrap.getSize());
23499         }
23500         if (this.resizeEl) {
23501             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23502             // should trigger onReize..
23503         }
23504         
23505     },
23506
23507     // private
23508     onResize : function(w, h)
23509     {
23510         Roo.log('resize: ' +w + ',' + h );
23511         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23512         var ew = false;
23513         var eh = false;
23514         
23515         if(this.inputEl() ){
23516             if(typeof w == 'number'){
23517                 var aw = w - this.wrap.getFrameWidth('lr');
23518                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23519                 ew = aw;
23520             }
23521             if(typeof h == 'number'){
23522                  var tbh = -11;  // fixme it needs to tool bar size!
23523                 for (var i =0; i < this.toolbars.length;i++) {
23524                     // fixme - ask toolbars for heights?
23525                     tbh += this.toolbars[i].el.getHeight();
23526                     //if (this.toolbars[i].footer) {
23527                     //    tbh += this.toolbars[i].footer.el.getHeight();
23528                     //}
23529                 }
23530               
23531                 
23532                 
23533                 
23534                 
23535                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23536                 ah -= 5; // knock a few pixes off for look..
23537                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23538                 var eh = ah;
23539             }
23540         }
23541         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23542         this.editorcore.onResize(ew,eh);
23543         
23544     },
23545
23546     /**
23547      * Toggles the editor between standard and source edit mode.
23548      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23549      */
23550     toggleSourceEdit : function(sourceEditMode)
23551     {
23552         this.editorcore.toggleSourceEdit(sourceEditMode);
23553         
23554         if(this.editorcore.sourceEditMode){
23555             Roo.log('editor - showing textarea');
23556             
23557 //            Roo.log('in');
23558 //            Roo.log(this.syncValue());
23559             this.syncValue();
23560             this.inputEl().removeClass(['hide', 'x-hidden']);
23561             this.inputEl().dom.removeAttribute('tabIndex');
23562             this.inputEl().focus();
23563         }else{
23564             Roo.log('editor - hiding textarea');
23565 //            Roo.log('out')
23566 //            Roo.log(this.pushValue()); 
23567             this.pushValue();
23568             
23569             this.inputEl().addClass(['hide', 'x-hidden']);
23570             this.inputEl().dom.setAttribute('tabIndex', -1);
23571             //this.deferFocus();
23572         }
23573          
23574         if(this.resizable){
23575             this.setSize(this.wrap.getSize());
23576         }
23577         
23578         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23579     },
23580  
23581     // private (for BoxComponent)
23582     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23583
23584     // private (for BoxComponent)
23585     getResizeEl : function(){
23586         return this.wrap;
23587     },
23588
23589     // private (for BoxComponent)
23590     getPositionEl : function(){
23591         return this.wrap;
23592     },
23593
23594     // private
23595     initEvents : function(){
23596         this.originalValue = this.getValue();
23597     },
23598
23599 //    /**
23600 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23601 //     * @method
23602 //     */
23603 //    markInvalid : Roo.emptyFn,
23604 //    /**
23605 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23606 //     * @method
23607 //     */
23608 //    clearInvalid : Roo.emptyFn,
23609
23610     setValue : function(v){
23611         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23612         this.editorcore.pushValue();
23613     },
23614
23615      
23616     // private
23617     deferFocus : function(){
23618         this.focus.defer(10, this);
23619     },
23620
23621     // doc'ed in Field
23622     focus : function(){
23623         this.editorcore.focus();
23624         
23625     },
23626       
23627
23628     // private
23629     onDestroy : function(){
23630         
23631         
23632         
23633         if(this.rendered){
23634             
23635             for (var i =0; i < this.toolbars.length;i++) {
23636                 // fixme - ask toolbars for heights?
23637                 this.toolbars[i].onDestroy();
23638             }
23639             
23640             this.wrap.dom.innerHTML = '';
23641             this.wrap.remove();
23642         }
23643     },
23644
23645     // private
23646     onFirstFocus : function(){
23647         //Roo.log("onFirstFocus");
23648         this.editorcore.onFirstFocus();
23649          for (var i =0; i < this.toolbars.length;i++) {
23650             this.toolbars[i].onFirstFocus();
23651         }
23652         
23653     },
23654     
23655     // private
23656     syncValue : function()
23657     {   
23658         this.editorcore.syncValue();
23659     },
23660     
23661     pushValue : function()
23662     {   
23663         this.editorcore.pushValue();
23664     }
23665      
23666     
23667     // hide stuff that is not compatible
23668     /**
23669      * @event blur
23670      * @hide
23671      */
23672     /**
23673      * @event change
23674      * @hide
23675      */
23676     /**
23677      * @event focus
23678      * @hide
23679      */
23680     /**
23681      * @event specialkey
23682      * @hide
23683      */
23684     /**
23685      * @cfg {String} fieldClass @hide
23686      */
23687     /**
23688      * @cfg {String} focusClass @hide
23689      */
23690     /**
23691      * @cfg {String} autoCreate @hide
23692      */
23693     /**
23694      * @cfg {String} inputType @hide
23695      */
23696     /**
23697      * @cfg {String} invalidClass @hide
23698      */
23699     /**
23700      * @cfg {String} invalidText @hide
23701      */
23702     /**
23703      * @cfg {String} msgFx @hide
23704      */
23705     /**
23706      * @cfg {String} validateOnBlur @hide
23707      */
23708 });
23709  
23710     
23711    
23712    
23713    
23714       
23715 Roo.namespace('Roo.bootstrap.htmleditor');
23716 /**
23717  * @class Roo.bootstrap.HtmlEditorToolbar1
23718  * Basic Toolbar
23719  * 
23720  * Usage:
23721  *
23722  new Roo.bootstrap.HtmlEditor({
23723     ....
23724     toolbars : [
23725         new Roo.bootstrap.HtmlEditorToolbar1({
23726             disable : { fonts: 1 , format: 1, ..., ... , ...],
23727             btns : [ .... ]
23728         })
23729     }
23730      
23731  * 
23732  * @cfg {Object} disable List of elements to disable..
23733  * @cfg {Array} btns List of additional buttons.
23734  * 
23735  * 
23736  * NEEDS Extra CSS? 
23737  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23738  */
23739  
23740 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23741 {
23742     
23743     Roo.apply(this, config);
23744     
23745     // default disabled, based on 'good practice'..
23746     this.disable = this.disable || {};
23747     Roo.applyIf(this.disable, {
23748         fontSize : true,
23749         colors : true,
23750         specialElements : true
23751     });
23752     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23753     
23754     this.editor = config.editor;
23755     this.editorcore = config.editor.editorcore;
23756     
23757     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23758     
23759     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23760     // dont call parent... till later.
23761 }
23762 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23763      
23764     bar : true,
23765     
23766     editor : false,
23767     editorcore : false,
23768     
23769     
23770     formats : [
23771         "p" ,  
23772         "h1","h2","h3","h4","h5","h6", 
23773         "pre", "code", 
23774         "abbr", "acronym", "address", "cite", "samp", "var",
23775         'div','span'
23776     ],
23777     
23778     onRender : function(ct, position)
23779     {
23780        // Roo.log("Call onRender: " + this.xtype);
23781         
23782        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23783        Roo.log(this.el);
23784        this.el.dom.style.marginBottom = '0';
23785        var _this = this;
23786        var editorcore = this.editorcore;
23787        var editor= this.editor;
23788        
23789        var children = [];
23790        var btn = function(id,cmd , toggle, handler, html){
23791        
23792             var  event = toggle ? 'toggle' : 'click';
23793        
23794             var a = {
23795                 size : 'sm',
23796                 xtype: 'Button',
23797                 xns: Roo.bootstrap,
23798                 glyphicon : id,
23799                 cmd : id || cmd,
23800                 enableToggle:toggle !== false,
23801                 html : html || '',
23802                 pressed : toggle ? false : null,
23803                 listeners : {}
23804             };
23805             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23806                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23807             };
23808             children.push(a);
23809             return a;
23810        }
23811        
23812     //    var cb_box = function...
23813         
23814         var style = {
23815                 xtype: 'Button',
23816                 size : 'sm',
23817                 xns: Roo.bootstrap,
23818                 glyphicon : 'font',
23819                 //html : 'submit'
23820                 menu : {
23821                     xtype: 'Menu',
23822                     xns: Roo.bootstrap,
23823                     items:  []
23824                 }
23825         };
23826         Roo.each(this.formats, function(f) {
23827             style.menu.items.push({
23828                 xtype :'MenuItem',
23829                 xns: Roo.bootstrap,
23830                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23831                 tagname : f,
23832                 listeners : {
23833                     click : function()
23834                     {
23835                         editorcore.insertTag(this.tagname);
23836                         editor.focus();
23837                     }
23838                 }
23839                 
23840             });
23841         });
23842         children.push(style);   
23843         
23844         btn('bold',false,true);
23845         btn('italic',false,true);
23846         btn('align-left', 'justifyleft',true);
23847         btn('align-center', 'justifycenter',true);
23848         btn('align-right' , 'justifyright',true);
23849         btn('link', false, false, function(btn) {
23850             //Roo.log("create link?");
23851             var url = prompt(this.createLinkText, this.defaultLinkValue);
23852             if(url && url != 'http:/'+'/'){
23853                 this.editorcore.relayCmd('createlink', url);
23854             }
23855         }),
23856         btn('list','insertunorderedlist',true);
23857         btn('pencil', false,true, function(btn){
23858                 Roo.log(this);
23859                 this.toggleSourceEdit(btn.pressed);
23860         });
23861         
23862         if (this.editor.btns.length > 0) {
23863             for (var i = 0; i<this.editor.btns.length; i++) {
23864                 children.push(this.editor.btns[i]);
23865             }
23866         }
23867         
23868         /*
23869         var cog = {
23870                 xtype: 'Button',
23871                 size : 'sm',
23872                 xns: Roo.bootstrap,
23873                 glyphicon : 'cog',
23874                 //html : 'submit'
23875                 menu : {
23876                     xtype: 'Menu',
23877                     xns: Roo.bootstrap,
23878                     items:  []
23879                 }
23880         };
23881         
23882         cog.menu.items.push({
23883             xtype :'MenuItem',
23884             xns: Roo.bootstrap,
23885             html : Clean styles,
23886             tagname : f,
23887             listeners : {
23888                 click : function()
23889                 {
23890                     editorcore.insertTag(this.tagname);
23891                     editor.focus();
23892                 }
23893             }
23894             
23895         });
23896        */
23897         
23898          
23899        this.xtype = 'NavSimplebar';
23900         
23901         for(var i=0;i< children.length;i++) {
23902             
23903             this.buttons.add(this.addxtypeChild(children[i]));
23904             
23905         }
23906         
23907         editor.on('editorevent', this.updateToolbar, this);
23908     },
23909     onBtnClick : function(id)
23910     {
23911        this.editorcore.relayCmd(id);
23912        this.editorcore.focus();
23913     },
23914     
23915     /**
23916      * Protected method that will not generally be called directly. It triggers
23917      * a toolbar update by reading the markup state of the current selection in the editor.
23918      */
23919     updateToolbar: function(){
23920
23921         if(!this.editorcore.activated){
23922             this.editor.onFirstFocus(); // is this neeed?
23923             return;
23924         }
23925
23926         var btns = this.buttons; 
23927         var doc = this.editorcore.doc;
23928         btns.get('bold').setActive(doc.queryCommandState('bold'));
23929         btns.get('italic').setActive(doc.queryCommandState('italic'));
23930         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23931         
23932         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23933         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23934         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23935         
23936         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23937         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23938          /*
23939         
23940         var ans = this.editorcore.getAllAncestors();
23941         if (this.formatCombo) {
23942             
23943             
23944             var store = this.formatCombo.store;
23945             this.formatCombo.setValue("");
23946             for (var i =0; i < ans.length;i++) {
23947                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23948                     // select it..
23949                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23950                     break;
23951                 }
23952             }
23953         }
23954         
23955         
23956         
23957         // hides menus... - so this cant be on a menu...
23958         Roo.bootstrap.MenuMgr.hideAll();
23959         */
23960         Roo.bootstrap.MenuMgr.hideAll();
23961         //this.editorsyncValue();
23962     },
23963     onFirstFocus: function() {
23964         this.buttons.each(function(item){
23965            item.enable();
23966         });
23967     },
23968     toggleSourceEdit : function(sourceEditMode){
23969         
23970           
23971         if(sourceEditMode){
23972             Roo.log("disabling buttons");
23973            this.buttons.each( function(item){
23974                 if(item.cmd != 'pencil'){
23975                     item.disable();
23976                 }
23977             });
23978           
23979         }else{
23980             Roo.log("enabling buttons");
23981             if(this.editorcore.initialized){
23982                 this.buttons.each( function(item){
23983                     item.enable();
23984                 });
23985             }
23986             
23987         }
23988         Roo.log("calling toggole on editor");
23989         // tell the editor that it's been pressed..
23990         this.editor.toggleSourceEdit(sourceEditMode);
23991        
23992     }
23993 });
23994
23995
23996
23997
23998
23999 /**
24000  * @class Roo.bootstrap.Table.AbstractSelectionModel
24001  * @extends Roo.util.Observable
24002  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24003  * implemented by descendant classes.  This class should not be directly instantiated.
24004  * @constructor
24005  */
24006 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24007     this.locked = false;
24008     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24009 };
24010
24011
24012 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24013     /** @ignore Called by the grid automatically. Do not call directly. */
24014     init : function(grid){
24015         this.grid = grid;
24016         this.initEvents();
24017     },
24018
24019     /**
24020      * Locks the selections.
24021      */
24022     lock : function(){
24023         this.locked = true;
24024     },
24025
24026     /**
24027      * Unlocks the selections.
24028      */
24029     unlock : function(){
24030         this.locked = false;
24031     },
24032
24033     /**
24034      * Returns true if the selections are locked.
24035      * @return {Boolean}
24036      */
24037     isLocked : function(){
24038         return this.locked;
24039     }
24040 });
24041 /**
24042  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24043  * @class Roo.bootstrap.Table.RowSelectionModel
24044  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24045  * It supports multiple selections and keyboard selection/navigation. 
24046  * @constructor
24047  * @param {Object} config
24048  */
24049
24050 Roo.bootstrap.Table.RowSelectionModel = function(config){
24051     Roo.apply(this, config);
24052     this.selections = new Roo.util.MixedCollection(false, function(o){
24053         return o.id;
24054     });
24055
24056     this.last = false;
24057     this.lastActive = false;
24058
24059     this.addEvents({
24060         /**
24061              * @event selectionchange
24062              * Fires when the selection changes
24063              * @param {SelectionModel} this
24064              */
24065             "selectionchange" : true,
24066         /**
24067              * @event afterselectionchange
24068              * Fires after the selection changes (eg. by key press or clicking)
24069              * @param {SelectionModel} this
24070              */
24071             "afterselectionchange" : true,
24072         /**
24073              * @event beforerowselect
24074              * Fires when a row is selected being selected, return false to cancel.
24075              * @param {SelectionModel} this
24076              * @param {Number} rowIndex The selected index
24077              * @param {Boolean} keepExisting False if other selections will be cleared
24078              */
24079             "beforerowselect" : true,
24080         /**
24081              * @event rowselect
24082              * Fires when a row is selected.
24083              * @param {SelectionModel} this
24084              * @param {Number} rowIndex The selected index
24085              * @param {Roo.data.Record} r The record
24086              */
24087             "rowselect" : true,
24088         /**
24089              * @event rowdeselect
24090              * Fires when a row is deselected.
24091              * @param {SelectionModel} this
24092              * @param {Number} rowIndex The selected index
24093              */
24094         "rowdeselect" : true
24095     });
24096     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24097     this.locked = false;
24098  };
24099
24100 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24101     /**
24102      * @cfg {Boolean} singleSelect
24103      * True to allow selection of only one row at a time (defaults to false)
24104      */
24105     singleSelect : false,
24106
24107     // private
24108     initEvents : function()
24109     {
24110
24111         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24112         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24113         //}else{ // allow click to work like normal
24114          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24115         //}
24116         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24117         this.grid.on("rowclick", this.handleMouseDown, this);
24118         
24119         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24120             "up" : function(e){
24121                 if(!e.shiftKey){
24122                     this.selectPrevious(e.shiftKey);
24123                 }else if(this.last !== false && this.lastActive !== false){
24124                     var last = this.last;
24125                     this.selectRange(this.last,  this.lastActive-1);
24126                     this.grid.getView().focusRow(this.lastActive);
24127                     if(last !== false){
24128                         this.last = last;
24129                     }
24130                 }else{
24131                     this.selectFirstRow();
24132                 }
24133                 this.fireEvent("afterselectionchange", this);
24134             },
24135             "down" : function(e){
24136                 if(!e.shiftKey){
24137                     this.selectNext(e.shiftKey);
24138                 }else if(this.last !== false && this.lastActive !== false){
24139                     var last = this.last;
24140                     this.selectRange(this.last,  this.lastActive+1);
24141                     this.grid.getView().focusRow(this.lastActive);
24142                     if(last !== false){
24143                         this.last = last;
24144                     }
24145                 }else{
24146                     this.selectFirstRow();
24147                 }
24148                 this.fireEvent("afterselectionchange", this);
24149             },
24150             scope: this
24151         });
24152         this.grid.store.on('load', function(){
24153             this.selections.clear();
24154         },this);
24155         /*
24156         var view = this.grid.view;
24157         view.on("refresh", this.onRefresh, this);
24158         view.on("rowupdated", this.onRowUpdated, this);
24159         view.on("rowremoved", this.onRemove, this);
24160         */
24161     },
24162
24163     // private
24164     onRefresh : function()
24165     {
24166         var ds = this.grid.store, i, v = this.grid.view;
24167         var s = this.selections;
24168         s.each(function(r){
24169             if((i = ds.indexOfId(r.id)) != -1){
24170                 v.onRowSelect(i);
24171             }else{
24172                 s.remove(r);
24173             }
24174         });
24175     },
24176
24177     // private
24178     onRemove : function(v, index, r){
24179         this.selections.remove(r);
24180     },
24181
24182     // private
24183     onRowUpdated : function(v, index, r){
24184         if(this.isSelected(r)){
24185             v.onRowSelect(index);
24186         }
24187     },
24188
24189     /**
24190      * Select records.
24191      * @param {Array} records The records to select
24192      * @param {Boolean} keepExisting (optional) True to keep existing selections
24193      */
24194     selectRecords : function(records, keepExisting)
24195     {
24196         if(!keepExisting){
24197             this.clearSelections();
24198         }
24199             var ds = this.grid.store;
24200         for(var i = 0, len = records.length; i < len; i++){
24201             this.selectRow(ds.indexOf(records[i]), true);
24202         }
24203     },
24204
24205     /**
24206      * Gets the number of selected rows.
24207      * @return {Number}
24208      */
24209     getCount : function(){
24210         return this.selections.length;
24211     },
24212
24213     /**
24214      * Selects the first row in the grid.
24215      */
24216     selectFirstRow : function(){
24217         this.selectRow(0);
24218     },
24219
24220     /**
24221      * Select the last row.
24222      * @param {Boolean} keepExisting (optional) True to keep existing selections
24223      */
24224     selectLastRow : function(keepExisting){
24225         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24226         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24227     },
24228
24229     /**
24230      * Selects the row immediately following the last selected row.
24231      * @param {Boolean} keepExisting (optional) True to keep existing selections
24232      */
24233     selectNext : function(keepExisting)
24234     {
24235             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24236             this.selectRow(this.last+1, keepExisting);
24237             this.grid.getView().focusRow(this.last);
24238         }
24239     },
24240
24241     /**
24242      * Selects the row that precedes the last selected row.
24243      * @param {Boolean} keepExisting (optional) True to keep existing selections
24244      */
24245     selectPrevious : function(keepExisting){
24246         if(this.last){
24247             this.selectRow(this.last-1, keepExisting);
24248             this.grid.getView().focusRow(this.last);
24249         }
24250     },
24251
24252     /**
24253      * Returns the selected records
24254      * @return {Array} Array of selected records
24255      */
24256     getSelections : function(){
24257         return [].concat(this.selections.items);
24258     },
24259
24260     /**
24261      * Returns the first selected record.
24262      * @return {Record}
24263      */
24264     getSelected : function(){
24265         return this.selections.itemAt(0);
24266     },
24267
24268
24269     /**
24270      * Clears all selections.
24271      */
24272     clearSelections : function(fast)
24273     {
24274         if(this.locked) {
24275             return;
24276         }
24277         if(fast !== true){
24278                 var ds = this.grid.store;
24279             var s = this.selections;
24280             s.each(function(r){
24281                 this.deselectRow(ds.indexOfId(r.id));
24282             }, this);
24283             s.clear();
24284         }else{
24285             this.selections.clear();
24286         }
24287         this.last = false;
24288     },
24289
24290
24291     /**
24292      * Selects all rows.
24293      */
24294     selectAll : function(){
24295         if(this.locked) {
24296             return;
24297         }
24298         this.selections.clear();
24299         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24300             this.selectRow(i, true);
24301         }
24302     },
24303
24304     /**
24305      * Returns True if there is a selection.
24306      * @return {Boolean}
24307      */
24308     hasSelection : function(){
24309         return this.selections.length > 0;
24310     },
24311
24312     /**
24313      * Returns True if the specified row is selected.
24314      * @param {Number/Record} record The record or index of the record to check
24315      * @return {Boolean}
24316      */
24317     isSelected : function(index){
24318             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24319         return (r && this.selections.key(r.id) ? true : false);
24320     },
24321
24322     /**
24323      * Returns True if the specified record id is selected.
24324      * @param {String} id The id of record to check
24325      * @return {Boolean}
24326      */
24327     isIdSelected : function(id){
24328         return (this.selections.key(id) ? true : false);
24329     },
24330
24331
24332     // private
24333     handleMouseDBClick : function(e, t){
24334         
24335     },
24336     // private
24337     handleMouseDown : function(e, t)
24338     {
24339             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24340         if(this.isLocked() || rowIndex < 0 ){
24341             return;
24342         };
24343         if(e.shiftKey && this.last !== false){
24344             var last = this.last;
24345             this.selectRange(last, rowIndex, e.ctrlKey);
24346             this.last = last; // reset the last
24347             t.focus();
24348     
24349         }else{
24350             var isSelected = this.isSelected(rowIndex);
24351             //Roo.log("select row:" + rowIndex);
24352             if(isSelected){
24353                 this.deselectRow(rowIndex);
24354             } else {
24355                         this.selectRow(rowIndex, true);
24356             }
24357     
24358             /*
24359                 if(e.button !== 0 && isSelected){
24360                 alert('rowIndex 2: ' + rowIndex);
24361                     view.focusRow(rowIndex);
24362                 }else if(e.ctrlKey && isSelected){
24363                     this.deselectRow(rowIndex);
24364                 }else if(!isSelected){
24365                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24366                     view.focusRow(rowIndex);
24367                 }
24368             */
24369         }
24370         this.fireEvent("afterselectionchange", this);
24371     },
24372     // private
24373     handleDragableRowClick :  function(grid, rowIndex, e) 
24374     {
24375         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24376             this.selectRow(rowIndex, false);
24377             grid.view.focusRow(rowIndex);
24378              this.fireEvent("afterselectionchange", this);
24379         }
24380     },
24381     
24382     /**
24383      * Selects multiple rows.
24384      * @param {Array} rows Array of the indexes of the row to select
24385      * @param {Boolean} keepExisting (optional) True to keep existing selections
24386      */
24387     selectRows : function(rows, keepExisting){
24388         if(!keepExisting){
24389             this.clearSelections();
24390         }
24391         for(var i = 0, len = rows.length; i < len; i++){
24392             this.selectRow(rows[i], true);
24393         }
24394     },
24395
24396     /**
24397      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24398      * @param {Number} startRow The index of the first row in the range
24399      * @param {Number} endRow The index of the last row in the range
24400      * @param {Boolean} keepExisting (optional) True to retain existing selections
24401      */
24402     selectRange : function(startRow, endRow, keepExisting){
24403         if(this.locked) {
24404             return;
24405         }
24406         if(!keepExisting){
24407             this.clearSelections();
24408         }
24409         if(startRow <= endRow){
24410             for(var i = startRow; i <= endRow; i++){
24411                 this.selectRow(i, true);
24412             }
24413         }else{
24414             for(var i = startRow; i >= endRow; i--){
24415                 this.selectRow(i, true);
24416             }
24417         }
24418     },
24419
24420     /**
24421      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24422      * @param {Number} startRow The index of the first row in the range
24423      * @param {Number} endRow The index of the last row in the range
24424      */
24425     deselectRange : function(startRow, endRow, preventViewNotify){
24426         if(this.locked) {
24427             return;
24428         }
24429         for(var i = startRow; i <= endRow; i++){
24430             this.deselectRow(i, preventViewNotify);
24431         }
24432     },
24433
24434     /**
24435      * Selects a row.
24436      * @param {Number} row The index of the row to select
24437      * @param {Boolean} keepExisting (optional) True to keep existing selections
24438      */
24439     selectRow : function(index, keepExisting, preventViewNotify)
24440     {
24441             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24442             return;
24443         }
24444         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24445             if(!keepExisting || this.singleSelect){
24446                 this.clearSelections();
24447             }
24448             
24449             var r = this.grid.store.getAt(index);
24450             //console.log('selectRow - record id :' + r.id);
24451             
24452             this.selections.add(r);
24453             this.last = this.lastActive = index;
24454             if(!preventViewNotify){
24455                 var proxy = new Roo.Element(
24456                                 this.grid.getRowDom(index)
24457                 );
24458                 proxy.addClass('bg-info info');
24459             }
24460             this.fireEvent("rowselect", this, index, r);
24461             this.fireEvent("selectionchange", this);
24462         }
24463     },
24464
24465     /**
24466      * Deselects a row.
24467      * @param {Number} row The index of the row to deselect
24468      */
24469     deselectRow : function(index, preventViewNotify)
24470     {
24471         if(this.locked) {
24472             return;
24473         }
24474         if(this.last == index){
24475             this.last = false;
24476         }
24477         if(this.lastActive == index){
24478             this.lastActive = false;
24479         }
24480         
24481         var r = this.grid.store.getAt(index);
24482         if (!r) {
24483             return;
24484         }
24485         
24486         this.selections.remove(r);
24487         //.console.log('deselectRow - record id :' + r.id);
24488         if(!preventViewNotify){
24489         
24490             var proxy = new Roo.Element(
24491                 this.grid.getRowDom(index)
24492             );
24493             proxy.removeClass('bg-info info');
24494         }
24495         this.fireEvent("rowdeselect", this, index);
24496         this.fireEvent("selectionchange", this);
24497     },
24498
24499     // private
24500     restoreLast : function(){
24501         if(this._last){
24502             this.last = this._last;
24503         }
24504     },
24505
24506     // private
24507     acceptsNav : function(row, col, cm){
24508         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24509     },
24510
24511     // private
24512     onEditorKey : function(field, e){
24513         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24514         if(k == e.TAB){
24515             e.stopEvent();
24516             ed.completeEdit();
24517             if(e.shiftKey){
24518                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24519             }else{
24520                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24521             }
24522         }else if(k == e.ENTER && !e.ctrlKey){
24523             e.stopEvent();
24524             ed.completeEdit();
24525             if(e.shiftKey){
24526                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24527             }else{
24528                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24529             }
24530         }else if(k == e.ESC){
24531             ed.cancelEdit();
24532         }
24533         if(newCell){
24534             g.startEditing(newCell[0], newCell[1]);
24535         }
24536     }
24537 });
24538 /*
24539  * Based on:
24540  * Ext JS Library 1.1.1
24541  * Copyright(c) 2006-2007, Ext JS, LLC.
24542  *
24543  * Originally Released Under LGPL - original licence link has changed is not relivant.
24544  *
24545  * Fork - LGPL
24546  * <script type="text/javascript">
24547  */
24548  
24549 /**
24550  * @class Roo.bootstrap.PagingToolbar
24551  * @extends Roo.bootstrap.NavSimplebar
24552  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24553  * @constructor
24554  * Create a new PagingToolbar
24555  * @param {Object} config The config object
24556  * @param {Roo.data.Store} store
24557  */
24558 Roo.bootstrap.PagingToolbar = function(config)
24559 {
24560     // old args format still supported... - xtype is prefered..
24561         // created from xtype...
24562     
24563     this.ds = config.dataSource;
24564     
24565     if (config.store && !this.ds) {
24566         this.store= Roo.factory(config.store, Roo.data);
24567         this.ds = this.store;
24568         this.ds.xmodule = this.xmodule || false;
24569     }
24570     
24571     this.toolbarItems = [];
24572     if (config.items) {
24573         this.toolbarItems = config.items;
24574     }
24575     
24576     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24577     
24578     this.cursor = 0;
24579     
24580     if (this.ds) { 
24581         this.bind(this.ds);
24582     }
24583     
24584     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24585     
24586 };
24587
24588 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24589     /**
24590      * @cfg {Roo.data.Store} dataSource
24591      * The underlying data store providing the paged data
24592      */
24593     /**
24594      * @cfg {String/HTMLElement/Element} container
24595      * container The id or element that will contain the toolbar
24596      */
24597     /**
24598      * @cfg {Boolean} displayInfo
24599      * True to display the displayMsg (defaults to false)
24600      */
24601     /**
24602      * @cfg {Number} pageSize
24603      * The number of records to display per page (defaults to 20)
24604      */
24605     pageSize: 20,
24606     /**
24607      * @cfg {String} displayMsg
24608      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24609      */
24610     displayMsg : 'Displaying {0} - {1} of {2}',
24611     /**
24612      * @cfg {String} emptyMsg
24613      * The message to display when no records are found (defaults to "No data to display")
24614      */
24615     emptyMsg : 'No data to display',
24616     /**
24617      * Customizable piece of the default paging text (defaults to "Page")
24618      * @type String
24619      */
24620     beforePageText : "Page",
24621     /**
24622      * Customizable piece of the default paging text (defaults to "of %0")
24623      * @type String
24624      */
24625     afterPageText : "of {0}",
24626     /**
24627      * Customizable piece of the default paging text (defaults to "First Page")
24628      * @type String
24629      */
24630     firstText : "First Page",
24631     /**
24632      * Customizable piece of the default paging text (defaults to "Previous Page")
24633      * @type String
24634      */
24635     prevText : "Previous Page",
24636     /**
24637      * Customizable piece of the default paging text (defaults to "Next Page")
24638      * @type String
24639      */
24640     nextText : "Next Page",
24641     /**
24642      * Customizable piece of the default paging text (defaults to "Last Page")
24643      * @type String
24644      */
24645     lastText : "Last Page",
24646     /**
24647      * Customizable piece of the default paging text (defaults to "Refresh")
24648      * @type String
24649      */
24650     refreshText : "Refresh",
24651
24652     buttons : false,
24653     // private
24654     onRender : function(ct, position) 
24655     {
24656         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24657         this.navgroup.parentId = this.id;
24658         this.navgroup.onRender(this.el, null);
24659         // add the buttons to the navgroup
24660         
24661         if(this.displayInfo){
24662             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24663             this.displayEl = this.el.select('.x-paging-info', true).first();
24664 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24665 //            this.displayEl = navel.el.select('span',true).first();
24666         }
24667         
24668         var _this = this;
24669         
24670         if(this.buttons){
24671             Roo.each(_this.buttons, function(e){ // this might need to use render????
24672                Roo.factory(e).render(_this.el);
24673             });
24674         }
24675             
24676         Roo.each(_this.toolbarItems, function(e) {
24677             _this.navgroup.addItem(e);
24678         });
24679         
24680         
24681         this.first = this.navgroup.addItem({
24682             tooltip: this.firstText,
24683             cls: "prev",
24684             icon : 'fa fa-backward',
24685             disabled: true,
24686             preventDefault: true,
24687             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24688         });
24689         
24690         this.prev =  this.navgroup.addItem({
24691             tooltip: this.prevText,
24692             cls: "prev",
24693             icon : 'fa fa-step-backward',
24694             disabled: true,
24695             preventDefault: true,
24696             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24697         });
24698     //this.addSeparator();
24699         
24700         
24701         var field = this.navgroup.addItem( {
24702             tagtype : 'span',
24703             cls : 'x-paging-position',
24704             
24705             html : this.beforePageText  +
24706                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24707                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24708          } ); //?? escaped?
24709         
24710         this.field = field.el.select('input', true).first();
24711         this.field.on("keydown", this.onPagingKeydown, this);
24712         this.field.on("focus", function(){this.dom.select();});
24713     
24714     
24715         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24716         //this.field.setHeight(18);
24717         //this.addSeparator();
24718         this.next = this.navgroup.addItem({
24719             tooltip: this.nextText,
24720             cls: "next",
24721             html : ' <i class="fa fa-step-forward">',
24722             disabled: true,
24723             preventDefault: true,
24724             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24725         });
24726         this.last = this.navgroup.addItem({
24727             tooltip: this.lastText,
24728             icon : 'fa fa-forward',
24729             cls: "next",
24730             disabled: true,
24731             preventDefault: true,
24732             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24733         });
24734     //this.addSeparator();
24735         this.loading = this.navgroup.addItem({
24736             tooltip: this.refreshText,
24737             icon: 'fa fa-refresh',
24738             preventDefault: true,
24739             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24740         });
24741         
24742     },
24743
24744     // private
24745     updateInfo : function(){
24746         if(this.displayEl){
24747             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24748             var msg = count == 0 ?
24749                 this.emptyMsg :
24750                 String.format(
24751                     this.displayMsg,
24752                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24753                 );
24754             this.displayEl.update(msg);
24755         }
24756     },
24757
24758     // private
24759     onLoad : function(ds, r, o)
24760     {
24761         this.cursor = o.params.start ? o.params.start : 0;
24762         
24763         var d = this.getPageData(),
24764             ap = d.activePage,
24765             ps = d.pages;
24766         
24767         
24768         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24769         this.field.dom.value = ap;
24770         this.first.setDisabled(ap == 1);
24771         this.prev.setDisabled(ap == 1);
24772         this.next.setDisabled(ap == ps);
24773         this.last.setDisabled(ap == ps);
24774         this.loading.enable();
24775         this.updateInfo();
24776     },
24777
24778     // private
24779     getPageData : function(){
24780         var total = this.ds.getTotalCount();
24781         return {
24782             total : total,
24783             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24784             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24785         };
24786     },
24787
24788     // private
24789     onLoadError : function(){
24790         this.loading.enable();
24791     },
24792
24793     // private
24794     onPagingKeydown : function(e){
24795         var k = e.getKey();
24796         var d = this.getPageData();
24797         if(k == e.RETURN){
24798             var v = this.field.dom.value, pageNum;
24799             if(!v || isNaN(pageNum = parseInt(v, 10))){
24800                 this.field.dom.value = d.activePage;
24801                 return;
24802             }
24803             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24804             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24805             e.stopEvent();
24806         }
24807         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))
24808         {
24809           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24810           this.field.dom.value = pageNum;
24811           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24812           e.stopEvent();
24813         }
24814         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24815         {
24816           var v = this.field.dom.value, pageNum; 
24817           var increment = (e.shiftKey) ? 10 : 1;
24818           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24819                 increment *= -1;
24820           }
24821           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24822             this.field.dom.value = d.activePage;
24823             return;
24824           }
24825           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24826           {
24827             this.field.dom.value = parseInt(v, 10) + increment;
24828             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24829             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24830           }
24831           e.stopEvent();
24832         }
24833     },
24834
24835     // private
24836     beforeLoad : function(){
24837         if(this.loading){
24838             this.loading.disable();
24839         }
24840     },
24841
24842     // private
24843     onClick : function(which){
24844         
24845         var ds = this.ds;
24846         if (!ds) {
24847             return;
24848         }
24849         
24850         switch(which){
24851             case "first":
24852                 ds.load({params:{start: 0, limit: this.pageSize}});
24853             break;
24854             case "prev":
24855                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24856             break;
24857             case "next":
24858                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24859             break;
24860             case "last":
24861                 var total = ds.getTotalCount();
24862                 var extra = total % this.pageSize;
24863                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24864                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24865             break;
24866             case "refresh":
24867                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24868             break;
24869         }
24870     },
24871
24872     /**
24873      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24874      * @param {Roo.data.Store} store The data store to unbind
24875      */
24876     unbind : function(ds){
24877         ds.un("beforeload", this.beforeLoad, this);
24878         ds.un("load", this.onLoad, this);
24879         ds.un("loadexception", this.onLoadError, this);
24880         ds.un("remove", this.updateInfo, this);
24881         ds.un("add", this.updateInfo, this);
24882         this.ds = undefined;
24883     },
24884
24885     /**
24886      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24887      * @param {Roo.data.Store} store The data store to bind
24888      */
24889     bind : function(ds){
24890         ds.on("beforeload", this.beforeLoad, this);
24891         ds.on("load", this.onLoad, this);
24892         ds.on("loadexception", this.onLoadError, this);
24893         ds.on("remove", this.updateInfo, this);
24894         ds.on("add", this.updateInfo, this);
24895         this.ds = ds;
24896     }
24897 });/*
24898  * - LGPL
24899  *
24900  * element
24901  * 
24902  */
24903
24904 /**
24905  * @class Roo.bootstrap.MessageBar
24906  * @extends Roo.bootstrap.Component
24907  * Bootstrap MessageBar class
24908  * @cfg {String} html contents of the MessageBar
24909  * @cfg {String} weight (info | success | warning | danger) default info
24910  * @cfg {String} beforeClass insert the bar before the given class
24911  * @cfg {Boolean} closable (true | false) default false
24912  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24913  * 
24914  * @constructor
24915  * Create a new Element
24916  * @param {Object} config The config object
24917  */
24918
24919 Roo.bootstrap.MessageBar = function(config){
24920     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24921 };
24922
24923 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24924     
24925     html: '',
24926     weight: 'info',
24927     closable: false,
24928     fixed: false,
24929     beforeClass: 'bootstrap-sticky-wrap',
24930     
24931     getAutoCreate : function(){
24932         
24933         var cfg = {
24934             tag: 'div',
24935             cls: 'alert alert-dismissable alert-' + this.weight,
24936             cn: [
24937                 {
24938                     tag: 'span',
24939                     cls: 'message',
24940                     html: this.html || ''
24941                 }
24942             ]
24943         };
24944         
24945         if(this.fixed){
24946             cfg.cls += ' alert-messages-fixed';
24947         }
24948         
24949         if(this.closable){
24950             cfg.cn.push({
24951                 tag: 'button',
24952                 cls: 'close',
24953                 html: 'x'
24954             });
24955         }
24956         
24957         return cfg;
24958     },
24959     
24960     onRender : function(ct, position)
24961     {
24962         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24963         
24964         if(!this.el){
24965             var cfg = Roo.apply({},  this.getAutoCreate());
24966             cfg.id = Roo.id();
24967             
24968             if (this.cls) {
24969                 cfg.cls += ' ' + this.cls;
24970             }
24971             if (this.style) {
24972                 cfg.style = this.style;
24973             }
24974             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24975             
24976             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24977         }
24978         
24979         this.el.select('>button.close').on('click', this.hide, this);
24980         
24981     },
24982     
24983     show : function()
24984     {
24985         if (!this.rendered) {
24986             this.render();
24987         }
24988         
24989         this.el.show();
24990         
24991         this.fireEvent('show', this);
24992         
24993     },
24994     
24995     hide : function()
24996     {
24997         if (!this.rendered) {
24998             this.render();
24999         }
25000         
25001         this.el.hide();
25002         
25003         this.fireEvent('hide', this);
25004     },
25005     
25006     update : function()
25007     {
25008 //        var e = this.el.dom.firstChild;
25009 //        
25010 //        if(this.closable){
25011 //            e = e.nextSibling;
25012 //        }
25013 //        
25014 //        e.data = this.html || '';
25015
25016         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25017     }
25018    
25019 });
25020
25021  
25022
25023      /*
25024  * - LGPL
25025  *
25026  * Graph
25027  * 
25028  */
25029
25030
25031 /**
25032  * @class Roo.bootstrap.Graph
25033  * @extends Roo.bootstrap.Component
25034  * Bootstrap Graph class
25035 > Prameters
25036  -sm {number} sm 4
25037  -md {number} md 5
25038  @cfg {String} graphtype  bar | vbar | pie
25039  @cfg {number} g_x coodinator | centre x (pie)
25040  @cfg {number} g_y coodinator | centre y (pie)
25041  @cfg {number} g_r radius (pie)
25042  @cfg {number} g_height height of the chart (respected by all elements in the set)
25043  @cfg {number} g_width width of the chart (respected by all elements in the set)
25044  @cfg {Object} title The title of the chart
25045     
25046  -{Array}  values
25047  -opts (object) options for the chart 
25048      o {
25049      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25050      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25051      o vgutter (number)
25052      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.
25053      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25054      o to
25055      o stretch (boolean)
25056      o }
25057  -opts (object) options for the pie
25058      o{
25059      o cut
25060      o startAngle (number)
25061      o endAngle (number)
25062      } 
25063  *
25064  * @constructor
25065  * Create a new Input
25066  * @param {Object} config The config object
25067  */
25068
25069 Roo.bootstrap.Graph = function(config){
25070     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25071     
25072     this.addEvents({
25073         // img events
25074         /**
25075          * @event click
25076          * The img click event for the img.
25077          * @param {Roo.EventObject} e
25078          */
25079         "click" : true
25080     });
25081 };
25082
25083 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25084     
25085     sm: 4,
25086     md: 5,
25087     graphtype: 'bar',
25088     g_height: 250,
25089     g_width: 400,
25090     g_x: 50,
25091     g_y: 50,
25092     g_r: 30,
25093     opts:{
25094         //g_colors: this.colors,
25095         g_type: 'soft',
25096         g_gutter: '20%'
25097
25098     },
25099     title : false,
25100
25101     getAutoCreate : function(){
25102         
25103         var cfg = {
25104             tag: 'div',
25105             html : null
25106         };
25107         
25108         
25109         return  cfg;
25110     },
25111
25112     onRender : function(ct,position){
25113         
25114         
25115         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25116         
25117         if (typeof(Raphael) == 'undefined') {
25118             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25119             return;
25120         }
25121         
25122         this.raphael = Raphael(this.el.dom);
25123         
25124                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25125                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25126                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25127                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25128                 /*
25129                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25130                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25131                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25132                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25133                 
25134                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25135                 r.barchart(330, 10, 300, 220, data1);
25136                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25137                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25138                 */
25139                 
25140                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25141                 // r.barchart(30, 30, 560, 250,  xdata, {
25142                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25143                 //     axis : "0 0 1 1",
25144                 //     axisxlabels :  xdata
25145                 //     //yvalues : cols,
25146                    
25147                 // });
25148 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25149 //        
25150 //        this.load(null,xdata,{
25151 //                axis : "0 0 1 1",
25152 //                axisxlabels :  xdata
25153 //                });
25154
25155     },
25156
25157     load : function(graphtype,xdata,opts)
25158     {
25159         this.raphael.clear();
25160         if(!graphtype) {
25161             graphtype = this.graphtype;
25162         }
25163         if(!opts){
25164             opts = this.opts;
25165         }
25166         var r = this.raphael,
25167             fin = function () {
25168                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25169             },
25170             fout = function () {
25171                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25172             },
25173             pfin = function() {
25174                 this.sector.stop();
25175                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25176
25177                 if (this.label) {
25178                     this.label[0].stop();
25179                     this.label[0].attr({ r: 7.5 });
25180                     this.label[1].attr({ "font-weight": 800 });
25181                 }
25182             },
25183             pfout = function() {
25184                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25185
25186                 if (this.label) {
25187                     this.label[0].animate({ r: 5 }, 500, "bounce");
25188                     this.label[1].attr({ "font-weight": 400 });
25189                 }
25190             };
25191
25192         switch(graphtype){
25193             case 'bar':
25194                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25195                 break;
25196             case 'hbar':
25197                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25198                 break;
25199             case 'pie':
25200 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25201 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25202 //            
25203                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25204                 
25205                 break;
25206
25207         }
25208         
25209         if(this.title){
25210             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25211         }
25212         
25213     },
25214     
25215     setTitle: function(o)
25216     {
25217         this.title = o;
25218     },
25219     
25220     initEvents: function() {
25221         
25222         if(!this.href){
25223             this.el.on('click', this.onClick, this);
25224         }
25225     },
25226     
25227     onClick : function(e)
25228     {
25229         Roo.log('img onclick');
25230         this.fireEvent('click', this, e);
25231     }
25232    
25233 });
25234
25235  
25236 /*
25237  * - LGPL
25238  *
25239  * numberBox
25240  * 
25241  */
25242 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25243
25244 /**
25245  * @class Roo.bootstrap.dash.NumberBox
25246  * @extends Roo.bootstrap.Component
25247  * Bootstrap NumberBox class
25248  * @cfg {String} headline Box headline
25249  * @cfg {String} content Box content
25250  * @cfg {String} icon Box icon
25251  * @cfg {String} footer Footer text
25252  * @cfg {String} fhref Footer href
25253  * 
25254  * @constructor
25255  * Create a new NumberBox
25256  * @param {Object} config The config object
25257  */
25258
25259
25260 Roo.bootstrap.dash.NumberBox = function(config){
25261     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25262     
25263 };
25264
25265 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25266     
25267     headline : '',
25268     content : '',
25269     icon : '',
25270     footer : '',
25271     fhref : '',
25272     ficon : '',
25273     
25274     getAutoCreate : function(){
25275         
25276         var cfg = {
25277             tag : 'div',
25278             cls : 'small-box ',
25279             cn : [
25280                 {
25281                     tag : 'div',
25282                     cls : 'inner',
25283                     cn :[
25284                         {
25285                             tag : 'h3',
25286                             cls : 'roo-headline',
25287                             html : this.headline
25288                         },
25289                         {
25290                             tag : 'p',
25291                             cls : 'roo-content',
25292                             html : this.content
25293                         }
25294                     ]
25295                 }
25296             ]
25297         };
25298         
25299         if(this.icon){
25300             cfg.cn.push({
25301                 tag : 'div',
25302                 cls : 'icon',
25303                 cn :[
25304                     {
25305                         tag : 'i',
25306                         cls : 'ion ' + this.icon
25307                     }
25308                 ]
25309             });
25310         }
25311         
25312         if(this.footer){
25313             var footer = {
25314                 tag : 'a',
25315                 cls : 'small-box-footer',
25316                 href : this.fhref || '#',
25317                 html : this.footer
25318             };
25319             
25320             cfg.cn.push(footer);
25321             
25322         }
25323         
25324         return  cfg;
25325     },
25326
25327     onRender : function(ct,position){
25328         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25329
25330
25331        
25332                 
25333     },
25334
25335     setHeadline: function (value)
25336     {
25337         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25338     },
25339     
25340     setFooter: function (value, href)
25341     {
25342         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25343         
25344         if(href){
25345             this.el.select('a.small-box-footer',true).first().attr('href', href);
25346         }
25347         
25348     },
25349
25350     setContent: function (value)
25351     {
25352         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25353     },
25354
25355     initEvents: function() 
25356     {   
25357         
25358     }
25359     
25360 });
25361
25362  
25363 /*
25364  * - LGPL
25365  *
25366  * TabBox
25367  * 
25368  */
25369 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25370
25371 /**
25372  * @class Roo.bootstrap.dash.TabBox
25373  * @extends Roo.bootstrap.Component
25374  * Bootstrap TabBox class
25375  * @cfg {String} title Title of the TabBox
25376  * @cfg {String} icon Icon of the TabBox
25377  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25378  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25379  * 
25380  * @constructor
25381  * Create a new TabBox
25382  * @param {Object} config The config object
25383  */
25384
25385
25386 Roo.bootstrap.dash.TabBox = function(config){
25387     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25388     this.addEvents({
25389         // raw events
25390         /**
25391          * @event addpane
25392          * When a pane is added
25393          * @param {Roo.bootstrap.dash.TabPane} pane
25394          */
25395         "addpane" : true,
25396         /**
25397          * @event activatepane
25398          * When a pane is activated
25399          * @param {Roo.bootstrap.dash.TabPane} pane
25400          */
25401         "activatepane" : true
25402         
25403          
25404     });
25405     
25406     this.panes = [];
25407 };
25408
25409 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25410
25411     title : '',
25412     icon : false,
25413     showtabs : true,
25414     tabScrollable : false,
25415     
25416     getChildContainer : function()
25417     {
25418         return this.el.select('.tab-content', true).first();
25419     },
25420     
25421     getAutoCreate : function(){
25422         
25423         var header = {
25424             tag: 'li',
25425             cls: 'pull-left header',
25426             html: this.title,
25427             cn : []
25428         };
25429         
25430         if(this.icon){
25431             header.cn.push({
25432                 tag: 'i',
25433                 cls: 'fa ' + this.icon
25434             });
25435         }
25436         
25437         var h = {
25438             tag: 'ul',
25439             cls: 'nav nav-tabs pull-right',
25440             cn: [
25441                 header
25442             ]
25443         };
25444         
25445         if(this.tabScrollable){
25446             h = {
25447                 tag: 'div',
25448                 cls: 'tab-header',
25449                 cn: [
25450                     {
25451                         tag: 'ul',
25452                         cls: 'nav nav-tabs pull-right',
25453                         cn: [
25454                             header
25455                         ]
25456                     }
25457                 ]
25458             };
25459         }
25460         
25461         var cfg = {
25462             tag: 'div',
25463             cls: 'nav-tabs-custom',
25464             cn: [
25465                 h,
25466                 {
25467                     tag: 'div',
25468                     cls: 'tab-content no-padding',
25469                     cn: []
25470                 }
25471             ]
25472         };
25473
25474         return  cfg;
25475     },
25476     initEvents : function()
25477     {
25478         //Roo.log('add add pane handler');
25479         this.on('addpane', this.onAddPane, this);
25480     },
25481      /**
25482      * Updates the box title
25483      * @param {String} html to set the title to.
25484      */
25485     setTitle : function(value)
25486     {
25487         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25488     },
25489     onAddPane : function(pane)
25490     {
25491         this.panes.push(pane);
25492         //Roo.log('addpane');
25493         //Roo.log(pane);
25494         // tabs are rendere left to right..
25495         if(!this.showtabs){
25496             return;
25497         }
25498         
25499         var ctr = this.el.select('.nav-tabs', true).first();
25500          
25501          
25502         var existing = ctr.select('.nav-tab',true);
25503         var qty = existing.getCount();;
25504         
25505         
25506         var tab = ctr.createChild({
25507             tag : 'li',
25508             cls : 'nav-tab' + (qty ? '' : ' active'),
25509             cn : [
25510                 {
25511                     tag : 'a',
25512                     href:'#',
25513                     html : pane.title
25514                 }
25515             ]
25516         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25517         pane.tab = tab;
25518         
25519         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25520         if (!qty) {
25521             pane.el.addClass('active');
25522         }
25523         
25524                 
25525     },
25526     onTabClick : function(ev,un,ob,pane)
25527     {
25528         //Roo.log('tab - prev default');
25529         ev.preventDefault();
25530         
25531         
25532         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25533         pane.tab.addClass('active');
25534         //Roo.log(pane.title);
25535         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25536         // technically we should have a deactivate event.. but maybe add later.
25537         // and it should not de-activate the selected tab...
25538         this.fireEvent('activatepane', pane);
25539         pane.el.addClass('active');
25540         pane.fireEvent('activate');
25541         
25542         
25543     },
25544     
25545     getActivePane : function()
25546     {
25547         var r = false;
25548         Roo.each(this.panes, function(p) {
25549             if(p.el.hasClass('active')){
25550                 r = p;
25551                 return false;
25552             }
25553             
25554             return;
25555         });
25556         
25557         return r;
25558     }
25559     
25560     
25561 });
25562
25563  
25564 /*
25565  * - LGPL
25566  *
25567  * Tab pane
25568  * 
25569  */
25570 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25571 /**
25572  * @class Roo.bootstrap.TabPane
25573  * @extends Roo.bootstrap.Component
25574  * Bootstrap TabPane class
25575  * @cfg {Boolean} active (false | true) Default false
25576  * @cfg {String} title title of panel
25577
25578  * 
25579  * @constructor
25580  * Create a new TabPane
25581  * @param {Object} config The config object
25582  */
25583
25584 Roo.bootstrap.dash.TabPane = function(config){
25585     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25586     
25587     this.addEvents({
25588         // raw events
25589         /**
25590          * @event activate
25591          * When a pane is activated
25592          * @param {Roo.bootstrap.dash.TabPane} pane
25593          */
25594         "activate" : true
25595          
25596     });
25597 };
25598
25599 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25600     
25601     active : false,
25602     title : '',
25603     
25604     // the tabBox that this is attached to.
25605     tab : false,
25606      
25607     getAutoCreate : function() 
25608     {
25609         var cfg = {
25610             tag: 'div',
25611             cls: 'tab-pane'
25612         };
25613         
25614         if(this.active){
25615             cfg.cls += ' active';
25616         }
25617         
25618         return cfg;
25619     },
25620     initEvents  : function()
25621     {
25622         //Roo.log('trigger add pane handler');
25623         this.parent().fireEvent('addpane', this)
25624     },
25625     
25626      /**
25627      * Updates the tab title 
25628      * @param {String} html to set the title to.
25629      */
25630     setTitle: function(str)
25631     {
25632         if (!this.tab) {
25633             return;
25634         }
25635         this.title = str;
25636         this.tab.select('a', true).first().dom.innerHTML = str;
25637         
25638     }
25639     
25640     
25641     
25642 });
25643
25644  
25645
25646
25647  /*
25648  * - LGPL
25649  *
25650  * menu
25651  * 
25652  */
25653 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25654
25655 /**
25656  * @class Roo.bootstrap.menu.Menu
25657  * @extends Roo.bootstrap.Component
25658  * Bootstrap Menu class - container for Menu
25659  * @cfg {String} html Text of the menu
25660  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25661  * @cfg {String} icon Font awesome icon
25662  * @cfg {String} pos Menu align to (top | bottom) default bottom
25663  * 
25664  * 
25665  * @constructor
25666  * Create a new Menu
25667  * @param {Object} config The config object
25668  */
25669
25670
25671 Roo.bootstrap.menu.Menu = function(config){
25672     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25673     
25674     this.addEvents({
25675         /**
25676          * @event beforeshow
25677          * Fires before this menu is displayed
25678          * @param {Roo.bootstrap.menu.Menu} this
25679          */
25680         beforeshow : true,
25681         /**
25682          * @event beforehide
25683          * Fires before this menu is hidden
25684          * @param {Roo.bootstrap.menu.Menu} this
25685          */
25686         beforehide : true,
25687         /**
25688          * @event show
25689          * Fires after this menu is displayed
25690          * @param {Roo.bootstrap.menu.Menu} this
25691          */
25692         show : true,
25693         /**
25694          * @event hide
25695          * Fires after this menu is hidden
25696          * @param {Roo.bootstrap.menu.Menu} this
25697          */
25698         hide : true,
25699         /**
25700          * @event click
25701          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25702          * @param {Roo.bootstrap.menu.Menu} this
25703          * @param {Roo.EventObject} e
25704          */
25705         click : true
25706     });
25707     
25708 };
25709
25710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25711     
25712     submenu : false,
25713     html : '',
25714     weight : 'default',
25715     icon : false,
25716     pos : 'bottom',
25717     
25718     
25719     getChildContainer : function() {
25720         if(this.isSubMenu){
25721             return this.el;
25722         }
25723         
25724         return this.el.select('ul.dropdown-menu', true).first();  
25725     },
25726     
25727     getAutoCreate : function()
25728     {
25729         var text = [
25730             {
25731                 tag : 'span',
25732                 cls : 'roo-menu-text',
25733                 html : this.html
25734             }
25735         ];
25736         
25737         if(this.icon){
25738             text.unshift({
25739                 tag : 'i',
25740                 cls : 'fa ' + this.icon
25741             })
25742         }
25743         
25744         
25745         var cfg = {
25746             tag : 'div',
25747             cls : 'btn-group',
25748             cn : [
25749                 {
25750                     tag : 'button',
25751                     cls : 'dropdown-button btn btn-' + this.weight,
25752                     cn : text
25753                 },
25754                 {
25755                     tag : 'button',
25756                     cls : 'dropdown-toggle btn btn-' + this.weight,
25757                     cn : [
25758                         {
25759                             tag : 'span',
25760                             cls : 'caret'
25761                         }
25762                     ]
25763                 },
25764                 {
25765                     tag : 'ul',
25766                     cls : 'dropdown-menu'
25767                 }
25768             ]
25769             
25770         };
25771         
25772         if(this.pos == 'top'){
25773             cfg.cls += ' dropup';
25774         }
25775         
25776         if(this.isSubMenu){
25777             cfg = {
25778                 tag : 'ul',
25779                 cls : 'dropdown-menu'
25780             }
25781         }
25782         
25783         return cfg;
25784     },
25785     
25786     onRender : function(ct, position)
25787     {
25788         this.isSubMenu = ct.hasClass('dropdown-submenu');
25789         
25790         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25791     },
25792     
25793     initEvents : function() 
25794     {
25795         if(this.isSubMenu){
25796             return;
25797         }
25798         
25799         this.hidden = true;
25800         
25801         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25802         this.triggerEl.on('click', this.onTriggerPress, this);
25803         
25804         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25805         this.buttonEl.on('click', this.onClick, this);
25806         
25807     },
25808     
25809     list : function()
25810     {
25811         if(this.isSubMenu){
25812             return this.el;
25813         }
25814         
25815         return this.el.select('ul.dropdown-menu', true).first();
25816     },
25817     
25818     onClick : function(e)
25819     {
25820         this.fireEvent("click", this, e);
25821     },
25822     
25823     onTriggerPress  : function(e)
25824     {   
25825         if (this.isVisible()) {
25826             this.hide();
25827         } else {
25828             this.show();
25829         }
25830     },
25831     
25832     isVisible : function(){
25833         return !this.hidden;
25834     },
25835     
25836     show : function()
25837     {
25838         this.fireEvent("beforeshow", this);
25839         
25840         this.hidden = false;
25841         this.el.addClass('open');
25842         
25843         Roo.get(document).on("mouseup", this.onMouseUp, this);
25844         
25845         this.fireEvent("show", this);
25846         
25847         
25848     },
25849     
25850     hide : function()
25851     {
25852         this.fireEvent("beforehide", this);
25853         
25854         this.hidden = true;
25855         this.el.removeClass('open');
25856         
25857         Roo.get(document).un("mouseup", this.onMouseUp);
25858         
25859         this.fireEvent("hide", this);
25860     },
25861     
25862     onMouseUp : function()
25863     {
25864         this.hide();
25865     }
25866     
25867 });
25868
25869  
25870  /*
25871  * - LGPL
25872  *
25873  * menu item
25874  * 
25875  */
25876 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25877
25878 /**
25879  * @class Roo.bootstrap.menu.Item
25880  * @extends Roo.bootstrap.Component
25881  * Bootstrap MenuItem class
25882  * @cfg {Boolean} submenu (true | false) default false
25883  * @cfg {String} html text of the item
25884  * @cfg {String} href the link
25885  * @cfg {Boolean} disable (true | false) default false
25886  * @cfg {Boolean} preventDefault (true | false) default true
25887  * @cfg {String} icon Font awesome icon
25888  * @cfg {String} pos Submenu align to (left | right) default right 
25889  * 
25890  * 
25891  * @constructor
25892  * Create a new Item
25893  * @param {Object} config The config object
25894  */
25895
25896
25897 Roo.bootstrap.menu.Item = function(config){
25898     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25899     this.addEvents({
25900         /**
25901          * @event mouseover
25902          * Fires when the mouse is hovering over this menu
25903          * @param {Roo.bootstrap.menu.Item} this
25904          * @param {Roo.EventObject} e
25905          */
25906         mouseover : true,
25907         /**
25908          * @event mouseout
25909          * Fires when the mouse exits this menu
25910          * @param {Roo.bootstrap.menu.Item} this
25911          * @param {Roo.EventObject} e
25912          */
25913         mouseout : true,
25914         // raw events
25915         /**
25916          * @event click
25917          * The raw click event for the entire grid.
25918          * @param {Roo.EventObject} e
25919          */
25920         click : true
25921     });
25922 };
25923
25924 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25925     
25926     submenu : false,
25927     href : '',
25928     html : '',
25929     preventDefault: true,
25930     disable : false,
25931     icon : false,
25932     pos : 'right',
25933     
25934     getAutoCreate : function()
25935     {
25936         var text = [
25937             {
25938                 tag : 'span',
25939                 cls : 'roo-menu-item-text',
25940                 html : this.html
25941             }
25942         ];
25943         
25944         if(this.icon){
25945             text.unshift({
25946                 tag : 'i',
25947                 cls : 'fa ' + this.icon
25948             })
25949         }
25950         
25951         var cfg = {
25952             tag : 'li',
25953             cn : [
25954                 {
25955                     tag : 'a',
25956                     href : this.href || '#',
25957                     cn : text
25958                 }
25959             ]
25960         };
25961         
25962         if(this.disable){
25963             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25964         }
25965         
25966         if(this.submenu){
25967             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25968             
25969             if(this.pos == 'left'){
25970                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25971             }
25972         }
25973         
25974         return cfg;
25975     },
25976     
25977     initEvents : function() 
25978     {
25979         this.el.on('mouseover', this.onMouseOver, this);
25980         this.el.on('mouseout', this.onMouseOut, this);
25981         
25982         this.el.select('a', true).first().on('click', this.onClick, this);
25983         
25984     },
25985     
25986     onClick : function(e)
25987     {
25988         if(this.preventDefault){
25989             e.preventDefault();
25990         }
25991         
25992         this.fireEvent("click", this, e);
25993     },
25994     
25995     onMouseOver : function(e)
25996     {
25997         if(this.submenu && this.pos == 'left'){
25998             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25999         }
26000         
26001         this.fireEvent("mouseover", this, e);
26002     },
26003     
26004     onMouseOut : function(e)
26005     {
26006         this.fireEvent("mouseout", this, e);
26007     }
26008 });
26009
26010  
26011
26012  /*
26013  * - LGPL
26014  *
26015  * menu separator
26016  * 
26017  */
26018 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26019
26020 /**
26021  * @class Roo.bootstrap.menu.Separator
26022  * @extends Roo.bootstrap.Component
26023  * Bootstrap Separator class
26024  * 
26025  * @constructor
26026  * Create a new Separator
26027  * @param {Object} config The config object
26028  */
26029
26030
26031 Roo.bootstrap.menu.Separator = function(config){
26032     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26033 };
26034
26035 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26036     
26037     getAutoCreate : function(){
26038         var cfg = {
26039             tag : 'li',
26040             cls: 'divider'
26041         };
26042         
26043         return cfg;
26044     }
26045    
26046 });
26047
26048  
26049
26050  /*
26051  * - LGPL
26052  *
26053  * Tooltip
26054  * 
26055  */
26056
26057 /**
26058  * @class Roo.bootstrap.Tooltip
26059  * Bootstrap Tooltip class
26060  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26061  * to determine which dom element triggers the tooltip.
26062  * 
26063  * It needs to add support for additional attributes like tooltip-position
26064  * 
26065  * @constructor
26066  * Create a new Toolti
26067  * @param {Object} config The config object
26068  */
26069
26070 Roo.bootstrap.Tooltip = function(config){
26071     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26072     
26073     this.alignment = Roo.bootstrap.Tooltip.alignment;
26074     
26075     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26076         this.alignment = config.alignment;
26077     }
26078     
26079 };
26080
26081 Roo.apply(Roo.bootstrap.Tooltip, {
26082     /**
26083      * @function init initialize tooltip monitoring.
26084      * @static
26085      */
26086     currentEl : false,
26087     currentTip : false,
26088     currentRegion : false,
26089     
26090     //  init : delay?
26091     
26092     init : function()
26093     {
26094         Roo.get(document).on('mouseover', this.enter ,this);
26095         Roo.get(document).on('mouseout', this.leave, this);
26096          
26097         
26098         this.currentTip = new Roo.bootstrap.Tooltip();
26099     },
26100     
26101     enter : function(ev)
26102     {
26103         var dom = ev.getTarget();
26104         
26105         //Roo.log(['enter',dom]);
26106         var el = Roo.fly(dom);
26107         if (this.currentEl) {
26108             //Roo.log(dom);
26109             //Roo.log(this.currentEl);
26110             //Roo.log(this.currentEl.contains(dom));
26111             if (this.currentEl == el) {
26112                 return;
26113             }
26114             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26115                 return;
26116             }
26117
26118         }
26119         
26120         if (this.currentTip.el) {
26121             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26122         }    
26123         //Roo.log(ev);
26124         
26125         if(!el || el.dom == document){
26126             return;
26127         }
26128         
26129         var bindEl = el;
26130         
26131         // you can not look for children, as if el is the body.. then everythign is the child..
26132         if (!el.attr('tooltip')) { //
26133             if (!el.select("[tooltip]").elements.length) {
26134                 return;
26135             }
26136             // is the mouse over this child...?
26137             bindEl = el.select("[tooltip]").first();
26138             var xy = ev.getXY();
26139             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26140                 //Roo.log("not in region.");
26141                 return;
26142             }
26143             //Roo.log("child element over..");
26144             
26145         }
26146         this.currentEl = bindEl;
26147         this.currentTip.bind(bindEl);
26148         this.currentRegion = Roo.lib.Region.getRegion(dom);
26149         this.currentTip.enter();
26150         
26151     },
26152     leave : function(ev)
26153     {
26154         var dom = ev.getTarget();
26155         //Roo.log(['leave',dom]);
26156         if (!this.currentEl) {
26157             return;
26158         }
26159         
26160         
26161         if (dom != this.currentEl.dom) {
26162             return;
26163         }
26164         var xy = ev.getXY();
26165         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26166             return;
26167         }
26168         // only activate leave if mouse cursor is outside... bounding box..
26169         
26170         
26171         
26172         
26173         if (this.currentTip) {
26174             this.currentTip.leave();
26175         }
26176         //Roo.log('clear currentEl');
26177         this.currentEl = false;
26178         
26179         
26180     },
26181     alignment : {
26182         'left' : ['r-l', [-2,0], 'right'],
26183         'right' : ['l-r', [2,0], 'left'],
26184         'bottom' : ['t-b', [0,2], 'top'],
26185         'top' : [ 'b-t', [0,-2], 'bottom']
26186     }
26187     
26188 });
26189
26190
26191 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26192     
26193     
26194     bindEl : false,
26195     
26196     delay : null, // can be { show : 300 , hide: 500}
26197     
26198     timeout : null,
26199     
26200     hoverState : null, //???
26201     
26202     placement : 'bottom', 
26203     
26204     alignment : false,
26205     
26206     getAutoCreate : function(){
26207     
26208         var cfg = {
26209            cls : 'tooltip',
26210            role : 'tooltip',
26211            cn : [
26212                 {
26213                     cls : 'tooltip-arrow'
26214                 },
26215                 {
26216                     cls : 'tooltip-inner'
26217                 }
26218            ]
26219         };
26220         
26221         return cfg;
26222     },
26223     bind : function(el)
26224     {
26225         this.bindEl = el;
26226     },
26227       
26228     
26229     enter : function () {
26230        
26231         if (this.timeout != null) {
26232             clearTimeout(this.timeout);
26233         }
26234         
26235         this.hoverState = 'in';
26236          //Roo.log("enter - show");
26237         if (!this.delay || !this.delay.show) {
26238             this.show();
26239             return;
26240         }
26241         var _t = this;
26242         this.timeout = setTimeout(function () {
26243             if (_t.hoverState == 'in') {
26244                 _t.show();
26245             }
26246         }, this.delay.show);
26247     },
26248     leave : function()
26249     {
26250         clearTimeout(this.timeout);
26251     
26252         this.hoverState = 'out';
26253          if (!this.delay || !this.delay.hide) {
26254             this.hide();
26255             return;
26256         }
26257        
26258         var _t = this;
26259         this.timeout = setTimeout(function () {
26260             //Roo.log("leave - timeout");
26261             
26262             if (_t.hoverState == 'out') {
26263                 _t.hide();
26264                 Roo.bootstrap.Tooltip.currentEl = false;
26265             }
26266         }, delay);
26267     },
26268     
26269     show : function (msg)
26270     {
26271         if (!this.el) {
26272             this.render(document.body);
26273         }
26274         // set content.
26275         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26276         
26277         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26278         
26279         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26280         
26281         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26282         
26283         var placement = typeof this.placement == 'function' ?
26284             this.placement.call(this, this.el, on_el) :
26285             this.placement;
26286             
26287         var autoToken = /\s?auto?\s?/i;
26288         var autoPlace = autoToken.test(placement);
26289         if (autoPlace) {
26290             placement = placement.replace(autoToken, '') || 'top';
26291         }
26292         
26293         //this.el.detach()
26294         //this.el.setXY([0,0]);
26295         this.el.show();
26296         //this.el.dom.style.display='block';
26297         
26298         //this.el.appendTo(on_el);
26299         
26300         var p = this.getPosition();
26301         var box = this.el.getBox();
26302         
26303         if (autoPlace) {
26304             // fixme..
26305         }
26306         
26307         var align = this.alignment[placement];
26308         
26309         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26310         
26311         if(placement == 'top' || placement == 'bottom'){
26312             if(xy[0] < 0){
26313                 placement = 'right';
26314             }
26315             
26316             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26317                 placement = 'left';
26318             }
26319             
26320             var scroll = Roo.select('body', true).first().getScroll();
26321             
26322             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26323                 placement = 'top';
26324             }
26325             
26326             align = this.alignment[placement];
26327         }
26328         
26329         this.el.alignTo(this.bindEl, align[0],align[1]);
26330         //var arrow = this.el.select('.arrow',true).first();
26331         //arrow.set(align[2], 
26332         
26333         this.el.addClass(placement);
26334         
26335         this.el.addClass('in fade');
26336         
26337         this.hoverState = null;
26338         
26339         if (this.el.hasClass('fade')) {
26340             // fade it?
26341         }
26342         
26343     },
26344     hide : function()
26345     {
26346          
26347         if (!this.el) {
26348             return;
26349         }
26350         //this.el.setXY([0,0]);
26351         this.el.removeClass('in');
26352         //this.el.hide();
26353         
26354     }
26355     
26356 });
26357  
26358
26359  /*
26360  * - LGPL
26361  *
26362  * Location Picker
26363  * 
26364  */
26365
26366 /**
26367  * @class Roo.bootstrap.LocationPicker
26368  * @extends Roo.bootstrap.Component
26369  * Bootstrap LocationPicker class
26370  * @cfg {Number} latitude Position when init default 0
26371  * @cfg {Number} longitude Position when init default 0
26372  * @cfg {Number} zoom default 15
26373  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26374  * @cfg {Boolean} mapTypeControl default false
26375  * @cfg {Boolean} disableDoubleClickZoom default false
26376  * @cfg {Boolean} scrollwheel default true
26377  * @cfg {Boolean} streetViewControl default false
26378  * @cfg {Number} radius default 0
26379  * @cfg {String} locationName
26380  * @cfg {Boolean} draggable default true
26381  * @cfg {Boolean} enableAutocomplete default false
26382  * @cfg {Boolean} enableReverseGeocode default true
26383  * @cfg {String} markerTitle
26384  * 
26385  * @constructor
26386  * Create a new LocationPicker
26387  * @param {Object} config The config object
26388  */
26389
26390
26391 Roo.bootstrap.LocationPicker = function(config){
26392     
26393     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26394     
26395     this.addEvents({
26396         /**
26397          * @event initial
26398          * Fires when the picker initialized.
26399          * @param {Roo.bootstrap.LocationPicker} this
26400          * @param {Google Location} location
26401          */
26402         initial : true,
26403         /**
26404          * @event positionchanged
26405          * Fires when the picker position changed.
26406          * @param {Roo.bootstrap.LocationPicker} this
26407          * @param {Google Location} location
26408          */
26409         positionchanged : true,
26410         /**
26411          * @event resize
26412          * Fires when the map resize.
26413          * @param {Roo.bootstrap.LocationPicker} this
26414          */
26415         resize : true,
26416         /**
26417          * @event show
26418          * Fires when the map show.
26419          * @param {Roo.bootstrap.LocationPicker} this
26420          */
26421         show : true,
26422         /**
26423          * @event hide
26424          * Fires when the map hide.
26425          * @param {Roo.bootstrap.LocationPicker} this
26426          */
26427         hide : true,
26428         /**
26429          * @event mapClick
26430          * Fires when click the map.
26431          * @param {Roo.bootstrap.LocationPicker} this
26432          * @param {Map event} e
26433          */
26434         mapClick : true,
26435         /**
26436          * @event mapRightClick
26437          * Fires when right click the map.
26438          * @param {Roo.bootstrap.LocationPicker} this
26439          * @param {Map event} e
26440          */
26441         mapRightClick : true,
26442         /**
26443          * @event markerClick
26444          * Fires when click the marker.
26445          * @param {Roo.bootstrap.LocationPicker} this
26446          * @param {Map event} e
26447          */
26448         markerClick : true,
26449         /**
26450          * @event markerRightClick
26451          * Fires when right click the marker.
26452          * @param {Roo.bootstrap.LocationPicker} this
26453          * @param {Map event} e
26454          */
26455         markerRightClick : true,
26456         /**
26457          * @event OverlayViewDraw
26458          * Fires when OverlayView Draw
26459          * @param {Roo.bootstrap.LocationPicker} this
26460          */
26461         OverlayViewDraw : true,
26462         /**
26463          * @event OverlayViewOnAdd
26464          * Fires when OverlayView Draw
26465          * @param {Roo.bootstrap.LocationPicker} this
26466          */
26467         OverlayViewOnAdd : true,
26468         /**
26469          * @event OverlayViewOnRemove
26470          * Fires when OverlayView Draw
26471          * @param {Roo.bootstrap.LocationPicker} this
26472          */
26473         OverlayViewOnRemove : true,
26474         /**
26475          * @event OverlayViewShow
26476          * Fires when OverlayView Draw
26477          * @param {Roo.bootstrap.LocationPicker} this
26478          * @param {Pixel} cpx
26479          */
26480         OverlayViewShow : true,
26481         /**
26482          * @event OverlayViewHide
26483          * Fires when OverlayView Draw
26484          * @param {Roo.bootstrap.LocationPicker} this
26485          */
26486         OverlayViewHide : true,
26487         /**
26488          * @event loadexception
26489          * Fires when load google lib failed.
26490          * @param {Roo.bootstrap.LocationPicker} this
26491          */
26492         loadexception : true
26493     });
26494         
26495 };
26496
26497 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26498     
26499     gMapContext: false,
26500     
26501     latitude: 0,
26502     longitude: 0,
26503     zoom: 15,
26504     mapTypeId: false,
26505     mapTypeControl: false,
26506     disableDoubleClickZoom: false,
26507     scrollwheel: true,
26508     streetViewControl: false,
26509     radius: 0,
26510     locationName: '',
26511     draggable: true,
26512     enableAutocomplete: false,
26513     enableReverseGeocode: true,
26514     markerTitle: '',
26515     
26516     getAutoCreate: function()
26517     {
26518
26519         var cfg = {
26520             tag: 'div',
26521             cls: 'roo-location-picker'
26522         };
26523         
26524         return cfg
26525     },
26526     
26527     initEvents: function(ct, position)
26528     {       
26529         if(!this.el.getWidth() || this.isApplied()){
26530             return;
26531         }
26532         
26533         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26534         
26535         this.initial();
26536     },
26537     
26538     initial: function()
26539     {
26540         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26541             this.fireEvent('loadexception', this);
26542             return;
26543         }
26544         
26545         if(!this.mapTypeId){
26546             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26547         }
26548         
26549         this.gMapContext = this.GMapContext();
26550         
26551         this.initOverlayView();
26552         
26553         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26554         
26555         var _this = this;
26556                 
26557         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26558             _this.setPosition(_this.gMapContext.marker.position);
26559         });
26560         
26561         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26562             _this.fireEvent('mapClick', this, event);
26563             
26564         });
26565
26566         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26567             _this.fireEvent('mapRightClick', this, event);
26568             
26569         });
26570         
26571         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26572             _this.fireEvent('markerClick', this, event);
26573             
26574         });
26575
26576         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26577             _this.fireEvent('markerRightClick', this, event);
26578             
26579         });
26580         
26581         this.setPosition(this.gMapContext.location);
26582         
26583         this.fireEvent('initial', this, this.gMapContext.location);
26584     },
26585     
26586     initOverlayView: function()
26587     {
26588         var _this = this;
26589         
26590         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26591             
26592             draw: function()
26593             {
26594                 _this.fireEvent('OverlayViewDraw', _this);
26595             },
26596             
26597             onAdd: function()
26598             {
26599                 _this.fireEvent('OverlayViewOnAdd', _this);
26600             },
26601             
26602             onRemove: function()
26603             {
26604                 _this.fireEvent('OverlayViewOnRemove', _this);
26605             },
26606             
26607             show: function(cpx)
26608             {
26609                 _this.fireEvent('OverlayViewShow', _this, cpx);
26610             },
26611             
26612             hide: function()
26613             {
26614                 _this.fireEvent('OverlayViewHide', _this);
26615             }
26616             
26617         });
26618     },
26619     
26620     fromLatLngToContainerPixel: function(event)
26621     {
26622         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26623     },
26624     
26625     isApplied: function() 
26626     {
26627         return this.getGmapContext() == false ? false : true;
26628     },
26629     
26630     getGmapContext: function() 
26631     {
26632         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26633     },
26634     
26635     GMapContext: function() 
26636     {
26637         var position = new google.maps.LatLng(this.latitude, this.longitude);
26638         
26639         var _map = new google.maps.Map(this.el.dom, {
26640             center: position,
26641             zoom: this.zoom,
26642             mapTypeId: this.mapTypeId,
26643             mapTypeControl: this.mapTypeControl,
26644             disableDoubleClickZoom: this.disableDoubleClickZoom,
26645             scrollwheel: this.scrollwheel,
26646             streetViewControl: this.streetViewControl,
26647             locationName: this.locationName,
26648             draggable: this.draggable,
26649             enableAutocomplete: this.enableAutocomplete,
26650             enableReverseGeocode: this.enableReverseGeocode
26651         });
26652         
26653         var _marker = new google.maps.Marker({
26654             position: position,
26655             map: _map,
26656             title: this.markerTitle,
26657             draggable: this.draggable
26658         });
26659         
26660         return {
26661             map: _map,
26662             marker: _marker,
26663             circle: null,
26664             location: position,
26665             radius: this.radius,
26666             locationName: this.locationName,
26667             addressComponents: {
26668                 formatted_address: null,
26669                 addressLine1: null,
26670                 addressLine2: null,
26671                 streetName: null,
26672                 streetNumber: null,
26673                 city: null,
26674                 district: null,
26675                 state: null,
26676                 stateOrProvince: null
26677             },
26678             settings: this,
26679             domContainer: this.el.dom,
26680             geodecoder: new google.maps.Geocoder()
26681         };
26682     },
26683     
26684     drawCircle: function(center, radius, options) 
26685     {
26686         if (this.gMapContext.circle != null) {
26687             this.gMapContext.circle.setMap(null);
26688         }
26689         if (radius > 0) {
26690             radius *= 1;
26691             options = Roo.apply({}, options, {
26692                 strokeColor: "#0000FF",
26693                 strokeOpacity: .35,
26694                 strokeWeight: 2,
26695                 fillColor: "#0000FF",
26696                 fillOpacity: .2
26697             });
26698             
26699             options.map = this.gMapContext.map;
26700             options.radius = radius;
26701             options.center = center;
26702             this.gMapContext.circle = new google.maps.Circle(options);
26703             return this.gMapContext.circle;
26704         }
26705         
26706         return null;
26707     },
26708     
26709     setPosition: function(location) 
26710     {
26711         this.gMapContext.location = location;
26712         this.gMapContext.marker.setPosition(location);
26713         this.gMapContext.map.panTo(location);
26714         this.drawCircle(location, this.gMapContext.radius, {});
26715         
26716         var _this = this;
26717         
26718         if (this.gMapContext.settings.enableReverseGeocode) {
26719             this.gMapContext.geodecoder.geocode({
26720                 latLng: this.gMapContext.location
26721             }, function(results, status) {
26722                 
26723                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26724                     _this.gMapContext.locationName = results[0].formatted_address;
26725                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26726                     
26727                     _this.fireEvent('positionchanged', this, location);
26728                 }
26729             });
26730             
26731             return;
26732         }
26733         
26734         this.fireEvent('positionchanged', this, location);
26735     },
26736     
26737     resize: function()
26738     {
26739         google.maps.event.trigger(this.gMapContext.map, "resize");
26740         
26741         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26742         
26743         this.fireEvent('resize', this);
26744     },
26745     
26746     setPositionByLatLng: function(latitude, longitude)
26747     {
26748         this.setPosition(new google.maps.LatLng(latitude, longitude));
26749     },
26750     
26751     getCurrentPosition: function() 
26752     {
26753         return {
26754             latitude: this.gMapContext.location.lat(),
26755             longitude: this.gMapContext.location.lng()
26756         };
26757     },
26758     
26759     getAddressName: function() 
26760     {
26761         return this.gMapContext.locationName;
26762     },
26763     
26764     getAddressComponents: function() 
26765     {
26766         return this.gMapContext.addressComponents;
26767     },
26768     
26769     address_component_from_google_geocode: function(address_components) 
26770     {
26771         var result = {};
26772         
26773         for (var i = 0; i < address_components.length; i++) {
26774             var component = address_components[i];
26775             if (component.types.indexOf("postal_code") >= 0) {
26776                 result.postalCode = component.short_name;
26777             } else if (component.types.indexOf("street_number") >= 0) {
26778                 result.streetNumber = component.short_name;
26779             } else if (component.types.indexOf("route") >= 0) {
26780                 result.streetName = component.short_name;
26781             } else if (component.types.indexOf("neighborhood") >= 0) {
26782                 result.city = component.short_name;
26783             } else if (component.types.indexOf("locality") >= 0) {
26784                 result.city = component.short_name;
26785             } else if (component.types.indexOf("sublocality") >= 0) {
26786                 result.district = component.short_name;
26787             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26788                 result.stateOrProvince = component.short_name;
26789             } else if (component.types.indexOf("country") >= 0) {
26790                 result.country = component.short_name;
26791             }
26792         }
26793         
26794         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26795         result.addressLine2 = "";
26796         return result;
26797     },
26798     
26799     setZoomLevel: function(zoom)
26800     {
26801         this.gMapContext.map.setZoom(zoom);
26802     },
26803     
26804     show: function()
26805     {
26806         if(!this.el){
26807             return;
26808         }
26809         
26810         this.el.show();
26811         
26812         this.resize();
26813         
26814         this.fireEvent('show', this);
26815     },
26816     
26817     hide: function()
26818     {
26819         if(!this.el){
26820             return;
26821         }
26822         
26823         this.el.hide();
26824         
26825         this.fireEvent('hide', this);
26826     }
26827     
26828 });
26829
26830 Roo.apply(Roo.bootstrap.LocationPicker, {
26831     
26832     OverlayView : function(map, options)
26833     {
26834         options = options || {};
26835         
26836         this.setMap(map);
26837     }
26838     
26839     
26840 });/*
26841  * - LGPL
26842  *
26843  * Alert
26844  * 
26845  */
26846
26847 /**
26848  * @class Roo.bootstrap.Alert
26849  * @extends Roo.bootstrap.Component
26850  * Bootstrap Alert class
26851  * @cfg {String} title The title of alert
26852  * @cfg {String} html The content of alert
26853  * @cfg {String} weight (  success | info | warning | danger )
26854  * @cfg {String} faicon font-awesomeicon
26855  * 
26856  * @constructor
26857  * Create a new alert
26858  * @param {Object} config The config object
26859  */
26860
26861
26862 Roo.bootstrap.Alert = function(config){
26863     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26864     
26865 };
26866
26867 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26868     
26869     title: '',
26870     html: '',
26871     weight: false,
26872     faicon: false,
26873     
26874     getAutoCreate : function()
26875     {
26876         
26877         var cfg = {
26878             tag : 'div',
26879             cls : 'alert',
26880             cn : [
26881                 {
26882                     tag : 'i',
26883                     cls : 'roo-alert-icon'
26884                     
26885                 },
26886                 {
26887                     tag : 'b',
26888                     cls : 'roo-alert-title',
26889                     html : this.title
26890                 },
26891                 {
26892                     tag : 'span',
26893                     cls : 'roo-alert-text',
26894                     html : this.html
26895                 }
26896             ]
26897         };
26898         
26899         if(this.faicon){
26900             cfg.cn[0].cls += ' fa ' + this.faicon;
26901         }
26902         
26903         if(this.weight){
26904             cfg.cls += ' alert-' + this.weight;
26905         }
26906         
26907         return cfg;
26908     },
26909     
26910     initEvents: function() 
26911     {
26912         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26913     },
26914     
26915     setTitle : function(str)
26916     {
26917         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26918     },
26919     
26920     setText : function(str)
26921     {
26922         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26923     },
26924     
26925     setWeight : function(weight)
26926     {
26927         if(this.weight){
26928             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26929         }
26930         
26931         this.weight = weight;
26932         
26933         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26934     },
26935     
26936     setIcon : function(icon)
26937     {
26938         if(this.faicon){
26939             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26940         }
26941         
26942         this.faicon = icon;
26943         
26944         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26945     },
26946     
26947     hide: function() 
26948     {
26949         this.el.hide();   
26950     },
26951     
26952     show: function() 
26953     {  
26954         this.el.show();   
26955     }
26956     
26957 });
26958
26959  
26960 /*
26961 * Licence: LGPL
26962 */
26963
26964 /**
26965  * @class Roo.bootstrap.UploadCropbox
26966  * @extends Roo.bootstrap.Component
26967  * Bootstrap UploadCropbox class
26968  * @cfg {String} emptyText show when image has been loaded
26969  * @cfg {String} rotateNotify show when image too small to rotate
26970  * @cfg {Number} errorTimeout default 3000
26971  * @cfg {Number} minWidth default 300
26972  * @cfg {Number} minHeight default 300
26973  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26974  * @cfg {Boolean} isDocument (true|false) default false
26975  * @cfg {String} url action url
26976  * @cfg {String} paramName default 'imageUpload'
26977  * @cfg {String} method default POST
26978  * @cfg {Boolean} loadMask (true|false) default true
26979  * @cfg {Boolean} loadingText default 'Loading...'
26980  * 
26981  * @constructor
26982  * Create a new UploadCropbox
26983  * @param {Object} config The config object
26984  */
26985
26986 Roo.bootstrap.UploadCropbox = function(config){
26987     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26988     
26989     this.addEvents({
26990         /**
26991          * @event beforeselectfile
26992          * Fire before select file
26993          * @param {Roo.bootstrap.UploadCropbox} this
26994          */
26995         "beforeselectfile" : true,
26996         /**
26997          * @event initial
26998          * Fire after initEvent
26999          * @param {Roo.bootstrap.UploadCropbox} this
27000          */
27001         "initial" : true,
27002         /**
27003          * @event crop
27004          * Fire after initEvent
27005          * @param {Roo.bootstrap.UploadCropbox} this
27006          * @param {String} data
27007          */
27008         "crop" : true,
27009         /**
27010          * @event prepare
27011          * Fire when preparing the file data
27012          * @param {Roo.bootstrap.UploadCropbox} this
27013          * @param {Object} file
27014          */
27015         "prepare" : true,
27016         /**
27017          * @event exception
27018          * Fire when get exception
27019          * @param {Roo.bootstrap.UploadCropbox} this
27020          * @param {XMLHttpRequest} xhr
27021          */
27022         "exception" : true,
27023         /**
27024          * @event beforeloadcanvas
27025          * Fire before load the canvas
27026          * @param {Roo.bootstrap.UploadCropbox} this
27027          * @param {String} src
27028          */
27029         "beforeloadcanvas" : true,
27030         /**
27031          * @event trash
27032          * Fire when trash image
27033          * @param {Roo.bootstrap.UploadCropbox} this
27034          */
27035         "trash" : true,
27036         /**
27037          * @event download
27038          * Fire when download the image
27039          * @param {Roo.bootstrap.UploadCropbox} this
27040          */
27041         "download" : true,
27042         /**
27043          * @event footerbuttonclick
27044          * Fire when footerbuttonclick
27045          * @param {Roo.bootstrap.UploadCropbox} this
27046          * @param {String} type
27047          */
27048         "footerbuttonclick" : true,
27049         /**
27050          * @event resize
27051          * Fire when resize
27052          * @param {Roo.bootstrap.UploadCropbox} this
27053          */
27054         "resize" : true,
27055         /**
27056          * @event rotate
27057          * Fire when rotate the image
27058          * @param {Roo.bootstrap.UploadCropbox} this
27059          * @param {String} pos
27060          */
27061         "rotate" : true,
27062         /**
27063          * @event inspect
27064          * Fire when inspect the file
27065          * @param {Roo.bootstrap.UploadCropbox} this
27066          * @param {Object} file
27067          */
27068         "inspect" : true,
27069         /**
27070          * @event upload
27071          * Fire when xhr upload the file
27072          * @param {Roo.bootstrap.UploadCropbox} this
27073          * @param {Object} data
27074          */
27075         "upload" : true,
27076         /**
27077          * @event arrange
27078          * Fire when arrange the file data
27079          * @param {Roo.bootstrap.UploadCropbox} this
27080          * @param {Object} formData
27081          */
27082         "arrange" : true
27083     });
27084     
27085     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27086 };
27087
27088 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27089     
27090     emptyText : 'Click to upload image',
27091     rotateNotify : 'Image is too small to rotate',
27092     errorTimeout : 3000,
27093     scale : 0,
27094     baseScale : 1,
27095     rotate : 0,
27096     dragable : false,
27097     pinching : false,
27098     mouseX : 0,
27099     mouseY : 0,
27100     cropData : false,
27101     minWidth : 300,
27102     minHeight : 300,
27103     file : false,
27104     exif : {},
27105     baseRotate : 1,
27106     cropType : 'image/jpeg',
27107     buttons : false,
27108     canvasLoaded : false,
27109     isDocument : false,
27110     method : 'POST',
27111     paramName : 'imageUpload',
27112     loadMask : true,
27113     loadingText : 'Loading...',
27114     maskEl : false,
27115     
27116     getAutoCreate : function()
27117     {
27118         var cfg = {
27119             tag : 'div',
27120             cls : 'roo-upload-cropbox',
27121             cn : [
27122                 {
27123                     tag : 'input',
27124                     cls : 'roo-upload-cropbox-selector',
27125                     type : 'file'
27126                 },
27127                 {
27128                     tag : 'div',
27129                     cls : 'roo-upload-cropbox-body',
27130                     style : 'cursor:pointer',
27131                     cn : [
27132                         {
27133                             tag : 'div',
27134                             cls : 'roo-upload-cropbox-preview'
27135                         },
27136                         {
27137                             tag : 'div',
27138                             cls : 'roo-upload-cropbox-thumb'
27139                         },
27140                         {
27141                             tag : 'div',
27142                             cls : 'roo-upload-cropbox-empty-notify',
27143                             html : this.emptyText
27144                         },
27145                         {
27146                             tag : 'div',
27147                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27148                             html : this.rotateNotify
27149                         }
27150                     ]
27151                 },
27152                 {
27153                     tag : 'div',
27154                     cls : 'roo-upload-cropbox-footer',
27155                     cn : {
27156                         tag : 'div',
27157                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27158                         cn : []
27159                     }
27160                 }
27161             ]
27162         };
27163         
27164         return cfg;
27165     },
27166     
27167     onRender : function(ct, position)
27168     {
27169         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27170         
27171         if (this.buttons.length) {
27172             
27173             Roo.each(this.buttons, function(bb) {
27174                 
27175                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27176                 
27177                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27178                 
27179             }, this);
27180         }
27181         
27182         if(this.loadMask){
27183             this.maskEl = this.el;
27184         }
27185     },
27186     
27187     initEvents : function()
27188     {
27189         this.urlAPI = (window.createObjectURL && window) || 
27190                                 (window.URL && URL.revokeObjectURL && URL) || 
27191                                 (window.webkitURL && webkitURL);
27192                         
27193         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27194         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27195         
27196         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27197         this.selectorEl.hide();
27198         
27199         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27200         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27201         
27202         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27203         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27204         this.thumbEl.hide();
27205         
27206         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27207         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27208         
27209         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27210         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27211         this.errorEl.hide();
27212         
27213         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27214         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27215         this.footerEl.hide();
27216         
27217         this.setThumbBoxSize();
27218         
27219         this.bind();
27220         
27221         this.resize();
27222         
27223         this.fireEvent('initial', this);
27224     },
27225
27226     bind : function()
27227     {
27228         var _this = this;
27229         
27230         window.addEventListener("resize", function() { _this.resize(); } );
27231         
27232         this.bodyEl.on('click', this.beforeSelectFile, this);
27233         
27234         if(Roo.isTouch){
27235             this.bodyEl.on('touchstart', this.onTouchStart, this);
27236             this.bodyEl.on('touchmove', this.onTouchMove, this);
27237             this.bodyEl.on('touchend', this.onTouchEnd, this);
27238         }
27239         
27240         if(!Roo.isTouch){
27241             this.bodyEl.on('mousedown', this.onMouseDown, this);
27242             this.bodyEl.on('mousemove', this.onMouseMove, this);
27243             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27244             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27245             Roo.get(document).on('mouseup', this.onMouseUp, this);
27246         }
27247         
27248         this.selectorEl.on('change', this.onFileSelected, this);
27249     },
27250     
27251     reset : function()
27252     {    
27253         this.scale = 0;
27254         this.baseScale = 1;
27255         this.rotate = 0;
27256         this.baseRotate = 1;
27257         this.dragable = false;
27258         this.pinching = false;
27259         this.mouseX = 0;
27260         this.mouseY = 0;
27261         this.cropData = false;
27262         this.notifyEl.dom.innerHTML = this.emptyText;
27263         
27264         this.selectorEl.dom.value = '';
27265         
27266     },
27267     
27268     resize : function()
27269     {
27270         if(this.fireEvent('resize', this) != false){
27271             this.setThumbBoxPosition();
27272             this.setCanvasPosition();
27273         }
27274     },
27275     
27276     onFooterButtonClick : function(e, el, o, type)
27277     {
27278         switch (type) {
27279             case 'rotate-left' :
27280                 this.onRotateLeft(e);
27281                 break;
27282             case 'rotate-right' :
27283                 this.onRotateRight(e);
27284                 break;
27285             case 'picture' :
27286                 this.beforeSelectFile(e);
27287                 break;
27288             case 'trash' :
27289                 this.trash(e);
27290                 break;
27291             case 'crop' :
27292                 this.crop(e);
27293                 break;
27294             case 'download' :
27295                 this.download(e);
27296                 break;
27297             default :
27298                 break;
27299         }
27300         
27301         this.fireEvent('footerbuttonclick', this, type);
27302     },
27303     
27304     beforeSelectFile : function(e)
27305     {
27306         e.preventDefault();
27307         
27308         if(this.fireEvent('beforeselectfile', this) != false){
27309             this.selectorEl.dom.click();
27310         }
27311     },
27312     
27313     onFileSelected : function(e)
27314     {
27315         e.preventDefault();
27316         
27317         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27318             return;
27319         }
27320         
27321         var file = this.selectorEl.dom.files[0];
27322         
27323         if(this.fireEvent('inspect', this, file) != false){
27324             this.prepare(file);
27325         }
27326         
27327     },
27328     
27329     trash : function(e)
27330     {
27331         this.fireEvent('trash', this);
27332     },
27333     
27334     download : function(e)
27335     {
27336         this.fireEvent('download', this);
27337     },
27338     
27339     loadCanvas : function(src)
27340     {   
27341         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27342             
27343             this.reset();
27344             
27345             this.imageEl = document.createElement('img');
27346             
27347             var _this = this;
27348             
27349             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27350             
27351             this.imageEl.src = src;
27352         }
27353     },
27354     
27355     onLoadCanvas : function()
27356     {   
27357         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27358         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27359         
27360         this.bodyEl.un('click', this.beforeSelectFile, this);
27361         
27362         this.notifyEl.hide();
27363         this.thumbEl.show();
27364         this.footerEl.show();
27365         
27366         this.baseRotateLevel();
27367         
27368         if(this.isDocument){
27369             this.setThumbBoxSize();
27370         }
27371         
27372         this.setThumbBoxPosition();
27373         
27374         this.baseScaleLevel();
27375         
27376         this.draw();
27377         
27378         this.resize();
27379         
27380         this.canvasLoaded = true;
27381         
27382         if(this.loadMask){
27383             this.maskEl.unmask();
27384         }
27385         
27386     },
27387     
27388     setCanvasPosition : function()
27389     {   
27390         if(!this.canvasEl){
27391             return;
27392         }
27393         
27394         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27395         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27396         
27397         this.previewEl.setLeft(pw);
27398         this.previewEl.setTop(ph);
27399         
27400     },
27401     
27402     onMouseDown : function(e)
27403     {   
27404         e.stopEvent();
27405         
27406         this.dragable = true;
27407         this.pinching = false;
27408         
27409         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27410             this.dragable = false;
27411             return;
27412         }
27413         
27414         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27415         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27416         
27417     },
27418     
27419     onMouseMove : function(e)
27420     {   
27421         e.stopEvent();
27422         
27423         if(!this.canvasLoaded){
27424             return;
27425         }
27426         
27427         if (!this.dragable){
27428             return;
27429         }
27430         
27431         var minX = Math.ceil(this.thumbEl.getLeft(true));
27432         var minY = Math.ceil(this.thumbEl.getTop(true));
27433         
27434         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27435         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27436         
27437         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27438         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27439         
27440         x = x - this.mouseX;
27441         y = y - this.mouseY;
27442         
27443         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27444         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27445         
27446         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27447         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27448         
27449         this.previewEl.setLeft(bgX);
27450         this.previewEl.setTop(bgY);
27451         
27452         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27453         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27454     },
27455     
27456     onMouseUp : function(e)
27457     {   
27458         e.stopEvent();
27459         
27460         this.dragable = false;
27461     },
27462     
27463     onMouseWheel : function(e)
27464     {   
27465         e.stopEvent();
27466         
27467         this.startScale = this.scale;
27468         
27469         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27470         
27471         if(!this.zoomable()){
27472             this.scale = this.startScale;
27473             return;
27474         }
27475         
27476         this.draw();
27477         
27478         return;
27479     },
27480     
27481     zoomable : function()
27482     {
27483         var minScale = this.thumbEl.getWidth() / this.minWidth;
27484         
27485         if(this.minWidth < this.minHeight){
27486             minScale = this.thumbEl.getHeight() / this.minHeight;
27487         }
27488         
27489         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27490         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27491         
27492         if(
27493                 this.isDocument &&
27494                 (this.rotate == 0 || this.rotate == 180) && 
27495                 (
27496                     width > this.imageEl.OriginWidth || 
27497                     height > this.imageEl.OriginHeight ||
27498                     (width < this.minWidth && height < this.minHeight)
27499                 )
27500         ){
27501             return false;
27502         }
27503         
27504         if(
27505                 this.isDocument &&
27506                 (this.rotate == 90 || this.rotate == 270) && 
27507                 (
27508                     width > this.imageEl.OriginWidth || 
27509                     height > this.imageEl.OriginHeight ||
27510                     (width < this.minHeight && height < this.minWidth)
27511                 )
27512         ){
27513             return false;
27514         }
27515         
27516         if(
27517                 !this.isDocument &&
27518                 (this.rotate == 0 || this.rotate == 180) && 
27519                 (
27520                     width < this.minWidth || 
27521                     width > this.imageEl.OriginWidth || 
27522                     height < this.minHeight || 
27523                     height > this.imageEl.OriginHeight
27524                 )
27525         ){
27526             return false;
27527         }
27528         
27529         if(
27530                 !this.isDocument &&
27531                 (this.rotate == 90 || this.rotate == 270) && 
27532                 (
27533                     width < this.minHeight || 
27534                     width > this.imageEl.OriginWidth || 
27535                     height < this.minWidth || 
27536                     height > this.imageEl.OriginHeight
27537                 )
27538         ){
27539             return false;
27540         }
27541         
27542         return true;
27543         
27544     },
27545     
27546     onRotateLeft : function(e)
27547     {   
27548         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27549             
27550             var minScale = this.thumbEl.getWidth() / this.minWidth;
27551             
27552             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27553             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27554             
27555             this.startScale = this.scale;
27556             
27557             while (this.getScaleLevel() < minScale){
27558             
27559                 this.scale = this.scale + 1;
27560                 
27561                 if(!this.zoomable()){
27562                     break;
27563                 }
27564                 
27565                 if(
27566                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27567                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27568                 ){
27569                     continue;
27570                 }
27571                 
27572                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27573
27574                 this.draw();
27575                 
27576                 return;
27577             }
27578             
27579             this.scale = this.startScale;
27580             
27581             this.onRotateFail();
27582             
27583             return false;
27584         }
27585         
27586         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27587
27588         if(this.isDocument){
27589             this.setThumbBoxSize();
27590             this.setThumbBoxPosition();
27591             this.setCanvasPosition();
27592         }
27593         
27594         this.draw();
27595         
27596         this.fireEvent('rotate', this, 'left');
27597         
27598     },
27599     
27600     onRotateRight : function(e)
27601     {
27602         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27603             
27604             var minScale = this.thumbEl.getWidth() / this.minWidth;
27605         
27606             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27607             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27608             
27609             this.startScale = this.scale;
27610             
27611             while (this.getScaleLevel() < minScale){
27612             
27613                 this.scale = this.scale + 1;
27614                 
27615                 if(!this.zoomable()){
27616                     break;
27617                 }
27618                 
27619                 if(
27620                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27621                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27622                 ){
27623                     continue;
27624                 }
27625                 
27626                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27627
27628                 this.draw();
27629                 
27630                 return;
27631             }
27632             
27633             this.scale = this.startScale;
27634             
27635             this.onRotateFail();
27636             
27637             return false;
27638         }
27639         
27640         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27641
27642         if(this.isDocument){
27643             this.setThumbBoxSize();
27644             this.setThumbBoxPosition();
27645             this.setCanvasPosition();
27646         }
27647         
27648         this.draw();
27649         
27650         this.fireEvent('rotate', this, 'right');
27651     },
27652     
27653     onRotateFail : function()
27654     {
27655         this.errorEl.show(true);
27656         
27657         var _this = this;
27658         
27659         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27660     },
27661     
27662     draw : function()
27663     {
27664         this.previewEl.dom.innerHTML = '';
27665         
27666         var canvasEl = document.createElement("canvas");
27667         
27668         var contextEl = canvasEl.getContext("2d");
27669         
27670         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27671         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27672         var center = this.imageEl.OriginWidth / 2;
27673         
27674         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27675             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27676             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27677             center = this.imageEl.OriginHeight / 2;
27678         }
27679         
27680         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27681         
27682         contextEl.translate(center, center);
27683         contextEl.rotate(this.rotate * Math.PI / 180);
27684
27685         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27686         
27687         this.canvasEl = document.createElement("canvas");
27688         
27689         this.contextEl = this.canvasEl.getContext("2d");
27690         
27691         switch (this.rotate) {
27692             case 0 :
27693                 
27694                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27695                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27696                 
27697                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27698                 
27699                 break;
27700             case 90 : 
27701                 
27702                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27703                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27704                 
27705                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27706                     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);
27707                     break;
27708                 }
27709                 
27710                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27711                 
27712                 break;
27713             case 180 :
27714                 
27715                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27716                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27717                 
27718                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27719                     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);
27720                     break;
27721                 }
27722                 
27723                 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);
27724                 
27725                 break;
27726             case 270 :
27727                 
27728                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27729                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27730         
27731                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27732                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27733                     break;
27734                 }
27735                 
27736                 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);
27737                 
27738                 break;
27739             default : 
27740                 break;
27741         }
27742         
27743         this.previewEl.appendChild(this.canvasEl);
27744         
27745         this.setCanvasPosition();
27746     },
27747     
27748     crop : function()
27749     {
27750         if(!this.canvasLoaded){
27751             return;
27752         }
27753         
27754         var imageCanvas = document.createElement("canvas");
27755         
27756         var imageContext = imageCanvas.getContext("2d");
27757         
27758         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27759         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27760         
27761         var center = imageCanvas.width / 2;
27762         
27763         imageContext.translate(center, center);
27764         
27765         imageContext.rotate(this.rotate * Math.PI / 180);
27766         
27767         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27768         
27769         var canvas = document.createElement("canvas");
27770         
27771         var context = canvas.getContext("2d");
27772                 
27773         canvas.width = this.minWidth;
27774         canvas.height = this.minHeight;
27775
27776         switch (this.rotate) {
27777             case 0 :
27778                 
27779                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27780                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27781                 
27782                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27783                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27784                 
27785                 var targetWidth = this.minWidth - 2 * x;
27786                 var targetHeight = this.minHeight - 2 * y;
27787                 
27788                 var scale = 1;
27789                 
27790                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27791                     scale = targetWidth / width;
27792                 }
27793                 
27794                 if(x > 0 && y == 0){
27795                     scale = targetHeight / height;
27796                 }
27797                 
27798                 if(x > 0 && y > 0){
27799                     scale = targetWidth / width;
27800                     
27801                     if(width < height){
27802                         scale = targetHeight / height;
27803                     }
27804                 }
27805                 
27806                 context.scale(scale, scale);
27807                 
27808                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27809                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27810
27811                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27812                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27813
27814                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27815                 
27816                 break;
27817             case 90 : 
27818                 
27819                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27820                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27821                 
27822                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27823                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27824                 
27825                 var targetWidth = this.minWidth - 2 * x;
27826                 var targetHeight = this.minHeight - 2 * y;
27827                 
27828                 var scale = 1;
27829                 
27830                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27831                     scale = targetWidth / width;
27832                 }
27833                 
27834                 if(x > 0 && y == 0){
27835                     scale = targetHeight / height;
27836                 }
27837                 
27838                 if(x > 0 && y > 0){
27839                     scale = targetWidth / width;
27840                     
27841                     if(width < height){
27842                         scale = targetHeight / height;
27843                     }
27844                 }
27845                 
27846                 context.scale(scale, scale);
27847                 
27848                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27849                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27850
27851                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27852                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27853                 
27854                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27855                 
27856                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27857                 
27858                 break;
27859             case 180 :
27860                 
27861                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27862                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27863                 
27864                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27865                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27866                 
27867                 var targetWidth = this.minWidth - 2 * x;
27868                 var targetHeight = this.minHeight - 2 * y;
27869                 
27870                 var scale = 1;
27871                 
27872                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27873                     scale = targetWidth / width;
27874                 }
27875                 
27876                 if(x > 0 && y == 0){
27877                     scale = targetHeight / height;
27878                 }
27879                 
27880                 if(x > 0 && y > 0){
27881                     scale = targetWidth / width;
27882                     
27883                     if(width < height){
27884                         scale = targetHeight / height;
27885                     }
27886                 }
27887                 
27888                 context.scale(scale, scale);
27889                 
27890                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27891                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27892
27893                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27894                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27895
27896                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27897                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27898                 
27899                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27900                 
27901                 break;
27902             case 270 :
27903                 
27904                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27905                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27906                 
27907                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27908                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27909                 
27910                 var targetWidth = this.minWidth - 2 * x;
27911                 var targetHeight = this.minHeight - 2 * y;
27912                 
27913                 var scale = 1;
27914                 
27915                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27916                     scale = targetWidth / width;
27917                 }
27918                 
27919                 if(x > 0 && y == 0){
27920                     scale = targetHeight / height;
27921                 }
27922                 
27923                 if(x > 0 && y > 0){
27924                     scale = targetWidth / width;
27925                     
27926                     if(width < height){
27927                         scale = targetHeight / height;
27928                     }
27929                 }
27930                 
27931                 context.scale(scale, scale);
27932                 
27933                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27934                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27935
27936                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27937                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27938                 
27939                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27940                 
27941                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27942                 
27943                 break;
27944             default : 
27945                 break;
27946         }
27947         
27948         this.cropData = canvas.toDataURL(this.cropType);
27949         
27950         if(this.fireEvent('crop', this, this.cropData) !== false){
27951             this.process(this.file, this.cropData);
27952         }
27953         
27954         return;
27955         
27956     },
27957     
27958     setThumbBoxSize : function()
27959     {
27960         var width, height;
27961         
27962         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27963             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27964             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27965             
27966             this.minWidth = width;
27967             this.minHeight = height;
27968             
27969             if(this.rotate == 90 || this.rotate == 270){
27970                 this.minWidth = height;
27971                 this.minHeight = width;
27972             }
27973         }
27974         
27975         height = 300;
27976         width = Math.ceil(this.minWidth * height / this.minHeight);
27977         
27978         if(this.minWidth > this.minHeight){
27979             width = 300;
27980             height = Math.ceil(this.minHeight * width / this.minWidth);
27981         }
27982         
27983         this.thumbEl.setStyle({
27984             width : width + 'px',
27985             height : height + 'px'
27986         });
27987
27988         return;
27989             
27990     },
27991     
27992     setThumbBoxPosition : function()
27993     {
27994         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27995         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27996         
27997         this.thumbEl.setLeft(x);
27998         this.thumbEl.setTop(y);
27999         
28000     },
28001     
28002     baseRotateLevel : function()
28003     {
28004         this.baseRotate = 1;
28005         
28006         if(
28007                 typeof(this.exif) != 'undefined' &&
28008                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28009                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28010         ){
28011             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28012         }
28013         
28014         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28015         
28016     },
28017     
28018     baseScaleLevel : function()
28019     {
28020         var width, height;
28021         
28022         if(this.isDocument){
28023             
28024             if(this.baseRotate == 6 || this.baseRotate == 8){
28025             
28026                 height = this.thumbEl.getHeight();
28027                 this.baseScale = height / this.imageEl.OriginWidth;
28028
28029                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28030                     width = this.thumbEl.getWidth();
28031                     this.baseScale = width / this.imageEl.OriginHeight;
28032                 }
28033
28034                 return;
28035             }
28036
28037             height = this.thumbEl.getHeight();
28038             this.baseScale = height / this.imageEl.OriginHeight;
28039
28040             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28041                 width = this.thumbEl.getWidth();
28042                 this.baseScale = width / this.imageEl.OriginWidth;
28043             }
28044
28045             return;
28046         }
28047         
28048         if(this.baseRotate == 6 || this.baseRotate == 8){
28049             
28050             width = this.thumbEl.getHeight();
28051             this.baseScale = width / this.imageEl.OriginHeight;
28052             
28053             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28054                 height = this.thumbEl.getWidth();
28055                 this.baseScale = height / this.imageEl.OriginHeight;
28056             }
28057             
28058             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28059                 height = this.thumbEl.getWidth();
28060                 this.baseScale = height / this.imageEl.OriginHeight;
28061                 
28062                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28063                     width = this.thumbEl.getHeight();
28064                     this.baseScale = width / this.imageEl.OriginWidth;
28065                 }
28066             }
28067             
28068             return;
28069         }
28070         
28071         width = this.thumbEl.getWidth();
28072         this.baseScale = width / this.imageEl.OriginWidth;
28073         
28074         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28075             height = this.thumbEl.getHeight();
28076             this.baseScale = height / this.imageEl.OriginHeight;
28077         }
28078         
28079         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28080             
28081             height = this.thumbEl.getHeight();
28082             this.baseScale = height / this.imageEl.OriginHeight;
28083             
28084             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28085                 width = this.thumbEl.getWidth();
28086                 this.baseScale = width / this.imageEl.OriginWidth;
28087             }
28088             
28089         }
28090         
28091         return;
28092     },
28093     
28094     getScaleLevel : function()
28095     {
28096         return this.baseScale * Math.pow(1.1, this.scale);
28097     },
28098     
28099     onTouchStart : function(e)
28100     {
28101         if(!this.canvasLoaded){
28102             this.beforeSelectFile(e);
28103             return;
28104         }
28105         
28106         var touches = e.browserEvent.touches;
28107         
28108         if(!touches){
28109             return;
28110         }
28111         
28112         if(touches.length == 1){
28113             this.onMouseDown(e);
28114             return;
28115         }
28116         
28117         if(touches.length != 2){
28118             return;
28119         }
28120         
28121         var coords = [];
28122         
28123         for(var i = 0, finger; finger = touches[i]; i++){
28124             coords.push(finger.pageX, finger.pageY);
28125         }
28126         
28127         var x = Math.pow(coords[0] - coords[2], 2);
28128         var y = Math.pow(coords[1] - coords[3], 2);
28129         
28130         this.startDistance = Math.sqrt(x + y);
28131         
28132         this.startScale = this.scale;
28133         
28134         this.pinching = true;
28135         this.dragable = false;
28136         
28137     },
28138     
28139     onTouchMove : function(e)
28140     {
28141         if(!this.pinching && !this.dragable){
28142             return;
28143         }
28144         
28145         var touches = e.browserEvent.touches;
28146         
28147         if(!touches){
28148             return;
28149         }
28150         
28151         if(this.dragable){
28152             this.onMouseMove(e);
28153             return;
28154         }
28155         
28156         var coords = [];
28157         
28158         for(var i = 0, finger; finger = touches[i]; i++){
28159             coords.push(finger.pageX, finger.pageY);
28160         }
28161         
28162         var x = Math.pow(coords[0] - coords[2], 2);
28163         var y = Math.pow(coords[1] - coords[3], 2);
28164         
28165         this.endDistance = Math.sqrt(x + y);
28166         
28167         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28168         
28169         if(!this.zoomable()){
28170             this.scale = this.startScale;
28171             return;
28172         }
28173         
28174         this.draw();
28175         
28176     },
28177     
28178     onTouchEnd : function(e)
28179     {
28180         this.pinching = false;
28181         this.dragable = false;
28182         
28183     },
28184     
28185     process : function(file, crop)
28186     {
28187         if(this.loadMask){
28188             this.maskEl.mask(this.loadingText);
28189         }
28190         
28191         this.xhr = new XMLHttpRequest();
28192         
28193         file.xhr = this.xhr;
28194
28195         this.xhr.open(this.method, this.url, true);
28196         
28197         var headers = {
28198             "Accept": "application/json",
28199             "Cache-Control": "no-cache",
28200             "X-Requested-With": "XMLHttpRequest"
28201         };
28202         
28203         for (var headerName in headers) {
28204             var headerValue = headers[headerName];
28205             if (headerValue) {
28206                 this.xhr.setRequestHeader(headerName, headerValue);
28207             }
28208         }
28209         
28210         var _this = this;
28211         
28212         this.xhr.onload = function()
28213         {
28214             _this.xhrOnLoad(_this.xhr);
28215         }
28216         
28217         this.xhr.onerror = function()
28218         {
28219             _this.xhrOnError(_this.xhr);
28220         }
28221         
28222         var formData = new FormData();
28223
28224         formData.append('returnHTML', 'NO');
28225         
28226         if(crop){
28227             formData.append('crop', crop);
28228         }
28229         
28230         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28231             formData.append(this.paramName, file, file.name);
28232         }
28233         
28234         if(typeof(file.filename) != 'undefined'){
28235             formData.append('filename', file.filename);
28236         }
28237         
28238         if(typeof(file.mimetype) != 'undefined'){
28239             formData.append('mimetype', file.mimetype);
28240         }
28241         
28242         if(this.fireEvent('arrange', this, formData) != false){
28243             this.xhr.send(formData);
28244         };
28245     },
28246     
28247     xhrOnLoad : function(xhr)
28248     {
28249         if(this.loadMask){
28250             this.maskEl.unmask();
28251         }
28252         
28253         if (xhr.readyState !== 4) {
28254             this.fireEvent('exception', this, xhr);
28255             return;
28256         }
28257
28258         var response = Roo.decode(xhr.responseText);
28259         
28260         if(!response.success){
28261             this.fireEvent('exception', this, xhr);
28262             return;
28263         }
28264         
28265         var response = Roo.decode(xhr.responseText);
28266         
28267         this.fireEvent('upload', this, response);
28268         
28269     },
28270     
28271     xhrOnError : function()
28272     {
28273         if(this.loadMask){
28274             this.maskEl.unmask();
28275         }
28276         
28277         Roo.log('xhr on error');
28278         
28279         var response = Roo.decode(xhr.responseText);
28280           
28281         Roo.log(response);
28282         
28283     },
28284     
28285     prepare : function(file)
28286     {   
28287         if(this.loadMask){
28288             this.maskEl.mask(this.loadingText);
28289         }
28290         
28291         this.file = false;
28292         this.exif = {};
28293         
28294         if(typeof(file) === 'string'){
28295             this.loadCanvas(file);
28296             return;
28297         }
28298         
28299         if(!file || !this.urlAPI){
28300             return;
28301         }
28302         
28303         this.file = file;
28304         this.cropType = file.type;
28305         
28306         var _this = this;
28307         
28308         if(this.fireEvent('prepare', this, this.file) != false){
28309             
28310             var reader = new FileReader();
28311             
28312             reader.onload = function (e) {
28313                 if (e.target.error) {
28314                     Roo.log(e.target.error);
28315                     return;
28316                 }
28317                 
28318                 var buffer = e.target.result,
28319                     dataView = new DataView(buffer),
28320                     offset = 2,
28321                     maxOffset = dataView.byteLength - 4,
28322                     markerBytes,
28323                     markerLength;
28324                 
28325                 if (dataView.getUint16(0) === 0xffd8) {
28326                     while (offset < maxOffset) {
28327                         markerBytes = dataView.getUint16(offset);
28328                         
28329                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28330                             markerLength = dataView.getUint16(offset + 2) + 2;
28331                             if (offset + markerLength > dataView.byteLength) {
28332                                 Roo.log('Invalid meta data: Invalid segment size.');
28333                                 break;
28334                             }
28335                             
28336                             if(markerBytes == 0xffe1){
28337                                 _this.parseExifData(
28338                                     dataView,
28339                                     offset,
28340                                     markerLength
28341                                 );
28342                             }
28343                             
28344                             offset += markerLength;
28345                             
28346                             continue;
28347                         }
28348                         
28349                         break;
28350                     }
28351                     
28352                 }
28353                 
28354                 var url = _this.urlAPI.createObjectURL(_this.file);
28355                 
28356                 _this.loadCanvas(url);
28357                 
28358                 return;
28359             }
28360             
28361             reader.readAsArrayBuffer(this.file);
28362             
28363         }
28364         
28365     },
28366     
28367     parseExifData : function(dataView, offset, length)
28368     {
28369         var tiffOffset = offset + 10,
28370             littleEndian,
28371             dirOffset;
28372     
28373         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28374             // No Exif data, might be XMP data instead
28375             return;
28376         }
28377         
28378         // Check for the ASCII code for "Exif" (0x45786966):
28379         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28380             // No Exif data, might be XMP data instead
28381             return;
28382         }
28383         if (tiffOffset + 8 > dataView.byteLength) {
28384             Roo.log('Invalid Exif data: Invalid segment size.');
28385             return;
28386         }
28387         // Check for the two null bytes:
28388         if (dataView.getUint16(offset + 8) !== 0x0000) {
28389             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28390             return;
28391         }
28392         // Check the byte alignment:
28393         switch (dataView.getUint16(tiffOffset)) {
28394         case 0x4949:
28395             littleEndian = true;
28396             break;
28397         case 0x4D4D:
28398             littleEndian = false;
28399             break;
28400         default:
28401             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28402             return;
28403         }
28404         // Check for the TIFF tag marker (0x002A):
28405         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28406             Roo.log('Invalid Exif data: Missing TIFF marker.');
28407             return;
28408         }
28409         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28410         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28411         
28412         this.parseExifTags(
28413             dataView,
28414             tiffOffset,
28415             tiffOffset + dirOffset,
28416             littleEndian
28417         );
28418     },
28419     
28420     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28421     {
28422         var tagsNumber,
28423             dirEndOffset,
28424             i;
28425         if (dirOffset + 6 > dataView.byteLength) {
28426             Roo.log('Invalid Exif data: Invalid directory offset.');
28427             return;
28428         }
28429         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28430         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28431         if (dirEndOffset + 4 > dataView.byteLength) {
28432             Roo.log('Invalid Exif data: Invalid directory size.');
28433             return;
28434         }
28435         for (i = 0; i < tagsNumber; i += 1) {
28436             this.parseExifTag(
28437                 dataView,
28438                 tiffOffset,
28439                 dirOffset + 2 + 12 * i, // tag offset
28440                 littleEndian
28441             );
28442         }
28443         // Return the offset to the next directory:
28444         return dataView.getUint32(dirEndOffset, littleEndian);
28445     },
28446     
28447     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28448     {
28449         var tag = dataView.getUint16(offset, littleEndian);
28450         
28451         this.exif[tag] = this.getExifValue(
28452             dataView,
28453             tiffOffset,
28454             offset,
28455             dataView.getUint16(offset + 2, littleEndian), // tag type
28456             dataView.getUint32(offset + 4, littleEndian), // tag length
28457             littleEndian
28458         );
28459     },
28460     
28461     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28462     {
28463         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28464             tagSize,
28465             dataOffset,
28466             values,
28467             i,
28468             str,
28469             c;
28470     
28471         if (!tagType) {
28472             Roo.log('Invalid Exif data: Invalid tag type.');
28473             return;
28474         }
28475         
28476         tagSize = tagType.size * length;
28477         // Determine if the value is contained in the dataOffset bytes,
28478         // or if the value at the dataOffset is a pointer to the actual data:
28479         dataOffset = tagSize > 4 ?
28480                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28481         if (dataOffset + tagSize > dataView.byteLength) {
28482             Roo.log('Invalid Exif data: Invalid data offset.');
28483             return;
28484         }
28485         if (length === 1) {
28486             return tagType.getValue(dataView, dataOffset, littleEndian);
28487         }
28488         values = [];
28489         for (i = 0; i < length; i += 1) {
28490             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28491         }
28492         
28493         if (tagType.ascii) {
28494             str = '';
28495             // Concatenate the chars:
28496             for (i = 0; i < values.length; i += 1) {
28497                 c = values[i];
28498                 // Ignore the terminating NULL byte(s):
28499                 if (c === '\u0000') {
28500                     break;
28501                 }
28502                 str += c;
28503             }
28504             return str;
28505         }
28506         return values;
28507     }
28508     
28509 });
28510
28511 Roo.apply(Roo.bootstrap.UploadCropbox, {
28512     tags : {
28513         'Orientation': 0x0112
28514     },
28515     
28516     Orientation: {
28517             1: 0, //'top-left',
28518 //            2: 'top-right',
28519             3: 180, //'bottom-right',
28520 //            4: 'bottom-left',
28521 //            5: 'left-top',
28522             6: 90, //'right-top',
28523 //            7: 'right-bottom',
28524             8: 270 //'left-bottom'
28525     },
28526     
28527     exifTagTypes : {
28528         // byte, 8-bit unsigned int:
28529         1: {
28530             getValue: function (dataView, dataOffset) {
28531                 return dataView.getUint8(dataOffset);
28532             },
28533             size: 1
28534         },
28535         // ascii, 8-bit byte:
28536         2: {
28537             getValue: function (dataView, dataOffset) {
28538                 return String.fromCharCode(dataView.getUint8(dataOffset));
28539             },
28540             size: 1,
28541             ascii: true
28542         },
28543         // short, 16 bit int:
28544         3: {
28545             getValue: function (dataView, dataOffset, littleEndian) {
28546                 return dataView.getUint16(dataOffset, littleEndian);
28547             },
28548             size: 2
28549         },
28550         // long, 32 bit int:
28551         4: {
28552             getValue: function (dataView, dataOffset, littleEndian) {
28553                 return dataView.getUint32(dataOffset, littleEndian);
28554             },
28555             size: 4
28556         },
28557         // rational = two long values, first is numerator, second is denominator:
28558         5: {
28559             getValue: function (dataView, dataOffset, littleEndian) {
28560                 return dataView.getUint32(dataOffset, littleEndian) /
28561                     dataView.getUint32(dataOffset + 4, littleEndian);
28562             },
28563             size: 8
28564         },
28565         // slong, 32 bit signed int:
28566         9: {
28567             getValue: function (dataView, dataOffset, littleEndian) {
28568                 return dataView.getInt32(dataOffset, littleEndian);
28569             },
28570             size: 4
28571         },
28572         // srational, two slongs, first is numerator, second is denominator:
28573         10: {
28574             getValue: function (dataView, dataOffset, littleEndian) {
28575                 return dataView.getInt32(dataOffset, littleEndian) /
28576                     dataView.getInt32(dataOffset + 4, littleEndian);
28577             },
28578             size: 8
28579         }
28580     },
28581     
28582     footer : {
28583         STANDARD : [
28584             {
28585                 tag : 'div',
28586                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28587                 action : 'rotate-left',
28588                 cn : [
28589                     {
28590                         tag : 'button',
28591                         cls : 'btn btn-default',
28592                         html : '<i class="fa fa-undo"></i>'
28593                     }
28594                 ]
28595             },
28596             {
28597                 tag : 'div',
28598                 cls : 'btn-group roo-upload-cropbox-picture',
28599                 action : 'picture',
28600                 cn : [
28601                     {
28602                         tag : 'button',
28603                         cls : 'btn btn-default',
28604                         html : '<i class="fa fa-picture-o"></i>'
28605                     }
28606                 ]
28607             },
28608             {
28609                 tag : 'div',
28610                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28611                 action : 'rotate-right',
28612                 cn : [
28613                     {
28614                         tag : 'button',
28615                         cls : 'btn btn-default',
28616                         html : '<i class="fa fa-repeat"></i>'
28617                     }
28618                 ]
28619             }
28620         ],
28621         DOCUMENT : [
28622             {
28623                 tag : 'div',
28624                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28625                 action : 'rotate-left',
28626                 cn : [
28627                     {
28628                         tag : 'button',
28629                         cls : 'btn btn-default',
28630                         html : '<i class="fa fa-undo"></i>'
28631                     }
28632                 ]
28633             },
28634             {
28635                 tag : 'div',
28636                 cls : 'btn-group roo-upload-cropbox-download',
28637                 action : 'download',
28638                 cn : [
28639                     {
28640                         tag : 'button',
28641                         cls : 'btn btn-default',
28642                         html : '<i class="fa fa-download"></i>'
28643                     }
28644                 ]
28645             },
28646             {
28647                 tag : 'div',
28648                 cls : 'btn-group roo-upload-cropbox-crop',
28649                 action : 'crop',
28650                 cn : [
28651                     {
28652                         tag : 'button',
28653                         cls : 'btn btn-default',
28654                         html : '<i class="fa fa-crop"></i>'
28655                     }
28656                 ]
28657             },
28658             {
28659                 tag : 'div',
28660                 cls : 'btn-group roo-upload-cropbox-trash',
28661                 action : 'trash',
28662                 cn : [
28663                     {
28664                         tag : 'button',
28665                         cls : 'btn btn-default',
28666                         html : '<i class="fa fa-trash"></i>'
28667                     }
28668                 ]
28669             },
28670             {
28671                 tag : 'div',
28672                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28673                 action : 'rotate-right',
28674                 cn : [
28675                     {
28676                         tag : 'button',
28677                         cls : 'btn btn-default',
28678                         html : '<i class="fa fa-repeat"></i>'
28679                     }
28680                 ]
28681             }
28682         ],
28683         ROTATOR : [
28684             {
28685                 tag : 'div',
28686                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28687                 action : 'rotate-left',
28688                 cn : [
28689                     {
28690                         tag : 'button',
28691                         cls : 'btn btn-default',
28692                         html : '<i class="fa fa-undo"></i>'
28693                     }
28694                 ]
28695             },
28696             {
28697                 tag : 'div',
28698                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28699                 action : 'rotate-right',
28700                 cn : [
28701                     {
28702                         tag : 'button',
28703                         cls : 'btn btn-default',
28704                         html : '<i class="fa fa-repeat"></i>'
28705                     }
28706                 ]
28707             }
28708         ]
28709     }
28710 });
28711
28712 /*
28713 * Licence: LGPL
28714 */
28715
28716 /**
28717  * @class Roo.bootstrap.DocumentManager
28718  * @extends Roo.bootstrap.Component
28719  * Bootstrap DocumentManager class
28720  * @cfg {String} paramName default 'imageUpload'
28721  * @cfg {String} toolTipName default 'filename'
28722  * @cfg {String} method default POST
28723  * @cfg {String} url action url
28724  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28725  * @cfg {Boolean} multiple multiple upload default true
28726  * @cfg {Number} thumbSize default 300
28727  * @cfg {String} fieldLabel
28728  * @cfg {Number} labelWidth default 4
28729  * @cfg {String} labelAlign (left|top) default left
28730  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28731 * @cfg {Number} labellg set the width of label (1-12)
28732  * @cfg {Number} labelmd set the width of label (1-12)
28733  * @cfg {Number} labelsm set the width of label (1-12)
28734  * @cfg {Number} labelxs set the width of label (1-12)
28735  * 
28736  * @constructor
28737  * Create a new DocumentManager
28738  * @param {Object} config The config object
28739  */
28740
28741 Roo.bootstrap.DocumentManager = function(config){
28742     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28743     
28744     this.files = [];
28745     this.delegates = [];
28746     
28747     this.addEvents({
28748         /**
28749          * @event initial
28750          * Fire when initial the DocumentManager
28751          * @param {Roo.bootstrap.DocumentManager} this
28752          */
28753         "initial" : true,
28754         /**
28755          * @event inspect
28756          * inspect selected file
28757          * @param {Roo.bootstrap.DocumentManager} this
28758          * @param {File} file
28759          */
28760         "inspect" : true,
28761         /**
28762          * @event exception
28763          * Fire when xhr load exception
28764          * @param {Roo.bootstrap.DocumentManager} this
28765          * @param {XMLHttpRequest} xhr
28766          */
28767         "exception" : true,
28768         /**
28769          * @event afterupload
28770          * Fire when xhr load exception
28771          * @param {Roo.bootstrap.DocumentManager} this
28772          * @param {XMLHttpRequest} xhr
28773          */
28774         "afterupload" : true,
28775         /**
28776          * @event prepare
28777          * prepare the form data
28778          * @param {Roo.bootstrap.DocumentManager} this
28779          * @param {Object} formData
28780          */
28781         "prepare" : true,
28782         /**
28783          * @event remove
28784          * Fire when remove the file
28785          * @param {Roo.bootstrap.DocumentManager} this
28786          * @param {Object} file
28787          */
28788         "remove" : true,
28789         /**
28790          * @event refresh
28791          * Fire after refresh the file
28792          * @param {Roo.bootstrap.DocumentManager} this
28793          */
28794         "refresh" : true,
28795         /**
28796          * @event click
28797          * Fire after click the image
28798          * @param {Roo.bootstrap.DocumentManager} this
28799          * @param {Object} file
28800          */
28801         "click" : true,
28802         /**
28803          * @event edit
28804          * Fire when upload a image and editable set to true
28805          * @param {Roo.bootstrap.DocumentManager} this
28806          * @param {Object} file
28807          */
28808         "edit" : true,
28809         /**
28810          * @event beforeselectfile
28811          * Fire before select file
28812          * @param {Roo.bootstrap.DocumentManager} this
28813          */
28814         "beforeselectfile" : true,
28815         /**
28816          * @event process
28817          * Fire before process file
28818          * @param {Roo.bootstrap.DocumentManager} this
28819          * @param {Object} file
28820          */
28821         "process" : true,
28822         /**
28823          * @event previewrendered
28824          * Fire when preview rendered
28825          * @param {Roo.bootstrap.DocumentManager} this
28826          * @param {Object} file
28827          */
28828         "previewrendered" : true,
28829         /**
28830          */
28831         "previewResize" : true
28832         
28833     });
28834 };
28835
28836 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28837     
28838     boxes : 0,
28839     inputName : '',
28840     thumbSize : 300,
28841     multiple : true,
28842     files : false,
28843     method : 'POST',
28844     url : '',
28845     paramName : 'imageUpload',
28846     toolTipName : 'filename',
28847     fieldLabel : '',
28848     labelWidth : 4,
28849     labelAlign : 'left',
28850     editable : true,
28851     delegates : false,
28852     xhr : false, 
28853     
28854     labellg : 0,
28855     labelmd : 0,
28856     labelsm : 0,
28857     labelxs : 0,
28858     
28859     getAutoCreate : function()
28860     {   
28861         var managerWidget = {
28862             tag : 'div',
28863             cls : 'roo-document-manager',
28864             cn : [
28865                 {
28866                     tag : 'input',
28867                     cls : 'roo-document-manager-selector',
28868                     type : 'file'
28869                 },
28870                 {
28871                     tag : 'div',
28872                     cls : 'roo-document-manager-uploader',
28873                     cn : [
28874                         {
28875                             tag : 'div',
28876                             cls : 'roo-document-manager-upload-btn',
28877                             html : '<i class="fa fa-plus"></i>'
28878                         }
28879                     ]
28880                     
28881                 }
28882             ]
28883         };
28884         
28885         var content = [
28886             {
28887                 tag : 'div',
28888                 cls : 'column col-md-12',
28889                 cn : managerWidget
28890             }
28891         ];
28892         
28893         if(this.fieldLabel.length){
28894             
28895             content = [
28896                 {
28897                     tag : 'div',
28898                     cls : 'column col-md-12',
28899                     html : this.fieldLabel
28900                 },
28901                 {
28902                     tag : 'div',
28903                     cls : 'column col-md-12',
28904                     cn : managerWidget
28905                 }
28906             ];
28907
28908             if(this.labelAlign == 'left'){
28909                 content = [
28910                     {
28911                         tag : 'div',
28912                         cls : 'column',
28913                         html : this.fieldLabel
28914                     },
28915                     {
28916                         tag : 'div',
28917                         cls : 'column',
28918                         cn : managerWidget
28919                     }
28920                 ];
28921                 
28922                 if(this.labelWidth > 12){
28923                     content[0].style = "width: " + this.labelWidth + 'px';
28924                 }
28925
28926                 if(this.labelWidth < 13 && this.labelmd == 0){
28927                     this.labelmd = this.labelWidth;
28928                 }
28929
28930                 if(this.labellg > 0){
28931                     content[0].cls += ' col-lg-' + this.labellg;
28932                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28933                 }
28934
28935                 if(this.labelmd > 0){
28936                     content[0].cls += ' col-md-' + this.labelmd;
28937                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28938                 }
28939
28940                 if(this.labelsm > 0){
28941                     content[0].cls += ' col-sm-' + this.labelsm;
28942                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28943                 }
28944
28945                 if(this.labelxs > 0){
28946                     content[0].cls += ' col-xs-' + this.labelxs;
28947                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28948                 }
28949                 
28950             }
28951         }
28952         
28953         var cfg = {
28954             tag : 'div',
28955             cls : 'row clearfix',
28956             cn : content
28957         };
28958         
28959         return cfg;
28960         
28961     },
28962     
28963     initEvents : function()
28964     {
28965         this.managerEl = this.el.select('.roo-document-manager', true).first();
28966         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28967         
28968         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28969         this.selectorEl.hide();
28970         
28971         if(this.multiple){
28972             this.selectorEl.attr('multiple', 'multiple');
28973         }
28974         
28975         this.selectorEl.on('change', this.onFileSelected, this);
28976         
28977         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28978         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28979         
28980         this.uploader.on('click', this.onUploaderClick, this);
28981         
28982         this.renderProgressDialog();
28983         
28984         var _this = this;
28985         
28986         window.addEventListener("resize", function() { _this.refresh(); } );
28987         
28988         this.fireEvent('initial', this);
28989     },
28990     
28991     renderProgressDialog : function()
28992     {
28993         var _this = this;
28994         
28995         this.progressDialog = new Roo.bootstrap.Modal({
28996             cls : 'roo-document-manager-progress-dialog',
28997             allow_close : false,
28998             title : '',
28999             buttons : [
29000                 {
29001                     name  :'cancel',
29002                     weight : 'danger',
29003                     html : 'Cancel'
29004                 }
29005             ], 
29006             listeners : { 
29007                 btnclick : function() {
29008                     _this.uploadCancel();
29009                     this.hide();
29010                 }
29011             }
29012         });
29013          
29014         this.progressDialog.render(Roo.get(document.body));
29015          
29016         this.progress = new Roo.bootstrap.Progress({
29017             cls : 'roo-document-manager-progress',
29018             active : true,
29019             striped : true
29020         });
29021         
29022         this.progress.render(this.progressDialog.getChildContainer());
29023         
29024         this.progressBar = new Roo.bootstrap.ProgressBar({
29025             cls : 'roo-document-manager-progress-bar',
29026             aria_valuenow : 0,
29027             aria_valuemin : 0,
29028             aria_valuemax : 12,
29029             panel : 'success'
29030         });
29031         
29032         this.progressBar.render(this.progress.getChildContainer());
29033     },
29034     
29035     onUploaderClick : function(e)
29036     {
29037         e.preventDefault();
29038      
29039         if(this.fireEvent('beforeselectfile', this) != false){
29040             this.selectorEl.dom.click();
29041         }
29042         
29043     },
29044     
29045     onFileSelected : function(e)
29046     {
29047         e.preventDefault();
29048         
29049         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29050             return;
29051         }
29052         
29053         Roo.each(this.selectorEl.dom.files, function(file){
29054             if(this.fireEvent('inspect', this, file) != false){
29055                 this.files.push(file);
29056             }
29057         }, this);
29058         
29059         this.queue();
29060         
29061     },
29062     
29063     queue : function()
29064     {
29065         this.selectorEl.dom.value = '';
29066         
29067         if(!this.files || !this.files.length){
29068             return;
29069         }
29070         
29071         if(this.boxes > 0 && this.files.length > this.boxes){
29072             this.files = this.files.slice(0, this.boxes);
29073         }
29074         
29075         this.uploader.show();
29076         
29077         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29078             this.uploader.hide();
29079         }
29080         
29081         var _this = this;
29082         
29083         var files = [];
29084         
29085         var docs = [];
29086         
29087         Roo.each(this.files, function(file){
29088             
29089             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29090                 var f = this.renderPreview(file);
29091                 files.push(f);
29092                 return;
29093             }
29094             
29095             if(file.type.indexOf('image') != -1){
29096                 this.delegates.push(
29097                     (function(){
29098                         _this.process(file);
29099                     }).createDelegate(this)
29100                 );
29101         
29102                 return;
29103             }
29104             
29105             docs.push(
29106                 (function(){
29107                     _this.process(file);
29108                 }).createDelegate(this)
29109             );
29110             
29111         }, this);
29112         
29113         this.files = files;
29114         
29115         this.delegates = this.delegates.concat(docs);
29116         
29117         if(!this.delegates.length){
29118             this.refresh();
29119             return;
29120         }
29121         
29122         this.progressBar.aria_valuemax = this.delegates.length;
29123         
29124         this.arrange();
29125         
29126         return;
29127     },
29128     
29129     arrange : function()
29130     {
29131         if(!this.delegates.length){
29132             this.progressDialog.hide();
29133             this.refresh();
29134             return;
29135         }
29136         
29137         var delegate = this.delegates.shift();
29138         
29139         this.progressDialog.show();
29140         
29141         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29142         
29143         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29144         
29145         delegate();
29146     },
29147     
29148     refresh : function()
29149     {
29150         this.uploader.show();
29151         
29152         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29153             this.uploader.hide();
29154         }
29155         
29156         Roo.isTouch ? this.closable(false) : this.closable(true);
29157         
29158         this.fireEvent('refresh', this);
29159     },
29160     
29161     onRemove : function(e, el, o)
29162     {
29163         e.preventDefault();
29164         
29165         this.fireEvent('remove', this, o);
29166         
29167     },
29168     
29169     remove : function(o)
29170     {
29171         var files = [];
29172         
29173         Roo.each(this.files, function(file){
29174             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29175                 files.push(file);
29176                 return;
29177             }
29178
29179             o.target.remove();
29180
29181         }, this);
29182         
29183         this.files = files;
29184         
29185         this.refresh();
29186     },
29187     
29188     clear : function()
29189     {
29190         Roo.each(this.files, function(file){
29191             if(!file.target){
29192                 return;
29193             }
29194             
29195             file.target.remove();
29196
29197         }, this);
29198         
29199         this.files = [];
29200         
29201         this.refresh();
29202     },
29203     
29204     onClick : function(e, el, o)
29205     {
29206         e.preventDefault();
29207         
29208         this.fireEvent('click', this, o);
29209         
29210     },
29211     
29212     closable : function(closable)
29213     {
29214         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29215             
29216             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29217             
29218             if(closable){
29219                 el.show();
29220                 return;
29221             }
29222             
29223             el.hide();
29224             
29225         }, this);
29226     },
29227     
29228     xhrOnLoad : function(xhr)
29229     {
29230         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29231             el.remove();
29232         }, this);
29233         
29234         if (xhr.readyState !== 4) {
29235             this.arrange();
29236             this.fireEvent('exception', this, xhr);
29237             return;
29238         }
29239
29240         var response = Roo.decode(xhr.responseText);
29241         
29242         if(!response.success){
29243             this.arrange();
29244             this.fireEvent('exception', this, xhr);
29245             return;
29246         }
29247         
29248         var file = this.renderPreview(response.data);
29249         
29250         this.files.push(file);
29251         
29252         this.arrange();
29253         
29254         this.fireEvent('afterupload', this, xhr);
29255         
29256     },
29257     
29258     xhrOnError : function(xhr)
29259     {
29260         Roo.log('xhr on error');
29261         
29262         var response = Roo.decode(xhr.responseText);
29263           
29264         Roo.log(response);
29265         
29266         this.arrange();
29267     },
29268     
29269     process : function(file)
29270     {
29271         if(this.fireEvent('process', this, file) !== false){
29272             if(this.editable && file.type.indexOf('image') != -1){
29273                 this.fireEvent('edit', this, file);
29274                 return;
29275             }
29276
29277             this.uploadStart(file, false);
29278
29279             return;
29280         }
29281         
29282     },
29283     
29284     uploadStart : function(file, crop)
29285     {
29286         this.xhr = new XMLHttpRequest();
29287         
29288         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29289             this.arrange();
29290             return;
29291         }
29292         
29293         file.xhr = this.xhr;
29294             
29295         this.managerEl.createChild({
29296             tag : 'div',
29297             cls : 'roo-document-manager-loading',
29298             cn : [
29299                 {
29300                     tag : 'div',
29301                     tooltip : file.name,
29302                     cls : 'roo-document-manager-thumb',
29303                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29304                 }
29305             ]
29306
29307         });
29308
29309         this.xhr.open(this.method, this.url, true);
29310         
29311         var headers = {
29312             "Accept": "application/json",
29313             "Cache-Control": "no-cache",
29314             "X-Requested-With": "XMLHttpRequest"
29315         };
29316         
29317         for (var headerName in headers) {
29318             var headerValue = headers[headerName];
29319             if (headerValue) {
29320                 this.xhr.setRequestHeader(headerName, headerValue);
29321             }
29322         }
29323         
29324         var _this = this;
29325         
29326         this.xhr.onload = function()
29327         {
29328             _this.xhrOnLoad(_this.xhr);
29329         }
29330         
29331         this.xhr.onerror = function()
29332         {
29333             _this.xhrOnError(_this.xhr);
29334         }
29335         
29336         var formData = new FormData();
29337
29338         formData.append('returnHTML', 'NO');
29339         
29340         if(crop){
29341             formData.append('crop', crop);
29342         }
29343         
29344         formData.append(this.paramName, file, file.name);
29345         
29346         var options = {
29347             file : file, 
29348             manually : false
29349         };
29350         
29351         if(this.fireEvent('prepare', this, formData, options) != false){
29352             
29353             if(options.manually){
29354                 return;
29355             }
29356             
29357             this.xhr.send(formData);
29358             return;
29359         };
29360         
29361         this.uploadCancel();
29362     },
29363     
29364     uploadCancel : function()
29365     {
29366         if (this.xhr) {
29367             this.xhr.abort();
29368         }
29369         
29370         this.delegates = [];
29371         
29372         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29373             el.remove();
29374         }, this);
29375         
29376         this.arrange();
29377     },
29378     
29379     renderPreview : function(file)
29380     {
29381         if(typeof(file.target) != 'undefined' && file.target){
29382             return file;
29383         }
29384         
29385         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29386         
29387         var previewEl = this.managerEl.createChild({
29388             tag : 'div',
29389             cls : 'roo-document-manager-preview',
29390             cn : [
29391                 {
29392                     tag : 'div',
29393                     tooltip : file[this.toolTipName],
29394                     cls : 'roo-document-manager-thumb',
29395                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29396                 },
29397                 {
29398                     tag : 'button',
29399                     cls : 'close',
29400                     html : '<i class="fa fa-times-circle"></i>'
29401                 }
29402             ]
29403         });
29404
29405         var close = previewEl.select('button.close', true).first();
29406
29407         close.on('click', this.onRemove, this, file);
29408
29409         file.target = previewEl;
29410
29411         var image = previewEl.select('img', true).first();
29412         
29413         var _this = this;
29414         
29415         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29416         
29417         image.on('click', this.onClick, this, file);
29418         
29419         this.fireEvent('previewrendered', this, file);
29420         
29421         return file;
29422         
29423     },
29424     
29425     onPreviewLoad : function(file, image)
29426     {
29427         if(typeof(file.target) == 'undefined' || !file.target){
29428             return;
29429         }
29430         
29431         var width = image.dom.naturalWidth || image.dom.width;
29432         var height = image.dom.naturalHeight || image.dom.height;
29433         
29434         if(!this.previewResize) {
29435             return;
29436         }
29437         
29438         if(width > height){
29439             file.target.addClass('wide');
29440             return;
29441         }
29442         
29443         file.target.addClass('tall');
29444         return;
29445         
29446     },
29447     
29448     uploadFromSource : function(file, crop)
29449     {
29450         this.xhr = new XMLHttpRequest();
29451         
29452         this.managerEl.createChild({
29453             tag : 'div',
29454             cls : 'roo-document-manager-loading',
29455             cn : [
29456                 {
29457                     tag : 'div',
29458                     tooltip : file.name,
29459                     cls : 'roo-document-manager-thumb',
29460                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29461                 }
29462             ]
29463
29464         });
29465
29466         this.xhr.open(this.method, this.url, true);
29467         
29468         var headers = {
29469             "Accept": "application/json",
29470             "Cache-Control": "no-cache",
29471             "X-Requested-With": "XMLHttpRequest"
29472         };
29473         
29474         for (var headerName in headers) {
29475             var headerValue = headers[headerName];
29476             if (headerValue) {
29477                 this.xhr.setRequestHeader(headerName, headerValue);
29478             }
29479         }
29480         
29481         var _this = this;
29482         
29483         this.xhr.onload = function()
29484         {
29485             _this.xhrOnLoad(_this.xhr);
29486         }
29487         
29488         this.xhr.onerror = function()
29489         {
29490             _this.xhrOnError(_this.xhr);
29491         }
29492         
29493         var formData = new FormData();
29494
29495         formData.append('returnHTML', 'NO');
29496         
29497         formData.append('crop', crop);
29498         
29499         if(typeof(file.filename) != 'undefined'){
29500             formData.append('filename', file.filename);
29501         }
29502         
29503         if(typeof(file.mimetype) != 'undefined'){
29504             formData.append('mimetype', file.mimetype);
29505         }
29506         
29507         Roo.log(formData);
29508         
29509         if(this.fireEvent('prepare', this, formData) != false){
29510             this.xhr.send(formData);
29511         };
29512     }
29513 });
29514
29515 /*
29516 * Licence: LGPL
29517 */
29518
29519 /**
29520  * @class Roo.bootstrap.DocumentViewer
29521  * @extends Roo.bootstrap.Component
29522  * Bootstrap DocumentViewer class
29523  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29524  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29525  * 
29526  * @constructor
29527  * Create a new DocumentViewer
29528  * @param {Object} config The config object
29529  */
29530
29531 Roo.bootstrap.DocumentViewer = function(config){
29532     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29533     
29534     this.addEvents({
29535         /**
29536          * @event initial
29537          * Fire after initEvent
29538          * @param {Roo.bootstrap.DocumentViewer} this
29539          */
29540         "initial" : true,
29541         /**
29542          * @event click
29543          * Fire after click
29544          * @param {Roo.bootstrap.DocumentViewer} this
29545          */
29546         "click" : true,
29547         /**
29548          * @event download
29549          * Fire after download button
29550          * @param {Roo.bootstrap.DocumentViewer} this
29551          */
29552         "download" : true,
29553         /**
29554          * @event trash
29555          * Fire after trash button
29556          * @param {Roo.bootstrap.DocumentViewer} this
29557          */
29558         "trash" : true
29559         
29560     });
29561 };
29562
29563 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29564     
29565     showDownload : true,
29566     
29567     showTrash : true,
29568     
29569     getAutoCreate : function()
29570     {
29571         var cfg = {
29572             tag : 'div',
29573             cls : 'roo-document-viewer',
29574             cn : [
29575                 {
29576                     tag : 'div',
29577                     cls : 'roo-document-viewer-body',
29578                     cn : [
29579                         {
29580                             tag : 'div',
29581                             cls : 'roo-document-viewer-thumb',
29582                             cn : [
29583                                 {
29584                                     tag : 'img',
29585                                     cls : 'roo-document-viewer-image'
29586                                 }
29587                             ]
29588                         }
29589                     ]
29590                 },
29591                 {
29592                     tag : 'div',
29593                     cls : 'roo-document-viewer-footer',
29594                     cn : {
29595                         tag : 'div',
29596                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29597                         cn : [
29598                             {
29599                                 tag : 'div',
29600                                 cls : 'btn-group roo-document-viewer-download',
29601                                 cn : [
29602                                     {
29603                                         tag : 'button',
29604                                         cls : 'btn btn-default',
29605                                         html : '<i class="fa fa-download"></i>'
29606                                     }
29607                                 ]
29608                             },
29609                             {
29610                                 tag : 'div',
29611                                 cls : 'btn-group roo-document-viewer-trash',
29612                                 cn : [
29613                                     {
29614                                         tag : 'button',
29615                                         cls : 'btn btn-default',
29616                                         html : '<i class="fa fa-trash"></i>'
29617                                     }
29618                                 ]
29619                             }
29620                         ]
29621                     }
29622                 }
29623             ]
29624         };
29625         
29626         return cfg;
29627     },
29628     
29629     initEvents : function()
29630     {
29631         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29632         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29633         
29634         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29635         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29636         
29637         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29638         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29639         
29640         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29641         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29642         
29643         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29644         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29645         
29646         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29647         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29648         
29649         this.bodyEl.on('click', this.onClick, this);
29650         this.downloadBtn.on('click', this.onDownload, this);
29651         this.trashBtn.on('click', this.onTrash, this);
29652         
29653         this.downloadBtn.hide();
29654         this.trashBtn.hide();
29655         
29656         if(this.showDownload){
29657             this.downloadBtn.show();
29658         }
29659         
29660         if(this.showTrash){
29661             this.trashBtn.show();
29662         }
29663         
29664         if(!this.showDownload && !this.showTrash) {
29665             this.footerEl.hide();
29666         }
29667         
29668     },
29669     
29670     initial : function()
29671     {
29672         this.fireEvent('initial', this);
29673         
29674     },
29675     
29676     onClick : function(e)
29677     {
29678         e.preventDefault();
29679         
29680         this.fireEvent('click', this);
29681     },
29682     
29683     onDownload : function(e)
29684     {
29685         e.preventDefault();
29686         
29687         this.fireEvent('download', this);
29688     },
29689     
29690     onTrash : function(e)
29691     {
29692         e.preventDefault();
29693         
29694         this.fireEvent('trash', this);
29695     }
29696     
29697 });
29698 /*
29699  * - LGPL
29700  *
29701  * nav progress bar
29702  * 
29703  */
29704
29705 /**
29706  * @class Roo.bootstrap.NavProgressBar
29707  * @extends Roo.bootstrap.Component
29708  * Bootstrap NavProgressBar class
29709  * 
29710  * @constructor
29711  * Create a new nav progress bar
29712  * @param {Object} config The config object
29713  */
29714
29715 Roo.bootstrap.NavProgressBar = function(config){
29716     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29717
29718     this.bullets = this.bullets || [];
29719    
29720 //    Roo.bootstrap.NavProgressBar.register(this);
29721      this.addEvents({
29722         /**
29723              * @event changed
29724              * Fires when the active item changes
29725              * @param {Roo.bootstrap.NavProgressBar} this
29726              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29727              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29728          */
29729         'changed': true
29730      });
29731     
29732 };
29733
29734 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29735     
29736     bullets : [],
29737     barItems : [],
29738     
29739     getAutoCreate : function()
29740     {
29741         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29742         
29743         cfg = {
29744             tag : 'div',
29745             cls : 'roo-navigation-bar-group',
29746             cn : [
29747                 {
29748                     tag : 'div',
29749                     cls : 'roo-navigation-top-bar'
29750                 },
29751                 {
29752                     tag : 'div',
29753                     cls : 'roo-navigation-bullets-bar',
29754                     cn : [
29755                         {
29756                             tag : 'ul',
29757                             cls : 'roo-navigation-bar'
29758                         }
29759                     ]
29760                 },
29761                 
29762                 {
29763                     tag : 'div',
29764                     cls : 'roo-navigation-bottom-bar'
29765                 }
29766             ]
29767             
29768         };
29769         
29770         return cfg;
29771         
29772     },
29773     
29774     initEvents: function() 
29775     {
29776         
29777     },
29778     
29779     onRender : function(ct, position) 
29780     {
29781         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29782         
29783         if(this.bullets.length){
29784             Roo.each(this.bullets, function(b){
29785                this.addItem(b);
29786             }, this);
29787         }
29788         
29789         this.format();
29790         
29791     },
29792     
29793     addItem : function(cfg)
29794     {
29795         var item = new Roo.bootstrap.NavProgressItem(cfg);
29796         
29797         item.parentId = this.id;
29798         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29799         
29800         if(cfg.html){
29801             var top = new Roo.bootstrap.Element({
29802                 tag : 'div',
29803                 cls : 'roo-navigation-bar-text'
29804             });
29805             
29806             var bottom = new Roo.bootstrap.Element({
29807                 tag : 'div',
29808                 cls : 'roo-navigation-bar-text'
29809             });
29810             
29811             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29812             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29813             
29814             var topText = new Roo.bootstrap.Element({
29815                 tag : 'span',
29816                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29817             });
29818             
29819             var bottomText = new Roo.bootstrap.Element({
29820                 tag : 'span',
29821                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29822             });
29823             
29824             topText.onRender(top.el, null);
29825             bottomText.onRender(bottom.el, null);
29826             
29827             item.topEl = top;
29828             item.bottomEl = bottom;
29829         }
29830         
29831         this.barItems.push(item);
29832         
29833         return item;
29834     },
29835     
29836     getActive : function()
29837     {
29838         var active = false;
29839         
29840         Roo.each(this.barItems, function(v){
29841             
29842             if (!v.isActive()) {
29843                 return;
29844             }
29845             
29846             active = v;
29847             return false;
29848             
29849         });
29850         
29851         return active;
29852     },
29853     
29854     setActiveItem : function(item)
29855     {
29856         var prev = false;
29857         
29858         Roo.each(this.barItems, function(v){
29859             if (v.rid == item.rid) {
29860                 return ;
29861             }
29862             
29863             if (v.isActive()) {
29864                 v.setActive(false);
29865                 prev = v;
29866             }
29867         });
29868
29869         item.setActive(true);
29870         
29871         this.fireEvent('changed', this, item, prev);
29872     },
29873     
29874     getBarItem: function(rid)
29875     {
29876         var ret = false;
29877         
29878         Roo.each(this.barItems, function(e) {
29879             if (e.rid != rid) {
29880                 return;
29881             }
29882             
29883             ret =  e;
29884             return false;
29885         });
29886         
29887         return ret;
29888     },
29889     
29890     indexOfItem : function(item)
29891     {
29892         var index = false;
29893         
29894         Roo.each(this.barItems, function(v, i){
29895             
29896             if (v.rid != item.rid) {
29897                 return;
29898             }
29899             
29900             index = i;
29901             return false
29902         });
29903         
29904         return index;
29905     },
29906     
29907     setActiveNext : function()
29908     {
29909         var i = this.indexOfItem(this.getActive());
29910         
29911         if (i > this.barItems.length) {
29912             return;
29913         }
29914         
29915         this.setActiveItem(this.barItems[i+1]);
29916     },
29917     
29918     setActivePrev : function()
29919     {
29920         var i = this.indexOfItem(this.getActive());
29921         
29922         if (i  < 1) {
29923             return;
29924         }
29925         
29926         this.setActiveItem(this.barItems[i-1]);
29927     },
29928     
29929     format : function()
29930     {
29931         if(!this.barItems.length){
29932             return;
29933         }
29934      
29935         var width = 100 / this.barItems.length;
29936         
29937         Roo.each(this.barItems, function(i){
29938             i.el.setStyle('width', width + '%');
29939             i.topEl.el.setStyle('width', width + '%');
29940             i.bottomEl.el.setStyle('width', width + '%');
29941         }, this);
29942         
29943     }
29944     
29945 });
29946 /*
29947  * - LGPL
29948  *
29949  * Nav Progress Item
29950  * 
29951  */
29952
29953 /**
29954  * @class Roo.bootstrap.NavProgressItem
29955  * @extends Roo.bootstrap.Component
29956  * Bootstrap NavProgressItem class
29957  * @cfg {String} rid the reference id
29958  * @cfg {Boolean} active (true|false) Is item active default false
29959  * @cfg {Boolean} disabled (true|false) Is item active default false
29960  * @cfg {String} html
29961  * @cfg {String} position (top|bottom) text position default bottom
29962  * @cfg {String} icon show icon instead of number
29963  * 
29964  * @constructor
29965  * Create a new NavProgressItem
29966  * @param {Object} config The config object
29967  */
29968 Roo.bootstrap.NavProgressItem = function(config){
29969     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29970     this.addEvents({
29971         // raw events
29972         /**
29973          * @event click
29974          * The raw click event for the entire grid.
29975          * @param {Roo.bootstrap.NavProgressItem} this
29976          * @param {Roo.EventObject} e
29977          */
29978         "click" : true
29979     });
29980    
29981 };
29982
29983 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29984     
29985     rid : '',
29986     active : false,
29987     disabled : false,
29988     html : '',
29989     position : 'bottom',
29990     icon : false,
29991     
29992     getAutoCreate : function()
29993     {
29994         var iconCls = 'roo-navigation-bar-item-icon';
29995         
29996         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29997         
29998         var cfg = {
29999             tag: 'li',
30000             cls: 'roo-navigation-bar-item',
30001             cn : [
30002                 {
30003                     tag : 'i',
30004                     cls : iconCls
30005                 }
30006             ]
30007         };
30008         
30009         if(this.active){
30010             cfg.cls += ' active';
30011         }
30012         if(this.disabled){
30013             cfg.cls += ' disabled';
30014         }
30015         
30016         return cfg;
30017     },
30018     
30019     disable : function()
30020     {
30021         this.setDisabled(true);
30022     },
30023     
30024     enable : function()
30025     {
30026         this.setDisabled(false);
30027     },
30028     
30029     initEvents: function() 
30030     {
30031         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30032         
30033         this.iconEl.on('click', this.onClick, this);
30034     },
30035     
30036     onClick : function(e)
30037     {
30038         e.preventDefault();
30039         
30040         if(this.disabled){
30041             return;
30042         }
30043         
30044         if(this.fireEvent('click', this, e) === false){
30045             return;
30046         };
30047         
30048         this.parent().setActiveItem(this);
30049     },
30050     
30051     isActive: function () 
30052     {
30053         return this.active;
30054     },
30055     
30056     setActive : function(state)
30057     {
30058         if(this.active == state){
30059             return;
30060         }
30061         
30062         this.active = state;
30063         
30064         if (state) {
30065             this.el.addClass('active');
30066             return;
30067         }
30068         
30069         this.el.removeClass('active');
30070         
30071         return;
30072     },
30073     
30074     setDisabled : function(state)
30075     {
30076         if(this.disabled == state){
30077             return;
30078         }
30079         
30080         this.disabled = state;
30081         
30082         if (state) {
30083             this.el.addClass('disabled');
30084             return;
30085         }
30086         
30087         this.el.removeClass('disabled');
30088     },
30089     
30090     tooltipEl : function()
30091     {
30092         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30093     }
30094 });
30095  
30096
30097  /*
30098  * - LGPL
30099  *
30100  * FieldLabel
30101  * 
30102  */
30103
30104 /**
30105  * @class Roo.bootstrap.FieldLabel
30106  * @extends Roo.bootstrap.Component
30107  * Bootstrap FieldLabel class
30108  * @cfg {String} html contents of the element
30109  * @cfg {String} tag tag of the element default label
30110  * @cfg {String} cls class of the element
30111  * @cfg {String} target label target 
30112  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30113  * @cfg {String} invalidClass default "text-warning"
30114  * @cfg {String} validClass default "text-success"
30115  * @cfg {String} iconTooltip default "This field is required"
30116  * @cfg {String} indicatorpos (left|right) default left
30117  * 
30118  * @constructor
30119  * Create a new FieldLabel
30120  * @param {Object} config The config object
30121  */
30122
30123 Roo.bootstrap.FieldLabel = function(config){
30124     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30125     
30126     this.addEvents({
30127             /**
30128              * @event invalid
30129              * Fires after the field has been marked as invalid.
30130              * @param {Roo.form.FieldLabel} this
30131              * @param {String} msg The validation message
30132              */
30133             invalid : true,
30134             /**
30135              * @event valid
30136              * Fires after the field has been validated with no errors.
30137              * @param {Roo.form.FieldLabel} this
30138              */
30139             valid : true
30140         });
30141 };
30142
30143 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30144     
30145     tag: 'label',
30146     cls: '',
30147     html: '',
30148     target: '',
30149     allowBlank : true,
30150     invalidClass : 'has-warning',
30151     validClass : 'has-success',
30152     iconTooltip : 'This field is required',
30153     indicatorpos : 'left',
30154     
30155     getAutoCreate : function(){
30156         
30157         var cls = "";
30158         if (!this.allowBlank) {
30159             cls  = "visible";
30160         }
30161         
30162         var cfg = {
30163             tag : this.tag,
30164             cls : 'roo-bootstrap-field-label ' + this.cls,
30165             for : this.target,
30166             cn : [
30167                 {
30168                     tag : 'i',
30169                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30170                     tooltip : this.iconTooltip
30171                 },
30172                 {
30173                     tag : 'span',
30174                     html : this.html
30175                 }
30176             ] 
30177         };
30178         
30179         if(this.indicatorpos == 'right'){
30180             var cfg = {
30181                 tag : this.tag,
30182                 cls : 'roo-bootstrap-field-label ' + this.cls,
30183                 for : this.target,
30184                 cn : [
30185                     {
30186                         tag : 'span',
30187                         html : this.html
30188                     },
30189                     {
30190                         tag : 'i',
30191                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30192                         tooltip : this.iconTooltip
30193                     }
30194                 ] 
30195             };
30196         }
30197         
30198         return cfg;
30199     },
30200     
30201     initEvents: function() 
30202     {
30203         Roo.bootstrap.Element.superclass.initEvents.call(this);
30204         
30205         this.indicator = this.indicatorEl();
30206         
30207         if(this.indicator){
30208             this.indicator.removeClass('visible');
30209             this.indicator.addClass('invisible');
30210         }
30211         
30212         Roo.bootstrap.FieldLabel.register(this);
30213     },
30214     
30215     indicatorEl : function()
30216     {
30217         var indicator = this.el.select('i.roo-required-indicator',true).first();
30218         
30219         if(!indicator){
30220             return false;
30221         }
30222         
30223         return indicator;
30224         
30225     },
30226     
30227     /**
30228      * Mark this field as valid
30229      */
30230     markValid : function()
30231     {
30232         if(this.indicator){
30233             this.indicator.removeClass('visible');
30234             this.indicator.addClass('invisible');
30235         }
30236         
30237         this.el.removeClass(this.invalidClass);
30238         
30239         this.el.addClass(this.validClass);
30240         
30241         this.fireEvent('valid', this);
30242     },
30243     
30244     /**
30245      * Mark this field as invalid
30246      * @param {String} msg The validation message
30247      */
30248     markInvalid : function(msg)
30249     {
30250         if(this.indicator){
30251             this.indicator.removeClass('invisible');
30252             this.indicator.addClass('visible');
30253         }
30254         
30255         this.el.removeClass(this.validClass);
30256         
30257         this.el.addClass(this.invalidClass);
30258         
30259         this.fireEvent('invalid', this, msg);
30260     }
30261     
30262    
30263 });
30264
30265 Roo.apply(Roo.bootstrap.FieldLabel, {
30266     
30267     groups: {},
30268     
30269      /**
30270     * register a FieldLabel Group
30271     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30272     */
30273     register : function(label)
30274     {
30275         if(this.groups.hasOwnProperty(label.target)){
30276             return;
30277         }
30278      
30279         this.groups[label.target] = label;
30280         
30281     },
30282     /**
30283     * fetch a FieldLabel Group based on the target
30284     * @param {string} target
30285     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30286     */
30287     get: function(target) {
30288         if (typeof(this.groups[target]) == 'undefined') {
30289             return false;
30290         }
30291         
30292         return this.groups[target] ;
30293     }
30294 });
30295
30296  
30297
30298  /*
30299  * - LGPL
30300  *
30301  * page DateSplitField.
30302  * 
30303  */
30304
30305
30306 /**
30307  * @class Roo.bootstrap.DateSplitField
30308  * @extends Roo.bootstrap.Component
30309  * Bootstrap DateSplitField class
30310  * @cfg {string} fieldLabel - the label associated
30311  * @cfg {Number} labelWidth set the width of label (0-12)
30312  * @cfg {String} labelAlign (top|left)
30313  * @cfg {Boolean} dayAllowBlank (true|false) default false
30314  * @cfg {Boolean} monthAllowBlank (true|false) default false
30315  * @cfg {Boolean} yearAllowBlank (true|false) default false
30316  * @cfg {string} dayPlaceholder 
30317  * @cfg {string} monthPlaceholder
30318  * @cfg {string} yearPlaceholder
30319  * @cfg {string} dayFormat default 'd'
30320  * @cfg {string} monthFormat default 'm'
30321  * @cfg {string} yearFormat default 'Y'
30322  * @cfg {Number} labellg set the width of label (1-12)
30323  * @cfg {Number} labelmd set the width of label (1-12)
30324  * @cfg {Number} labelsm set the width of label (1-12)
30325  * @cfg {Number} labelxs set the width of label (1-12)
30326
30327  *     
30328  * @constructor
30329  * Create a new DateSplitField
30330  * @param {Object} config The config object
30331  */
30332
30333 Roo.bootstrap.DateSplitField = function(config){
30334     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30335     
30336     this.addEvents({
30337         // raw events
30338          /**
30339          * @event years
30340          * getting the data of years
30341          * @param {Roo.bootstrap.DateSplitField} this
30342          * @param {Object} years
30343          */
30344         "years" : true,
30345         /**
30346          * @event days
30347          * getting the data of days
30348          * @param {Roo.bootstrap.DateSplitField} this
30349          * @param {Object} days
30350          */
30351         "days" : true,
30352         /**
30353          * @event invalid
30354          * Fires after the field has been marked as invalid.
30355          * @param {Roo.form.Field} this
30356          * @param {String} msg The validation message
30357          */
30358         invalid : true,
30359        /**
30360          * @event valid
30361          * Fires after the field has been validated with no errors.
30362          * @param {Roo.form.Field} this
30363          */
30364         valid : true
30365     });
30366 };
30367
30368 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30369     
30370     fieldLabel : '',
30371     labelAlign : 'top',
30372     labelWidth : 3,
30373     dayAllowBlank : false,
30374     monthAllowBlank : false,
30375     yearAllowBlank : false,
30376     dayPlaceholder : '',
30377     monthPlaceholder : '',
30378     yearPlaceholder : '',
30379     dayFormat : 'd',
30380     monthFormat : 'm',
30381     yearFormat : 'Y',
30382     isFormField : true,
30383     labellg : 0,
30384     labelmd : 0,
30385     labelsm : 0,
30386     labelxs : 0,
30387     
30388     getAutoCreate : function()
30389     {
30390         var cfg = {
30391             tag : 'div',
30392             cls : 'row roo-date-split-field-group',
30393             cn : [
30394                 {
30395                     tag : 'input',
30396                     type : 'hidden',
30397                     cls : 'form-hidden-field roo-date-split-field-group-value',
30398                     name : this.name
30399                 }
30400             ]
30401         };
30402         
30403         var labelCls = 'col-md-12';
30404         var contentCls = 'col-md-4';
30405         
30406         if(this.fieldLabel){
30407             
30408             var label = {
30409                 tag : 'div',
30410                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30411                 cn : [
30412                     {
30413                         tag : 'label',
30414                         html : this.fieldLabel
30415                     }
30416                 ]
30417             };
30418             
30419             if(this.labelAlign == 'left'){
30420             
30421                 if(this.labelWidth > 12){
30422                     label.style = "width: " + this.labelWidth + 'px';
30423                 }
30424
30425                 if(this.labelWidth < 13 && this.labelmd == 0){
30426                     this.labelmd = this.labelWidth;
30427                 }
30428
30429                 if(this.labellg > 0){
30430                     labelCls = ' col-lg-' + this.labellg;
30431                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30432                 }
30433
30434                 if(this.labelmd > 0){
30435                     labelCls = ' col-md-' + this.labelmd;
30436                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30437                 }
30438
30439                 if(this.labelsm > 0){
30440                     labelCls = ' col-sm-' + this.labelsm;
30441                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30442                 }
30443
30444                 if(this.labelxs > 0){
30445                     labelCls = ' col-xs-' + this.labelxs;
30446                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30447                 }
30448             }
30449             
30450             label.cls += ' ' + labelCls;
30451             
30452             cfg.cn.push(label);
30453         }
30454         
30455         Roo.each(['day', 'month', 'year'], function(t){
30456             cfg.cn.push({
30457                 tag : 'div',
30458                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30459             });
30460         }, this);
30461         
30462         return cfg;
30463     },
30464     
30465     inputEl: function ()
30466     {
30467         return this.el.select('.roo-date-split-field-group-value', true).first();
30468     },
30469     
30470     onRender : function(ct, position) 
30471     {
30472         var _this = this;
30473         
30474         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30475         
30476         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30477         
30478         this.dayField = new Roo.bootstrap.ComboBox({
30479             allowBlank : this.dayAllowBlank,
30480             alwaysQuery : true,
30481             displayField : 'value',
30482             editable : false,
30483             fieldLabel : '',
30484             forceSelection : true,
30485             mode : 'local',
30486             placeholder : this.dayPlaceholder,
30487             selectOnFocus : true,
30488             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30489             triggerAction : 'all',
30490             typeAhead : true,
30491             valueField : 'value',
30492             store : new Roo.data.SimpleStore({
30493                 data : (function() {    
30494                     var days = [];
30495                     _this.fireEvent('days', _this, days);
30496                     return days;
30497                 })(),
30498                 fields : [ 'value' ]
30499             }),
30500             listeners : {
30501                 select : function (_self, record, index)
30502                 {
30503                     _this.setValue(_this.getValue());
30504                 }
30505             }
30506         });
30507
30508         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30509         
30510         this.monthField = new Roo.bootstrap.MonthField({
30511             after : '<i class=\"fa fa-calendar\"></i>',
30512             allowBlank : this.monthAllowBlank,
30513             placeholder : this.monthPlaceholder,
30514             readOnly : true,
30515             listeners : {
30516                 render : function (_self)
30517                 {
30518                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30519                         e.preventDefault();
30520                         _self.focus();
30521                     });
30522                 },
30523                 select : function (_self, oldvalue, newvalue)
30524                 {
30525                     _this.setValue(_this.getValue());
30526                 }
30527             }
30528         });
30529         
30530         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30531         
30532         this.yearField = new Roo.bootstrap.ComboBox({
30533             allowBlank : this.yearAllowBlank,
30534             alwaysQuery : true,
30535             displayField : 'value',
30536             editable : false,
30537             fieldLabel : '',
30538             forceSelection : true,
30539             mode : 'local',
30540             placeholder : this.yearPlaceholder,
30541             selectOnFocus : true,
30542             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30543             triggerAction : 'all',
30544             typeAhead : true,
30545             valueField : 'value',
30546             store : new Roo.data.SimpleStore({
30547                 data : (function() {
30548                     var years = [];
30549                     _this.fireEvent('years', _this, years);
30550                     return years;
30551                 })(),
30552                 fields : [ 'value' ]
30553             }),
30554             listeners : {
30555                 select : function (_self, record, index)
30556                 {
30557                     _this.setValue(_this.getValue());
30558                 }
30559             }
30560         });
30561
30562         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30563     },
30564     
30565     setValue : function(v, format)
30566     {
30567         this.inputEl.dom.value = v;
30568         
30569         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30570         
30571         var d = Date.parseDate(v, f);
30572         
30573         if(!d){
30574             this.validate();
30575             return;
30576         }
30577         
30578         this.setDay(d.format(this.dayFormat));
30579         this.setMonth(d.format(this.monthFormat));
30580         this.setYear(d.format(this.yearFormat));
30581         
30582         this.validate();
30583         
30584         return;
30585     },
30586     
30587     setDay : function(v)
30588     {
30589         this.dayField.setValue(v);
30590         this.inputEl.dom.value = this.getValue();
30591         this.validate();
30592         return;
30593     },
30594     
30595     setMonth : function(v)
30596     {
30597         this.monthField.setValue(v, true);
30598         this.inputEl.dom.value = this.getValue();
30599         this.validate();
30600         return;
30601     },
30602     
30603     setYear : function(v)
30604     {
30605         this.yearField.setValue(v);
30606         this.inputEl.dom.value = this.getValue();
30607         this.validate();
30608         return;
30609     },
30610     
30611     getDay : function()
30612     {
30613         return this.dayField.getValue();
30614     },
30615     
30616     getMonth : function()
30617     {
30618         return this.monthField.getValue();
30619     },
30620     
30621     getYear : function()
30622     {
30623         return this.yearField.getValue();
30624     },
30625     
30626     getValue : function()
30627     {
30628         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30629         
30630         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30631         
30632         return date;
30633     },
30634     
30635     reset : function()
30636     {
30637         this.setDay('');
30638         this.setMonth('');
30639         this.setYear('');
30640         this.inputEl.dom.value = '';
30641         this.validate();
30642         return;
30643     },
30644     
30645     validate : function()
30646     {
30647         var d = this.dayField.validate();
30648         var m = this.monthField.validate();
30649         var y = this.yearField.validate();
30650         
30651         var valid = true;
30652         
30653         if(
30654                 (!this.dayAllowBlank && !d) ||
30655                 (!this.monthAllowBlank && !m) ||
30656                 (!this.yearAllowBlank && !y)
30657         ){
30658             valid = false;
30659         }
30660         
30661         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30662             return valid;
30663         }
30664         
30665         if(valid){
30666             this.markValid();
30667             return valid;
30668         }
30669         
30670         this.markInvalid();
30671         
30672         return valid;
30673     },
30674     
30675     markValid : function()
30676     {
30677         
30678         var label = this.el.select('label', true).first();
30679         var icon = this.el.select('i.fa-star', true).first();
30680
30681         if(label && icon){
30682             icon.remove();
30683         }
30684         
30685         this.fireEvent('valid', this);
30686     },
30687     
30688      /**
30689      * Mark this field as invalid
30690      * @param {String} msg The validation message
30691      */
30692     markInvalid : function(msg)
30693     {
30694         
30695         var label = this.el.select('label', true).first();
30696         var icon = this.el.select('i.fa-star', true).first();
30697
30698         if(label && !icon){
30699             this.el.select('.roo-date-split-field-label', true).createChild({
30700                 tag : 'i',
30701                 cls : 'text-danger fa fa-lg fa-star',
30702                 tooltip : 'This field is required',
30703                 style : 'margin-right:5px;'
30704             }, label, true);
30705         }
30706         
30707         this.fireEvent('invalid', this, msg);
30708     },
30709     
30710     clearInvalid : function()
30711     {
30712         var label = this.el.select('label', true).first();
30713         var icon = this.el.select('i.fa-star', true).first();
30714
30715         if(label && icon){
30716             icon.remove();
30717         }
30718         
30719         this.fireEvent('valid', this);
30720     },
30721     
30722     getName: function()
30723     {
30724         return this.name;
30725     }
30726     
30727 });
30728
30729  /**
30730  *
30731  * This is based on 
30732  * http://masonry.desandro.com
30733  *
30734  * The idea is to render all the bricks based on vertical width...
30735  *
30736  * The original code extends 'outlayer' - we might need to use that....
30737  * 
30738  */
30739
30740
30741 /**
30742  * @class Roo.bootstrap.LayoutMasonry
30743  * @extends Roo.bootstrap.Component
30744  * Bootstrap Layout Masonry class
30745  * 
30746  * @constructor
30747  * Create a new Element
30748  * @param {Object} config The config object
30749  */
30750
30751 Roo.bootstrap.LayoutMasonry = function(config){
30752     
30753     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30754     
30755     this.bricks = [];
30756     
30757     Roo.bootstrap.LayoutMasonry.register(this);
30758     
30759     this.addEvents({
30760         // raw events
30761         /**
30762          * @event layout
30763          * Fire after layout the items
30764          * @param {Roo.bootstrap.LayoutMasonry} this
30765          * @param {Roo.EventObject} e
30766          */
30767         "layout" : true
30768     });
30769     
30770 };
30771
30772 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30773     
30774     /**
30775      * @cfg {Boolean} isLayoutInstant = no animation?
30776      */   
30777     isLayoutInstant : false, // needed?
30778    
30779     /**
30780      * @cfg {Number} boxWidth  width of the columns
30781      */   
30782     boxWidth : 450,
30783     
30784       /**
30785      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30786      */   
30787     boxHeight : 0,
30788     
30789     /**
30790      * @cfg {Number} padWidth padding below box..
30791      */   
30792     padWidth : 10, 
30793     
30794     /**
30795      * @cfg {Number} gutter gutter width..
30796      */   
30797     gutter : 10,
30798     
30799      /**
30800      * @cfg {Number} maxCols maximum number of columns
30801      */   
30802     
30803     maxCols: 0,
30804     
30805     /**
30806      * @cfg {Boolean} isAutoInitial defalut true
30807      */   
30808     isAutoInitial : true, 
30809     
30810     containerWidth: 0,
30811     
30812     /**
30813      * @cfg {Boolean} isHorizontal defalut false
30814      */   
30815     isHorizontal : false, 
30816
30817     currentSize : null,
30818     
30819     tag: 'div',
30820     
30821     cls: '',
30822     
30823     bricks: null, //CompositeElement
30824     
30825     cols : 1,
30826     
30827     _isLayoutInited : false,
30828     
30829 //    isAlternative : false, // only use for vertical layout...
30830     
30831     /**
30832      * @cfg {Number} alternativePadWidth padding below box..
30833      */   
30834     alternativePadWidth : 50,
30835     
30836     selectedBrick : [],
30837     
30838     getAutoCreate : function(){
30839         
30840         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30841         
30842         var cfg = {
30843             tag: this.tag,
30844             cls: 'blog-masonary-wrapper ' + this.cls,
30845             cn : {
30846                 cls : 'mas-boxes masonary'
30847             }
30848         };
30849         
30850         return cfg;
30851     },
30852     
30853     getChildContainer: function( )
30854     {
30855         if (this.boxesEl) {
30856             return this.boxesEl;
30857         }
30858         
30859         this.boxesEl = this.el.select('.mas-boxes').first();
30860         
30861         return this.boxesEl;
30862     },
30863     
30864     
30865     initEvents : function()
30866     {
30867         var _this = this;
30868         
30869         if(this.isAutoInitial){
30870             Roo.log('hook children rendered');
30871             this.on('childrenrendered', function() {
30872                 Roo.log('children rendered');
30873                 _this.initial();
30874             } ,this);
30875         }
30876     },
30877     
30878     initial : function()
30879     {
30880         this.selectedBrick = [];
30881         
30882         this.currentSize = this.el.getBox(true);
30883         
30884         Roo.EventManager.onWindowResize(this.resize, this); 
30885
30886         if(!this.isAutoInitial){
30887             this.layout();
30888             return;
30889         }
30890         
30891         this.layout();
30892         
30893         return;
30894         //this.layout.defer(500,this);
30895         
30896     },
30897     
30898     resize : function()
30899     {
30900         var cs = this.el.getBox(true);
30901         
30902         if (
30903                 this.currentSize.width == cs.width && 
30904                 this.currentSize.x == cs.x && 
30905                 this.currentSize.height == cs.height && 
30906                 this.currentSize.y == cs.y 
30907         ) {
30908             Roo.log("no change in with or X or Y");
30909             return;
30910         }
30911         
30912         this.currentSize = cs;
30913         
30914         this.layout();
30915         
30916     },
30917     
30918     layout : function()
30919     {   
30920         this._resetLayout();
30921         
30922         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30923         
30924         this.layoutItems( isInstant );
30925       
30926         this._isLayoutInited = true;
30927         
30928         this.fireEvent('layout', this);
30929         
30930     },
30931     
30932     _resetLayout : function()
30933     {
30934         if(this.isHorizontal){
30935             this.horizontalMeasureColumns();
30936             return;
30937         }
30938         
30939         this.verticalMeasureColumns();
30940         
30941     },
30942     
30943     verticalMeasureColumns : function()
30944     {
30945         this.getContainerWidth();
30946         
30947 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30948 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30949 //            return;
30950 //        }
30951         
30952         var boxWidth = this.boxWidth + this.padWidth;
30953         
30954         if(this.containerWidth < this.boxWidth){
30955             boxWidth = this.containerWidth
30956         }
30957         
30958         var containerWidth = this.containerWidth;
30959         
30960         var cols = Math.floor(containerWidth / boxWidth);
30961         
30962         this.cols = Math.max( cols, 1 );
30963         
30964         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30965         
30966         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30967         
30968         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30969         
30970         this.colWidth = boxWidth + avail - this.padWidth;
30971         
30972         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30973         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30974     },
30975     
30976     horizontalMeasureColumns : function()
30977     {
30978         this.getContainerWidth();
30979         
30980         var boxWidth = this.boxWidth;
30981         
30982         if(this.containerWidth < boxWidth){
30983             boxWidth = this.containerWidth;
30984         }
30985         
30986         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30987         
30988         this.el.setHeight(boxWidth);
30989         
30990     },
30991     
30992     getContainerWidth : function()
30993     {
30994         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30995     },
30996     
30997     layoutItems : function( isInstant )
30998     {
30999         Roo.log(this.bricks);
31000         
31001         var items = Roo.apply([], this.bricks);
31002         
31003         if(this.isHorizontal){
31004             this._horizontalLayoutItems( items , isInstant );
31005             return;
31006         }
31007         
31008 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31009 //            this._verticalAlternativeLayoutItems( items , isInstant );
31010 //            return;
31011 //        }
31012         
31013         this._verticalLayoutItems( items , isInstant );
31014         
31015     },
31016     
31017     _verticalLayoutItems : function ( items , isInstant)
31018     {
31019         if ( !items || !items.length ) {
31020             return;
31021         }
31022         
31023         var standard = [
31024             ['xs', 'xs', 'xs', 'tall'],
31025             ['xs', 'xs', 'tall'],
31026             ['xs', 'xs', 'sm'],
31027             ['xs', 'xs', 'xs'],
31028             ['xs', 'tall'],
31029             ['xs', 'sm'],
31030             ['xs', 'xs'],
31031             ['xs'],
31032             
31033             ['sm', 'xs', 'xs'],
31034             ['sm', 'xs'],
31035             ['sm'],
31036             
31037             ['tall', 'xs', 'xs', 'xs'],
31038             ['tall', 'xs', 'xs'],
31039             ['tall', 'xs'],
31040             ['tall']
31041             
31042         ];
31043         
31044         var queue = [];
31045         
31046         var boxes = [];
31047         
31048         var box = [];
31049         
31050         Roo.each(items, function(item, k){
31051             
31052             switch (item.size) {
31053                 // these layouts take up a full box,
31054                 case 'md' :
31055                 case 'md-left' :
31056                 case 'md-right' :
31057                 case 'wide' :
31058                     
31059                     if(box.length){
31060                         boxes.push(box);
31061                         box = [];
31062                     }
31063                     
31064                     boxes.push([item]);
31065                     
31066                     break;
31067                     
31068                 case 'xs' :
31069                 case 'sm' :
31070                 case 'tall' :
31071                     
31072                     box.push(item);
31073                     
31074                     break;
31075                 default :
31076                     break;
31077                     
31078             }
31079             
31080         }, this);
31081         
31082         if(box.length){
31083             boxes.push(box);
31084             box = [];
31085         }
31086         
31087         var filterPattern = function(box, length)
31088         {
31089             if(!box.length){
31090                 return;
31091             }
31092             
31093             var match = false;
31094             
31095             var pattern = box.slice(0, length);
31096             
31097             var format = [];
31098             
31099             Roo.each(pattern, function(i){
31100                 format.push(i.size);
31101             }, this);
31102             
31103             Roo.each(standard, function(s){
31104                 
31105                 if(String(s) != String(format)){
31106                     return;
31107                 }
31108                 
31109                 match = true;
31110                 return false;
31111                 
31112             }, this);
31113             
31114             if(!match && length == 1){
31115                 return;
31116             }
31117             
31118             if(!match){
31119                 filterPattern(box, length - 1);
31120                 return;
31121             }
31122                 
31123             queue.push(pattern);
31124
31125             box = box.slice(length, box.length);
31126
31127             filterPattern(box, 4);
31128
31129             return;
31130             
31131         }
31132         
31133         Roo.each(boxes, function(box, k){
31134             
31135             if(!box.length){
31136                 return;
31137             }
31138             
31139             if(box.length == 1){
31140                 queue.push(box);
31141                 return;
31142             }
31143             
31144             filterPattern(box, 4);
31145             
31146         }, this);
31147         
31148         this._processVerticalLayoutQueue( queue, isInstant );
31149         
31150     },
31151     
31152 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31153 //    {
31154 //        if ( !items || !items.length ) {
31155 //            return;
31156 //        }
31157 //
31158 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31159 //        
31160 //    },
31161     
31162     _horizontalLayoutItems : function ( items , isInstant)
31163     {
31164         if ( !items || !items.length || items.length < 3) {
31165             return;
31166         }
31167         
31168         items.reverse();
31169         
31170         var eItems = items.slice(0, 3);
31171         
31172         items = items.slice(3, items.length);
31173         
31174         var standard = [
31175             ['xs', 'xs', 'xs', 'wide'],
31176             ['xs', 'xs', 'wide'],
31177             ['xs', 'xs', 'sm'],
31178             ['xs', 'xs', 'xs'],
31179             ['xs', 'wide'],
31180             ['xs', 'sm'],
31181             ['xs', 'xs'],
31182             ['xs'],
31183             
31184             ['sm', 'xs', 'xs'],
31185             ['sm', 'xs'],
31186             ['sm'],
31187             
31188             ['wide', 'xs', 'xs', 'xs'],
31189             ['wide', 'xs', 'xs'],
31190             ['wide', 'xs'],
31191             ['wide'],
31192             
31193             ['wide-thin']
31194         ];
31195         
31196         var queue = [];
31197         
31198         var boxes = [];
31199         
31200         var box = [];
31201         
31202         Roo.each(items, function(item, k){
31203             
31204             switch (item.size) {
31205                 case 'md' :
31206                 case 'md-left' :
31207                 case 'md-right' :
31208                 case 'tall' :
31209                     
31210                     if(box.length){
31211                         boxes.push(box);
31212                         box = [];
31213                     }
31214                     
31215                     boxes.push([item]);
31216                     
31217                     break;
31218                     
31219                 case 'xs' :
31220                 case 'sm' :
31221                 case 'wide' :
31222                 case 'wide-thin' :
31223                     
31224                     box.push(item);
31225                     
31226                     break;
31227                 default :
31228                     break;
31229                     
31230             }
31231             
31232         }, this);
31233         
31234         if(box.length){
31235             boxes.push(box);
31236             box = [];
31237         }
31238         
31239         var filterPattern = function(box, length)
31240         {
31241             if(!box.length){
31242                 return;
31243             }
31244             
31245             var match = false;
31246             
31247             var pattern = box.slice(0, length);
31248             
31249             var format = [];
31250             
31251             Roo.each(pattern, function(i){
31252                 format.push(i.size);
31253             }, this);
31254             
31255             Roo.each(standard, function(s){
31256                 
31257                 if(String(s) != String(format)){
31258                     return;
31259                 }
31260                 
31261                 match = true;
31262                 return false;
31263                 
31264             }, this);
31265             
31266             if(!match && length == 1){
31267                 return;
31268             }
31269             
31270             if(!match){
31271                 filterPattern(box, length - 1);
31272                 return;
31273             }
31274                 
31275             queue.push(pattern);
31276
31277             box = box.slice(length, box.length);
31278
31279             filterPattern(box, 4);
31280
31281             return;
31282             
31283         }
31284         
31285         Roo.each(boxes, function(box, k){
31286             
31287             if(!box.length){
31288                 return;
31289             }
31290             
31291             if(box.length == 1){
31292                 queue.push(box);
31293                 return;
31294             }
31295             
31296             filterPattern(box, 4);
31297             
31298         }, this);
31299         
31300         
31301         var prune = [];
31302         
31303         var pos = this.el.getBox(true);
31304         
31305         var minX = pos.x;
31306         
31307         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31308         
31309         var hit_end = false;
31310         
31311         Roo.each(queue, function(box){
31312             
31313             if(hit_end){
31314                 
31315                 Roo.each(box, function(b){
31316                 
31317                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31318                     b.el.hide();
31319
31320                 }, this);
31321
31322                 return;
31323             }
31324             
31325             var mx = 0;
31326             
31327             Roo.each(box, function(b){
31328                 
31329                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31330                 b.el.show();
31331
31332                 mx = Math.max(mx, b.x);
31333                 
31334             }, this);
31335             
31336             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31337             
31338             if(maxX < minX){
31339                 
31340                 Roo.each(box, function(b){
31341                 
31342                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31343                     b.el.hide();
31344                     
31345                 }, this);
31346                 
31347                 hit_end = true;
31348                 
31349                 return;
31350             }
31351             
31352             prune.push(box);
31353             
31354         }, this);
31355         
31356         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31357     },
31358     
31359     /** Sets position of item in DOM
31360     * @param {Element} item
31361     * @param {Number} x - horizontal position
31362     * @param {Number} y - vertical position
31363     * @param {Boolean} isInstant - disables transitions
31364     */
31365     _processVerticalLayoutQueue : function( queue, isInstant )
31366     {
31367         var pos = this.el.getBox(true);
31368         var x = pos.x;
31369         var y = pos.y;
31370         var maxY = [];
31371         
31372         for (var i = 0; i < this.cols; i++){
31373             maxY[i] = pos.y;
31374         }
31375         
31376         Roo.each(queue, function(box, k){
31377             
31378             var col = k % this.cols;
31379             
31380             Roo.each(box, function(b,kk){
31381                 
31382                 b.el.position('absolute');
31383                 
31384                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31385                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31386                 
31387                 if(b.size == 'md-left' || b.size == 'md-right'){
31388                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31389                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31390                 }
31391                 
31392                 b.el.setWidth(width);
31393                 b.el.setHeight(height);
31394                 // iframe?
31395                 b.el.select('iframe',true).setSize(width,height);
31396                 
31397             }, this);
31398             
31399             for (var i = 0; i < this.cols; i++){
31400                 
31401                 if(maxY[i] < maxY[col]){
31402                     col = i;
31403                     continue;
31404                 }
31405                 
31406                 col = Math.min(col, i);
31407                 
31408             }
31409             
31410             x = pos.x + col * (this.colWidth + this.padWidth);
31411             
31412             y = maxY[col];
31413             
31414             var positions = [];
31415             
31416             switch (box.length){
31417                 case 1 :
31418                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31419                     break;
31420                 case 2 :
31421                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31422                     break;
31423                 case 3 :
31424                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31425                     break;
31426                 case 4 :
31427                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31428                     break;
31429                 default :
31430                     break;
31431             }
31432             
31433             Roo.each(box, function(b,kk){
31434                 
31435                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31436                 
31437                 var sz = b.el.getSize();
31438                 
31439                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31440                 
31441             }, this);
31442             
31443         }, this);
31444         
31445         var mY = 0;
31446         
31447         for (var i = 0; i < this.cols; i++){
31448             mY = Math.max(mY, maxY[i]);
31449         }
31450         
31451         this.el.setHeight(mY - pos.y);
31452         
31453     },
31454     
31455 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31456 //    {
31457 //        var pos = this.el.getBox(true);
31458 //        var x = pos.x;
31459 //        var y = pos.y;
31460 //        var maxX = pos.right;
31461 //        
31462 //        var maxHeight = 0;
31463 //        
31464 //        Roo.each(items, function(item, k){
31465 //            
31466 //            var c = k % 2;
31467 //            
31468 //            item.el.position('absolute');
31469 //                
31470 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31471 //
31472 //            item.el.setWidth(width);
31473 //
31474 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31475 //
31476 //            item.el.setHeight(height);
31477 //            
31478 //            if(c == 0){
31479 //                item.el.setXY([x, y], isInstant ? false : true);
31480 //            } else {
31481 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31482 //            }
31483 //            
31484 //            y = y + height + this.alternativePadWidth;
31485 //            
31486 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31487 //            
31488 //        }, this);
31489 //        
31490 //        this.el.setHeight(maxHeight);
31491 //        
31492 //    },
31493     
31494     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31495     {
31496         var pos = this.el.getBox(true);
31497         
31498         var minX = pos.x;
31499         var minY = pos.y;
31500         
31501         var maxX = pos.right;
31502         
31503         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31504         
31505         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31506         
31507         Roo.each(queue, function(box, k){
31508             
31509             Roo.each(box, function(b, kk){
31510                 
31511                 b.el.position('absolute');
31512                 
31513                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31514                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31515                 
31516                 if(b.size == 'md-left' || b.size == 'md-right'){
31517                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31518                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31519                 }
31520                 
31521                 b.el.setWidth(width);
31522                 b.el.setHeight(height);
31523                 
31524             }, this);
31525             
31526             if(!box.length){
31527                 return;
31528             }
31529             
31530             var positions = [];
31531             
31532             switch (box.length){
31533                 case 1 :
31534                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31535                     break;
31536                 case 2 :
31537                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31538                     break;
31539                 case 3 :
31540                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31541                     break;
31542                 case 4 :
31543                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31544                     break;
31545                 default :
31546                     break;
31547             }
31548             
31549             Roo.each(box, function(b,kk){
31550                 
31551                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31552                 
31553                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31554                 
31555             }, this);
31556             
31557         }, this);
31558         
31559     },
31560     
31561     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31562     {
31563         Roo.each(eItems, function(b,k){
31564             
31565             b.size = (k == 0) ? 'sm' : 'xs';
31566             b.x = (k == 0) ? 2 : 1;
31567             b.y = (k == 0) ? 2 : 1;
31568             
31569             b.el.position('absolute');
31570             
31571             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31572                 
31573             b.el.setWidth(width);
31574             
31575             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31576             
31577             b.el.setHeight(height);
31578             
31579         }, this);
31580
31581         var positions = [];
31582         
31583         positions.push({
31584             x : maxX - this.unitWidth * 2 - this.gutter,
31585             y : minY
31586         });
31587         
31588         positions.push({
31589             x : maxX - this.unitWidth,
31590             y : minY + (this.unitWidth + this.gutter) * 2
31591         });
31592         
31593         positions.push({
31594             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31595             y : minY
31596         });
31597         
31598         Roo.each(eItems, function(b,k){
31599             
31600             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31601
31602         }, this);
31603         
31604     },
31605     
31606     getVerticalOneBoxColPositions : function(x, y, box)
31607     {
31608         var pos = [];
31609         
31610         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31611         
31612         if(box[0].size == 'md-left'){
31613             rand = 0;
31614         }
31615         
31616         if(box[0].size == 'md-right'){
31617             rand = 1;
31618         }
31619         
31620         pos.push({
31621             x : x + (this.unitWidth + this.gutter) * rand,
31622             y : y
31623         });
31624         
31625         return pos;
31626     },
31627     
31628     getVerticalTwoBoxColPositions : function(x, y, box)
31629     {
31630         var pos = [];
31631         
31632         if(box[0].size == 'xs'){
31633             
31634             pos.push({
31635                 x : x,
31636                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31637             });
31638
31639             pos.push({
31640                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31641                 y : y
31642             });
31643             
31644             return pos;
31645             
31646         }
31647         
31648         pos.push({
31649             x : x,
31650             y : y
31651         });
31652
31653         pos.push({
31654             x : x + (this.unitWidth + this.gutter) * 2,
31655             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31656         });
31657         
31658         return pos;
31659         
31660     },
31661     
31662     getVerticalThreeBoxColPositions : function(x, y, box)
31663     {
31664         var pos = [];
31665         
31666         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31667             
31668             pos.push({
31669                 x : x,
31670                 y : y
31671             });
31672
31673             pos.push({
31674                 x : x + (this.unitWidth + this.gutter) * 1,
31675                 y : y
31676             });
31677             
31678             pos.push({
31679                 x : x + (this.unitWidth + this.gutter) * 2,
31680                 y : y
31681             });
31682             
31683             return pos;
31684             
31685         }
31686         
31687         if(box[0].size == 'xs' && box[1].size == 'xs'){
31688             
31689             pos.push({
31690                 x : x,
31691                 y : y
31692             });
31693
31694             pos.push({
31695                 x : x,
31696                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31697             });
31698             
31699             pos.push({
31700                 x : x + (this.unitWidth + this.gutter) * 1,
31701                 y : y
31702             });
31703             
31704             return pos;
31705             
31706         }
31707         
31708         pos.push({
31709             x : x,
31710             y : y
31711         });
31712
31713         pos.push({
31714             x : x + (this.unitWidth + this.gutter) * 2,
31715             y : y
31716         });
31717
31718         pos.push({
31719             x : x + (this.unitWidth + this.gutter) * 2,
31720             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31721         });
31722             
31723         return pos;
31724         
31725     },
31726     
31727     getVerticalFourBoxColPositions : function(x, y, box)
31728     {
31729         var pos = [];
31730         
31731         if(box[0].size == 'xs'){
31732             
31733             pos.push({
31734                 x : x,
31735                 y : y
31736             });
31737
31738             pos.push({
31739                 x : x,
31740                 y : y + (this.unitHeight + this.gutter) * 1
31741             });
31742             
31743             pos.push({
31744                 x : x,
31745                 y : y + (this.unitHeight + this.gutter) * 2
31746             });
31747             
31748             pos.push({
31749                 x : x + (this.unitWidth + this.gutter) * 1,
31750                 y : y
31751             });
31752             
31753             return pos;
31754             
31755         }
31756         
31757         pos.push({
31758             x : x,
31759             y : y
31760         });
31761
31762         pos.push({
31763             x : x + (this.unitWidth + this.gutter) * 2,
31764             y : y
31765         });
31766
31767         pos.push({
31768             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31769             y : y + (this.unitHeight + this.gutter) * 1
31770         });
31771
31772         pos.push({
31773             x : x + (this.unitWidth + this.gutter) * 2,
31774             y : y + (this.unitWidth + this.gutter) * 2
31775         });
31776
31777         return pos;
31778         
31779     },
31780     
31781     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31782     {
31783         var pos = [];
31784         
31785         if(box[0].size == 'md-left'){
31786             pos.push({
31787                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31788                 y : minY
31789             });
31790             
31791             return pos;
31792         }
31793         
31794         if(box[0].size == 'md-right'){
31795             pos.push({
31796                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31797                 y : minY + (this.unitWidth + this.gutter) * 1
31798             });
31799             
31800             return pos;
31801         }
31802         
31803         var rand = Math.floor(Math.random() * (4 - box[0].y));
31804         
31805         pos.push({
31806             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31807             y : minY + (this.unitWidth + this.gutter) * rand
31808         });
31809         
31810         return pos;
31811         
31812     },
31813     
31814     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31815     {
31816         var pos = [];
31817         
31818         if(box[0].size == 'xs'){
31819             
31820             pos.push({
31821                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31822                 y : minY
31823             });
31824
31825             pos.push({
31826                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31827                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31828             });
31829             
31830             return pos;
31831             
31832         }
31833         
31834         pos.push({
31835             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31836             y : minY
31837         });
31838
31839         pos.push({
31840             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31841             y : minY + (this.unitWidth + this.gutter) * 2
31842         });
31843         
31844         return pos;
31845         
31846     },
31847     
31848     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31849     {
31850         var pos = [];
31851         
31852         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31853             
31854             pos.push({
31855                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31856                 y : minY
31857             });
31858
31859             pos.push({
31860                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31861                 y : minY + (this.unitWidth + this.gutter) * 1
31862             });
31863             
31864             pos.push({
31865                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31866                 y : minY + (this.unitWidth + this.gutter) * 2
31867             });
31868             
31869             return pos;
31870             
31871         }
31872         
31873         if(box[0].size == 'xs' && box[1].size == 'xs'){
31874             
31875             pos.push({
31876                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31877                 y : minY
31878             });
31879
31880             pos.push({
31881                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31882                 y : minY
31883             });
31884             
31885             pos.push({
31886                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31887                 y : minY + (this.unitWidth + this.gutter) * 1
31888             });
31889             
31890             return pos;
31891             
31892         }
31893         
31894         pos.push({
31895             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31896             y : minY
31897         });
31898
31899         pos.push({
31900             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31901             y : minY + (this.unitWidth + this.gutter) * 2
31902         });
31903
31904         pos.push({
31905             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31906             y : minY + (this.unitWidth + this.gutter) * 2
31907         });
31908             
31909         return pos;
31910         
31911     },
31912     
31913     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31914     {
31915         var pos = [];
31916         
31917         if(box[0].size == 'xs'){
31918             
31919             pos.push({
31920                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31921                 y : minY
31922             });
31923
31924             pos.push({
31925                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31926                 y : minY
31927             });
31928             
31929             pos.push({
31930                 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),
31931                 y : minY
31932             });
31933             
31934             pos.push({
31935                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31936                 y : minY + (this.unitWidth + this.gutter) * 1
31937             });
31938             
31939             return pos;
31940             
31941         }
31942         
31943         pos.push({
31944             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31945             y : minY
31946         });
31947         
31948         pos.push({
31949             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31950             y : minY + (this.unitWidth + this.gutter) * 2
31951         });
31952         
31953         pos.push({
31954             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31955             y : minY + (this.unitWidth + this.gutter) * 2
31956         });
31957         
31958         pos.push({
31959             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),
31960             y : minY + (this.unitWidth + this.gutter) * 2
31961         });
31962
31963         return pos;
31964         
31965     },
31966     
31967     /**
31968     * remove a Masonry Brick
31969     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31970     */
31971     removeBrick : function(brick_id)
31972     {
31973         if (!brick_id) {
31974             return;
31975         }
31976         
31977         for (var i = 0; i<this.bricks.length; i++) {
31978             if (this.bricks[i].id == brick_id) {
31979                 this.bricks.splice(i,1);
31980                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31981                 this.initial();
31982             }
31983         }
31984     },
31985     
31986     /**
31987     * adds a Masonry Brick
31988     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31989     */
31990     addBrick : function(cfg)
31991     {
31992         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31993         //this.register(cn);
31994         cn.parentId = this.id;
31995         cn.render(this.el);
31996         return cn;
31997     },
31998     
31999     /**
32000     * register a Masonry Brick
32001     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32002     */
32003     
32004     register : function(brick)
32005     {
32006         this.bricks.push(brick);
32007         brick.masonryId = this.id;
32008     },
32009     
32010     /**
32011     * clear all the Masonry Brick
32012     */
32013     clearAll : function()
32014     {
32015         this.bricks = [];
32016         //this.getChildContainer().dom.innerHTML = "";
32017         this.el.dom.innerHTML = '';
32018     },
32019     
32020     getSelected : function()
32021     {
32022         if (!this.selectedBrick) {
32023             return false;
32024         }
32025         
32026         return this.selectedBrick;
32027     }
32028 });
32029
32030 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32031     
32032     groups: {},
32033      /**
32034     * register a Masonry Layout
32035     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32036     */
32037     
32038     register : function(layout)
32039     {
32040         this.groups[layout.id] = layout;
32041     },
32042     /**
32043     * fetch a  Masonry Layout based on the masonry layout ID
32044     * @param {string} the masonry layout to add
32045     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32046     */
32047     
32048     get: function(layout_id) {
32049         if (typeof(this.groups[layout_id]) == 'undefined') {
32050             return false;
32051         }
32052         return this.groups[layout_id] ;
32053     }
32054     
32055     
32056     
32057 });
32058
32059  
32060
32061  /**
32062  *
32063  * This is based on 
32064  * http://masonry.desandro.com
32065  *
32066  * The idea is to render all the bricks based on vertical width...
32067  *
32068  * The original code extends 'outlayer' - we might need to use that....
32069  * 
32070  */
32071
32072
32073 /**
32074  * @class Roo.bootstrap.LayoutMasonryAuto
32075  * @extends Roo.bootstrap.Component
32076  * Bootstrap Layout Masonry class
32077  * 
32078  * @constructor
32079  * Create a new Element
32080  * @param {Object} config The config object
32081  */
32082
32083 Roo.bootstrap.LayoutMasonryAuto = function(config){
32084     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32085 };
32086
32087 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32088     
32089       /**
32090      * @cfg {Boolean} isFitWidth  - resize the width..
32091      */   
32092     isFitWidth : false,  // options..
32093     /**
32094      * @cfg {Boolean} isOriginLeft = left align?
32095      */   
32096     isOriginLeft : true,
32097     /**
32098      * @cfg {Boolean} isOriginTop = top align?
32099      */   
32100     isOriginTop : false,
32101     /**
32102      * @cfg {Boolean} isLayoutInstant = no animation?
32103      */   
32104     isLayoutInstant : false, // needed?
32105     /**
32106      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32107      */   
32108     isResizingContainer : true,
32109     /**
32110      * @cfg {Number} columnWidth  width of the columns 
32111      */   
32112     
32113     columnWidth : 0,
32114     
32115     /**
32116      * @cfg {Number} maxCols maximum number of columns
32117      */   
32118     
32119     maxCols: 0,
32120     /**
32121      * @cfg {Number} padHeight padding below box..
32122      */   
32123     
32124     padHeight : 10, 
32125     
32126     /**
32127      * @cfg {Boolean} isAutoInitial defalut true
32128      */   
32129     
32130     isAutoInitial : true, 
32131     
32132     // private?
32133     gutter : 0,
32134     
32135     containerWidth: 0,
32136     initialColumnWidth : 0,
32137     currentSize : null,
32138     
32139     colYs : null, // array.
32140     maxY : 0,
32141     padWidth: 10,
32142     
32143     
32144     tag: 'div',
32145     cls: '',
32146     bricks: null, //CompositeElement
32147     cols : 0, // array?
32148     // element : null, // wrapped now this.el
32149     _isLayoutInited : null, 
32150     
32151     
32152     getAutoCreate : function(){
32153         
32154         var cfg = {
32155             tag: this.tag,
32156             cls: 'blog-masonary-wrapper ' + this.cls,
32157             cn : {
32158                 cls : 'mas-boxes masonary'
32159             }
32160         };
32161         
32162         return cfg;
32163     },
32164     
32165     getChildContainer: function( )
32166     {
32167         if (this.boxesEl) {
32168             return this.boxesEl;
32169         }
32170         
32171         this.boxesEl = this.el.select('.mas-boxes').first();
32172         
32173         return this.boxesEl;
32174     },
32175     
32176     
32177     initEvents : function()
32178     {
32179         var _this = this;
32180         
32181         if(this.isAutoInitial){
32182             Roo.log('hook children rendered');
32183             this.on('childrenrendered', function() {
32184                 Roo.log('children rendered');
32185                 _this.initial();
32186             } ,this);
32187         }
32188         
32189     },
32190     
32191     initial : function()
32192     {
32193         this.reloadItems();
32194
32195         this.currentSize = this.el.getBox(true);
32196
32197         /// was window resize... - let's see if this works..
32198         Roo.EventManager.onWindowResize(this.resize, this); 
32199
32200         if(!this.isAutoInitial){
32201             this.layout();
32202             return;
32203         }
32204         
32205         this.layout.defer(500,this);
32206     },
32207     
32208     reloadItems: function()
32209     {
32210         this.bricks = this.el.select('.masonry-brick', true);
32211         
32212         this.bricks.each(function(b) {
32213             //Roo.log(b.getSize());
32214             if (!b.attr('originalwidth')) {
32215                 b.attr('originalwidth',  b.getSize().width);
32216             }
32217             
32218         });
32219         
32220         Roo.log(this.bricks.elements.length);
32221     },
32222     
32223     resize : function()
32224     {
32225         Roo.log('resize');
32226         var cs = this.el.getBox(true);
32227         
32228         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32229             Roo.log("no change in with or X");
32230             return;
32231         }
32232         this.currentSize = cs;
32233         this.layout();
32234     },
32235     
32236     layout : function()
32237     {
32238          Roo.log('layout');
32239         this._resetLayout();
32240         //this._manageStamps();
32241       
32242         // don't animate first layout
32243         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32244         this.layoutItems( isInstant );
32245       
32246         // flag for initalized
32247         this._isLayoutInited = true;
32248     },
32249     
32250     layoutItems : function( isInstant )
32251     {
32252         //var items = this._getItemsForLayout( this.items );
32253         // original code supports filtering layout items.. we just ignore it..
32254         
32255         this._layoutItems( this.bricks , isInstant );
32256       
32257         this._postLayout();
32258     },
32259     _layoutItems : function ( items , isInstant)
32260     {
32261        //this.fireEvent( 'layout', this, items );
32262     
32263
32264         if ( !items || !items.elements.length ) {
32265           // no items, emit event with empty array
32266             return;
32267         }
32268
32269         var queue = [];
32270         items.each(function(item) {
32271             Roo.log("layout item");
32272             Roo.log(item);
32273             // get x/y object from method
32274             var position = this._getItemLayoutPosition( item );
32275             // enqueue
32276             position.item = item;
32277             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32278             queue.push( position );
32279         }, this);
32280       
32281         this._processLayoutQueue( queue );
32282     },
32283     /** Sets position of item in DOM
32284     * @param {Element} item
32285     * @param {Number} x - horizontal position
32286     * @param {Number} y - vertical position
32287     * @param {Boolean} isInstant - disables transitions
32288     */
32289     _processLayoutQueue : function( queue )
32290     {
32291         for ( var i=0, len = queue.length; i < len; i++ ) {
32292             var obj = queue[i];
32293             obj.item.position('absolute');
32294             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32295         }
32296     },
32297       
32298     
32299     /**
32300     * Any logic you want to do after each layout,
32301     * i.e. size the container
32302     */
32303     _postLayout : function()
32304     {
32305         this.resizeContainer();
32306     },
32307     
32308     resizeContainer : function()
32309     {
32310         if ( !this.isResizingContainer ) {
32311             return;
32312         }
32313         var size = this._getContainerSize();
32314         if ( size ) {
32315             this.el.setSize(size.width,size.height);
32316             this.boxesEl.setSize(size.width,size.height);
32317         }
32318     },
32319     
32320     
32321     
32322     _resetLayout : function()
32323     {
32324         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32325         this.colWidth = this.el.getWidth();
32326         //this.gutter = this.el.getWidth(); 
32327         
32328         this.measureColumns();
32329
32330         // reset column Y
32331         var i = this.cols;
32332         this.colYs = [];
32333         while (i--) {
32334             this.colYs.push( 0 );
32335         }
32336     
32337         this.maxY = 0;
32338     },
32339
32340     measureColumns : function()
32341     {
32342         this.getContainerWidth();
32343       // if columnWidth is 0, default to outerWidth of first item
32344         if ( !this.columnWidth ) {
32345             var firstItem = this.bricks.first();
32346             Roo.log(firstItem);
32347             this.columnWidth  = this.containerWidth;
32348             if (firstItem && firstItem.attr('originalwidth') ) {
32349                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32350             }
32351             // columnWidth fall back to item of first element
32352             Roo.log("set column width?");
32353                         this.initialColumnWidth = this.columnWidth  ;
32354
32355             // if first elem has no width, default to size of container
32356             
32357         }
32358         
32359         
32360         if (this.initialColumnWidth) {
32361             this.columnWidth = this.initialColumnWidth;
32362         }
32363         
32364         
32365             
32366         // column width is fixed at the top - however if container width get's smaller we should
32367         // reduce it...
32368         
32369         // this bit calcs how man columns..
32370             
32371         var columnWidth = this.columnWidth += this.gutter;
32372       
32373         // calculate columns
32374         var containerWidth = this.containerWidth + this.gutter;
32375         
32376         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32377         // fix rounding errors, typically with gutters
32378         var excess = columnWidth - containerWidth % columnWidth;
32379         
32380         
32381         // if overshoot is less than a pixel, round up, otherwise floor it
32382         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32383         cols = Math[ mathMethod ]( cols );
32384         this.cols = Math.max( cols, 1 );
32385         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32386         
32387          // padding positioning..
32388         var totalColWidth = this.cols * this.columnWidth;
32389         var padavail = this.containerWidth - totalColWidth;
32390         // so for 2 columns - we need 3 'pads'
32391         
32392         var padNeeded = (1+this.cols) * this.padWidth;
32393         
32394         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32395         
32396         this.columnWidth += padExtra
32397         //this.padWidth = Math.floor(padavail /  ( this.cols));
32398         
32399         // adjust colum width so that padding is fixed??
32400         
32401         // we have 3 columns ... total = width * 3
32402         // we have X left over... that should be used by 
32403         
32404         //if (this.expandC) {
32405             
32406         //}
32407         
32408         
32409         
32410     },
32411     
32412     getContainerWidth : function()
32413     {
32414        /* // container is parent if fit width
32415         var container = this.isFitWidth ? this.element.parentNode : this.element;
32416         // check that this.size and size are there
32417         // IE8 triggers resize on body size change, so they might not be
32418         
32419         var size = getSize( container );  //FIXME
32420         this.containerWidth = size && size.innerWidth; //FIXME
32421         */
32422          
32423         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32424         
32425     },
32426     
32427     _getItemLayoutPosition : function( item )  // what is item?
32428     {
32429         // we resize the item to our columnWidth..
32430       
32431         item.setWidth(this.columnWidth);
32432         item.autoBoxAdjust  = false;
32433         
32434         var sz = item.getSize();
32435  
32436         // how many columns does this brick span
32437         var remainder = this.containerWidth % this.columnWidth;
32438         
32439         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32440         // round if off by 1 pixel, otherwise use ceil
32441         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32442         colSpan = Math.min( colSpan, this.cols );
32443         
32444         // normally this should be '1' as we dont' currently allow multi width columns..
32445         
32446         var colGroup = this._getColGroup( colSpan );
32447         // get the minimum Y value from the columns
32448         var minimumY = Math.min.apply( Math, colGroup );
32449         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32450         
32451         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32452          
32453         // position the brick
32454         var position = {
32455             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32456             y: this.currentSize.y + minimumY + this.padHeight
32457         };
32458         
32459         Roo.log(position);
32460         // apply setHeight to necessary columns
32461         var setHeight = minimumY + sz.height + this.padHeight;
32462         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32463         
32464         var setSpan = this.cols + 1 - colGroup.length;
32465         for ( var i = 0; i < setSpan; i++ ) {
32466           this.colYs[ shortColIndex + i ] = setHeight ;
32467         }
32468       
32469         return position;
32470     },
32471     
32472     /**
32473      * @param {Number} colSpan - number of columns the element spans
32474      * @returns {Array} colGroup
32475      */
32476     _getColGroup : function( colSpan )
32477     {
32478         if ( colSpan < 2 ) {
32479           // if brick spans only one column, use all the column Ys
32480           return this.colYs;
32481         }
32482       
32483         var colGroup = [];
32484         // how many different places could this brick fit horizontally
32485         var groupCount = this.cols + 1 - colSpan;
32486         // for each group potential horizontal position
32487         for ( var i = 0; i < groupCount; i++ ) {
32488           // make an array of colY values for that one group
32489           var groupColYs = this.colYs.slice( i, i + colSpan );
32490           // and get the max value of the array
32491           colGroup[i] = Math.max.apply( Math, groupColYs );
32492         }
32493         return colGroup;
32494     },
32495     /*
32496     _manageStamp : function( stamp )
32497     {
32498         var stampSize =  stamp.getSize();
32499         var offset = stamp.getBox();
32500         // get the columns that this stamp affects
32501         var firstX = this.isOriginLeft ? offset.x : offset.right;
32502         var lastX = firstX + stampSize.width;
32503         var firstCol = Math.floor( firstX / this.columnWidth );
32504         firstCol = Math.max( 0, firstCol );
32505         
32506         var lastCol = Math.floor( lastX / this.columnWidth );
32507         // lastCol should not go over if multiple of columnWidth #425
32508         lastCol -= lastX % this.columnWidth ? 0 : 1;
32509         lastCol = Math.min( this.cols - 1, lastCol );
32510         
32511         // set colYs to bottom of the stamp
32512         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32513             stampSize.height;
32514             
32515         for ( var i = firstCol; i <= lastCol; i++ ) {
32516           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32517         }
32518     },
32519     */
32520     
32521     _getContainerSize : function()
32522     {
32523         this.maxY = Math.max.apply( Math, this.colYs );
32524         var size = {
32525             height: this.maxY
32526         };
32527       
32528         if ( this.isFitWidth ) {
32529             size.width = this._getContainerFitWidth();
32530         }
32531       
32532         return size;
32533     },
32534     
32535     _getContainerFitWidth : function()
32536     {
32537         var unusedCols = 0;
32538         // count unused columns
32539         var i = this.cols;
32540         while ( --i ) {
32541           if ( this.colYs[i] !== 0 ) {
32542             break;
32543           }
32544           unusedCols++;
32545         }
32546         // fit container to columns that have been used
32547         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32548     },
32549     
32550     needsResizeLayout : function()
32551     {
32552         var previousWidth = this.containerWidth;
32553         this.getContainerWidth();
32554         return previousWidth !== this.containerWidth;
32555     }
32556  
32557 });
32558
32559  
32560
32561  /*
32562  * - LGPL
32563  *
32564  * element
32565  * 
32566  */
32567
32568 /**
32569  * @class Roo.bootstrap.MasonryBrick
32570  * @extends Roo.bootstrap.Component
32571  * Bootstrap MasonryBrick class
32572  * 
32573  * @constructor
32574  * Create a new MasonryBrick
32575  * @param {Object} config The config object
32576  */
32577
32578 Roo.bootstrap.MasonryBrick = function(config){
32579     
32580     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32581     
32582     Roo.bootstrap.MasonryBrick.register(this);
32583     
32584     this.addEvents({
32585         // raw events
32586         /**
32587          * @event click
32588          * When a MasonryBrick is clcik
32589          * @param {Roo.bootstrap.MasonryBrick} this
32590          * @param {Roo.EventObject} e
32591          */
32592         "click" : true
32593     });
32594 };
32595
32596 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32597     
32598     /**
32599      * @cfg {String} title
32600      */   
32601     title : '',
32602     /**
32603      * @cfg {String} html
32604      */   
32605     html : '',
32606     /**
32607      * @cfg {String} bgimage
32608      */   
32609     bgimage : '',
32610     /**
32611      * @cfg {String} videourl
32612      */   
32613     videourl : '',
32614     /**
32615      * @cfg {String} cls
32616      */   
32617     cls : '',
32618     /**
32619      * @cfg {String} href
32620      */   
32621     href : '',
32622     /**
32623      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32624      */   
32625     size : 'xs',
32626     
32627     /**
32628      * @cfg {String} placetitle (center|bottom)
32629      */   
32630     placetitle : '',
32631     
32632     /**
32633      * @cfg {Boolean} isFitContainer defalut true
32634      */   
32635     isFitContainer : true, 
32636     
32637     /**
32638      * @cfg {Boolean} preventDefault defalut false
32639      */   
32640     preventDefault : false, 
32641     
32642     /**
32643      * @cfg {Boolean} inverse defalut false
32644      */   
32645     maskInverse : false, 
32646     
32647     getAutoCreate : function()
32648     {
32649         if(!this.isFitContainer){
32650             return this.getSplitAutoCreate();
32651         }
32652         
32653         var cls = 'masonry-brick masonry-brick-full';
32654         
32655         if(this.href.length){
32656             cls += ' masonry-brick-link';
32657         }
32658         
32659         if(this.bgimage.length){
32660             cls += ' masonry-brick-image';
32661         }
32662         
32663         if(this.maskInverse){
32664             cls += ' mask-inverse';
32665         }
32666         
32667         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32668             cls += ' enable-mask';
32669         }
32670         
32671         if(this.size){
32672             cls += ' masonry-' + this.size + '-brick';
32673         }
32674         
32675         if(this.placetitle.length){
32676             
32677             switch (this.placetitle) {
32678                 case 'center' :
32679                     cls += ' masonry-center-title';
32680                     break;
32681                 case 'bottom' :
32682                     cls += ' masonry-bottom-title';
32683                     break;
32684                 default:
32685                     break;
32686             }
32687             
32688         } else {
32689             if(!this.html.length && !this.bgimage.length){
32690                 cls += ' masonry-center-title';
32691             }
32692
32693             if(!this.html.length && this.bgimage.length){
32694                 cls += ' masonry-bottom-title';
32695             }
32696         }
32697         
32698         if(this.cls){
32699             cls += ' ' + this.cls;
32700         }
32701         
32702         var cfg = {
32703             tag: (this.href.length) ? 'a' : 'div',
32704             cls: cls,
32705             cn: [
32706                 {
32707                     tag: 'div',
32708                     cls: 'masonry-brick-mask'
32709                 },
32710                 {
32711                     tag: 'div',
32712                     cls: 'masonry-brick-paragraph',
32713                     cn: []
32714                 }
32715             ]
32716         };
32717         
32718         if(this.href.length){
32719             cfg.href = this.href;
32720         }
32721         
32722         var cn = cfg.cn[1].cn;
32723         
32724         if(this.title.length){
32725             cn.push({
32726                 tag: 'h4',
32727                 cls: 'masonry-brick-title',
32728                 html: this.title
32729             });
32730         }
32731         
32732         if(this.html.length){
32733             cn.push({
32734                 tag: 'p',
32735                 cls: 'masonry-brick-text',
32736                 html: this.html
32737             });
32738         }
32739         
32740         if (!this.title.length && !this.html.length) {
32741             cfg.cn[1].cls += ' hide';
32742         }
32743         
32744         if(this.bgimage.length){
32745             cfg.cn.push({
32746                 tag: 'img',
32747                 cls: 'masonry-brick-image-view',
32748                 src: this.bgimage
32749             });
32750         }
32751         
32752         if(this.videourl.length){
32753             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32754             // youtube support only?
32755             cfg.cn.push({
32756                 tag: 'iframe',
32757                 cls: 'masonry-brick-image-view',
32758                 src: vurl,
32759                 frameborder : 0,
32760                 allowfullscreen : true
32761             });
32762         }
32763         
32764         return cfg;
32765         
32766     },
32767     
32768     getSplitAutoCreate : function()
32769     {
32770         var cls = 'masonry-brick masonry-brick-split';
32771         
32772         if(this.href.length){
32773             cls += ' masonry-brick-link';
32774         }
32775         
32776         if(this.bgimage.length){
32777             cls += ' masonry-brick-image';
32778         }
32779         
32780         if(this.size){
32781             cls += ' masonry-' + this.size + '-brick';
32782         }
32783         
32784         switch (this.placetitle) {
32785             case 'center' :
32786                 cls += ' masonry-center-title';
32787                 break;
32788             case 'bottom' :
32789                 cls += ' masonry-bottom-title';
32790                 break;
32791             default:
32792                 if(!this.bgimage.length){
32793                     cls += ' masonry-center-title';
32794                 }
32795
32796                 if(this.bgimage.length){
32797                     cls += ' masonry-bottom-title';
32798                 }
32799                 break;
32800         }
32801         
32802         if(this.cls){
32803             cls += ' ' + this.cls;
32804         }
32805         
32806         var cfg = {
32807             tag: (this.href.length) ? 'a' : 'div',
32808             cls: cls,
32809             cn: [
32810                 {
32811                     tag: 'div',
32812                     cls: 'masonry-brick-split-head',
32813                     cn: [
32814                         {
32815                             tag: 'div',
32816                             cls: 'masonry-brick-paragraph',
32817                             cn: []
32818                         }
32819                     ]
32820                 },
32821                 {
32822                     tag: 'div',
32823                     cls: 'masonry-brick-split-body',
32824                     cn: []
32825                 }
32826             ]
32827         };
32828         
32829         if(this.href.length){
32830             cfg.href = this.href;
32831         }
32832         
32833         if(this.title.length){
32834             cfg.cn[0].cn[0].cn.push({
32835                 tag: 'h4',
32836                 cls: 'masonry-brick-title',
32837                 html: this.title
32838             });
32839         }
32840         
32841         if(this.html.length){
32842             cfg.cn[1].cn.push({
32843                 tag: 'p',
32844                 cls: 'masonry-brick-text',
32845                 html: this.html
32846             });
32847         }
32848
32849         if(this.bgimage.length){
32850             cfg.cn[0].cn.push({
32851                 tag: 'img',
32852                 cls: 'masonry-brick-image-view',
32853                 src: this.bgimage
32854             });
32855         }
32856         
32857         if(this.videourl.length){
32858             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32859             // youtube support only?
32860             cfg.cn[0].cn.cn.push({
32861                 tag: 'iframe',
32862                 cls: 'masonry-brick-image-view',
32863                 src: vurl,
32864                 frameborder : 0,
32865                 allowfullscreen : true
32866             });
32867         }
32868         
32869         return cfg;
32870     },
32871     
32872     initEvents: function() 
32873     {
32874         switch (this.size) {
32875             case 'xs' :
32876                 this.x = 1;
32877                 this.y = 1;
32878                 break;
32879             case 'sm' :
32880                 this.x = 2;
32881                 this.y = 2;
32882                 break;
32883             case 'md' :
32884             case 'md-left' :
32885             case 'md-right' :
32886                 this.x = 3;
32887                 this.y = 3;
32888                 break;
32889             case 'tall' :
32890                 this.x = 2;
32891                 this.y = 3;
32892                 break;
32893             case 'wide' :
32894                 this.x = 3;
32895                 this.y = 2;
32896                 break;
32897             case 'wide-thin' :
32898                 this.x = 3;
32899                 this.y = 1;
32900                 break;
32901                         
32902             default :
32903                 break;
32904         }
32905         
32906         if(Roo.isTouch){
32907             this.el.on('touchstart', this.onTouchStart, this);
32908             this.el.on('touchmove', this.onTouchMove, this);
32909             this.el.on('touchend', this.onTouchEnd, this);
32910             this.el.on('contextmenu', this.onContextMenu, this);
32911         } else {
32912             this.el.on('mouseenter'  ,this.enter, this);
32913             this.el.on('mouseleave', this.leave, this);
32914             this.el.on('click', this.onClick, this);
32915         }
32916         
32917         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32918             this.parent().bricks.push(this);   
32919         }
32920         
32921     },
32922     
32923     onClick: function(e, el)
32924     {
32925         var time = this.endTimer - this.startTimer;
32926         // Roo.log(e.preventDefault());
32927         if(Roo.isTouch){
32928             if(time > 1000){
32929                 e.preventDefault();
32930                 return;
32931             }
32932         }
32933         
32934         if(!this.preventDefault){
32935             return;
32936         }
32937         
32938         e.preventDefault();
32939         
32940         if (this.activeClass != '') {
32941             this.selectBrick();
32942         }
32943         
32944         this.fireEvent('click', this, e);
32945     },
32946     
32947     enter: function(e, el)
32948     {
32949         e.preventDefault();
32950         
32951         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32952             return;
32953         }
32954         
32955         if(this.bgimage.length && this.html.length){
32956             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32957         }
32958     },
32959     
32960     leave: function(e, el)
32961     {
32962         e.preventDefault();
32963         
32964         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32965             return;
32966         }
32967         
32968         if(this.bgimage.length && this.html.length){
32969             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32970         }
32971     },
32972     
32973     onTouchStart: function(e, el)
32974     {
32975 //        e.preventDefault();
32976         
32977         this.touchmoved = false;
32978         
32979         if(!this.isFitContainer){
32980             return;
32981         }
32982         
32983         if(!this.bgimage.length || !this.html.length){
32984             return;
32985         }
32986         
32987         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32988         
32989         this.timer = new Date().getTime();
32990         
32991     },
32992     
32993     onTouchMove: function(e, el)
32994     {
32995         this.touchmoved = true;
32996     },
32997     
32998     onContextMenu : function(e,el)
32999     {
33000         e.preventDefault();
33001         e.stopPropagation();
33002         return false;
33003     },
33004     
33005     onTouchEnd: function(e, el)
33006     {
33007 //        e.preventDefault();
33008         
33009         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33010         
33011             this.leave(e,el);
33012             
33013             return;
33014         }
33015         
33016         if(!this.bgimage.length || !this.html.length){
33017             
33018             if(this.href.length){
33019                 window.location.href = this.href;
33020             }
33021             
33022             return;
33023         }
33024         
33025         if(!this.isFitContainer){
33026             return;
33027         }
33028         
33029         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33030         
33031         window.location.href = this.href;
33032     },
33033     
33034     //selection on single brick only
33035     selectBrick : function() {
33036         
33037         if (!this.parentId) {
33038             return;
33039         }
33040         
33041         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33042         var index = m.selectedBrick.indexOf(this.id);
33043         
33044         if ( index > -1) {
33045             m.selectedBrick.splice(index,1);
33046             this.el.removeClass(this.activeClass);
33047             return;
33048         }
33049         
33050         for(var i = 0; i < m.selectedBrick.length; i++) {
33051             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33052             b.el.removeClass(b.activeClass);
33053         }
33054         
33055         m.selectedBrick = [];
33056         
33057         m.selectedBrick.push(this.id);
33058         this.el.addClass(this.activeClass);
33059         return;
33060     },
33061     
33062     isSelected : function(){
33063         return this.el.hasClass(this.activeClass);
33064         
33065     }
33066 });
33067
33068 Roo.apply(Roo.bootstrap.MasonryBrick, {
33069     
33070     //groups: {},
33071     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33072      /**
33073     * register a Masonry Brick
33074     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33075     */
33076     
33077     register : function(brick)
33078     {
33079         //this.groups[brick.id] = brick;
33080         this.groups.add(brick.id, brick);
33081     },
33082     /**
33083     * fetch a  masonry brick based on the masonry brick ID
33084     * @param {string} the masonry brick to add
33085     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33086     */
33087     
33088     get: function(brick_id) 
33089     {
33090         // if (typeof(this.groups[brick_id]) == 'undefined') {
33091         //     return false;
33092         // }
33093         // return this.groups[brick_id] ;
33094         
33095         if(this.groups.key(brick_id)) {
33096             return this.groups.key(brick_id);
33097         }
33098         
33099         return false;
33100     }
33101     
33102     
33103     
33104 });
33105
33106  /*
33107  * - LGPL
33108  *
33109  * element
33110  * 
33111  */
33112
33113 /**
33114  * @class Roo.bootstrap.Brick
33115  * @extends Roo.bootstrap.Component
33116  * Bootstrap Brick class
33117  * 
33118  * @constructor
33119  * Create a new Brick
33120  * @param {Object} config The config object
33121  */
33122
33123 Roo.bootstrap.Brick = function(config){
33124     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33125     
33126     this.addEvents({
33127         // raw events
33128         /**
33129          * @event click
33130          * When a Brick is click
33131          * @param {Roo.bootstrap.Brick} this
33132          * @param {Roo.EventObject} e
33133          */
33134         "click" : true
33135     });
33136 };
33137
33138 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33139     
33140     /**
33141      * @cfg {String} title
33142      */   
33143     title : '',
33144     /**
33145      * @cfg {String} html
33146      */   
33147     html : '',
33148     /**
33149      * @cfg {String} bgimage
33150      */   
33151     bgimage : '',
33152     /**
33153      * @cfg {String} cls
33154      */   
33155     cls : '',
33156     /**
33157      * @cfg {String} href
33158      */   
33159     href : '',
33160     /**
33161      * @cfg {String} video
33162      */   
33163     video : '',
33164     /**
33165      * @cfg {Boolean} square
33166      */   
33167     square : true,
33168     
33169     getAutoCreate : function()
33170     {
33171         var cls = 'roo-brick';
33172         
33173         if(this.href.length){
33174             cls += ' roo-brick-link';
33175         }
33176         
33177         if(this.bgimage.length){
33178             cls += ' roo-brick-image';
33179         }
33180         
33181         if(!this.html.length && !this.bgimage.length){
33182             cls += ' roo-brick-center-title';
33183         }
33184         
33185         if(!this.html.length && this.bgimage.length){
33186             cls += ' roo-brick-bottom-title';
33187         }
33188         
33189         if(this.cls){
33190             cls += ' ' + this.cls;
33191         }
33192         
33193         var cfg = {
33194             tag: (this.href.length) ? 'a' : 'div',
33195             cls: cls,
33196             cn: [
33197                 {
33198                     tag: 'div',
33199                     cls: 'roo-brick-paragraph',
33200                     cn: []
33201                 }
33202             ]
33203         };
33204         
33205         if(this.href.length){
33206             cfg.href = this.href;
33207         }
33208         
33209         var cn = cfg.cn[0].cn;
33210         
33211         if(this.title.length){
33212             cn.push({
33213                 tag: 'h4',
33214                 cls: 'roo-brick-title',
33215                 html: this.title
33216             });
33217         }
33218         
33219         if(this.html.length){
33220             cn.push({
33221                 tag: 'p',
33222                 cls: 'roo-brick-text',
33223                 html: this.html
33224             });
33225         } else {
33226             cn.cls += ' hide';
33227         }
33228         
33229         if(this.bgimage.length){
33230             cfg.cn.push({
33231                 tag: 'img',
33232                 cls: 'roo-brick-image-view',
33233                 src: this.bgimage
33234             });
33235         }
33236         
33237         return cfg;
33238     },
33239     
33240     initEvents: function() 
33241     {
33242         if(this.title.length || this.html.length){
33243             this.el.on('mouseenter'  ,this.enter, this);
33244             this.el.on('mouseleave', this.leave, this);
33245         }
33246         
33247         Roo.EventManager.onWindowResize(this.resize, this); 
33248         
33249         if(this.bgimage.length){
33250             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33251             this.imageEl.on('load', this.onImageLoad, this);
33252             return;
33253         }
33254         
33255         this.resize();
33256     },
33257     
33258     onImageLoad : function()
33259     {
33260         this.resize();
33261     },
33262     
33263     resize : function()
33264     {
33265         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33266         
33267         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33268         
33269         if(this.bgimage.length){
33270             var image = this.el.select('.roo-brick-image-view', true).first();
33271             
33272             image.setWidth(paragraph.getWidth());
33273             
33274             if(this.square){
33275                 image.setHeight(paragraph.getWidth());
33276             }
33277             
33278             this.el.setHeight(image.getHeight());
33279             paragraph.setHeight(image.getHeight());
33280             
33281         }
33282         
33283     },
33284     
33285     enter: function(e, el)
33286     {
33287         e.preventDefault();
33288         
33289         if(this.bgimage.length){
33290             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33291             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33292         }
33293     },
33294     
33295     leave: function(e, el)
33296     {
33297         e.preventDefault();
33298         
33299         if(this.bgimage.length){
33300             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33301             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33302         }
33303     }
33304     
33305 });
33306
33307  
33308
33309  /*
33310  * - LGPL
33311  *
33312  * Number field 
33313  */
33314
33315 /**
33316  * @class Roo.bootstrap.NumberField
33317  * @extends Roo.bootstrap.Input
33318  * Bootstrap NumberField class
33319  * 
33320  * 
33321  * 
33322  * 
33323  * @constructor
33324  * Create a new NumberField
33325  * @param {Object} config The config object
33326  */
33327
33328 Roo.bootstrap.NumberField = function(config){
33329     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33330 };
33331
33332 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33333     
33334     /**
33335      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33336      */
33337     allowDecimals : true,
33338     /**
33339      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33340      */
33341     decimalSeparator : ".",
33342     /**
33343      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33344      */
33345     decimalPrecision : 2,
33346     /**
33347      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33348      */
33349     allowNegative : true,
33350     
33351     /**
33352      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33353      */
33354     allowZero: true,
33355     /**
33356      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33357      */
33358     minValue : Number.NEGATIVE_INFINITY,
33359     /**
33360      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33361      */
33362     maxValue : Number.MAX_VALUE,
33363     /**
33364      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33365      */
33366     minText : "The minimum value for this field is {0}",
33367     /**
33368      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33369      */
33370     maxText : "The maximum value for this field is {0}",
33371     /**
33372      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33373      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33374      */
33375     nanText : "{0} is not a valid number",
33376     /**
33377      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33378      */
33379     thousandsDelimiter : false,
33380     /**
33381      * @cfg {String} valueAlign alignment of value
33382      */
33383     valueAlign : "left",
33384
33385     getAutoCreate : function()
33386     {
33387         var hiddenInput = {
33388             tag: 'input',
33389             type: 'hidden',
33390             id: Roo.id(),
33391             cls: 'hidden-number-input'
33392         };
33393         
33394         if (this.name) {
33395             hiddenInput.name = this.name;
33396         }
33397         
33398         this.name = '';
33399         
33400         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33401         
33402         this.name = hiddenInput.name;
33403         
33404         if(cfg.cn.length > 0) {
33405             cfg.cn.push(hiddenInput);
33406         }
33407         
33408         return cfg;
33409     },
33410
33411     // private
33412     initEvents : function()
33413     {   
33414         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33415         
33416         var allowed = "0123456789";
33417         
33418         if(this.allowDecimals){
33419             allowed += this.decimalSeparator;
33420         }
33421         
33422         if(this.allowNegative){
33423             allowed += "-";
33424         }
33425         
33426         if(this.thousandsDelimiter) {
33427             allowed += ",";
33428         }
33429         
33430         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33431         
33432         var keyPress = function(e){
33433             
33434             var k = e.getKey();
33435             
33436             var c = e.getCharCode();
33437             
33438             if(
33439                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33440                     allowed.indexOf(String.fromCharCode(c)) === -1
33441             ){
33442                 e.stopEvent();
33443                 return;
33444             }
33445             
33446             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33447                 return;
33448             }
33449             
33450             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33451                 e.stopEvent();
33452             }
33453         };
33454         
33455         this.el.on("keypress", keyPress, this);
33456     },
33457     
33458     validateValue : function(value)
33459     {
33460         
33461         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33462             return false;
33463         }
33464         
33465         var num = this.parseValue(value);
33466         
33467         if(isNaN(num)){
33468             this.markInvalid(String.format(this.nanText, value));
33469             return false;
33470         }
33471         
33472         if(num < this.minValue){
33473             this.markInvalid(String.format(this.minText, this.minValue));
33474             return false;
33475         }
33476         
33477         if(num > this.maxValue){
33478             this.markInvalid(String.format(this.maxText, this.maxValue));
33479             return false;
33480         }
33481         
33482         return true;
33483     },
33484
33485     getValue : function()
33486     {
33487         var v = this.hiddenEl().getValue();
33488         
33489         return this.fixPrecision(this.parseValue(v));
33490     },
33491
33492     parseValue : function(value)
33493     {
33494         if(this.thousandsDelimiter) {
33495             value += "";
33496             r = new RegExp(",", "g");
33497             value = value.replace(r, "");
33498         }
33499         
33500         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33501         return isNaN(value) ? '' : value;
33502     },
33503
33504     fixPrecision : function(value)
33505     {
33506         if(this.thousandsDelimiter) {
33507             value += "";
33508             r = new RegExp(",", "g");
33509             value = value.replace(r, "");
33510         }
33511         
33512         var nan = isNaN(value);
33513         
33514         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33515             return nan ? '' : value;
33516         }
33517         return parseFloat(value).toFixed(this.decimalPrecision);
33518     },
33519
33520     setValue : function(v)
33521     {
33522         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33523         
33524         this.value = v;
33525         
33526         if(this.rendered){
33527             
33528             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33529             
33530             this.inputEl().dom.value = (v == '') ? '' :
33531                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33532             
33533             if(!this.allowZero && v === '0') {
33534                 this.hiddenEl().dom.value = '';
33535                 this.inputEl().dom.value = '';
33536             }
33537             
33538             this.validate();
33539         }
33540     },
33541
33542     decimalPrecisionFcn : function(v)
33543     {
33544         return Math.floor(v);
33545     },
33546
33547     beforeBlur : function()
33548     {
33549         var v = this.parseValue(this.getRawValue());
33550         
33551         if(v || v === 0 || v === ''){
33552             this.setValue(v);
33553         }
33554     },
33555     
33556     hiddenEl : function()
33557     {
33558         return this.el.select('input.hidden-number-input',true).first();
33559     }
33560     
33561 });
33562
33563  
33564
33565 /*
33566 * Licence: LGPL
33567 */
33568
33569 /**
33570  * @class Roo.bootstrap.DocumentSlider
33571  * @extends Roo.bootstrap.Component
33572  * Bootstrap DocumentSlider class
33573  * 
33574  * @constructor
33575  * Create a new DocumentViewer
33576  * @param {Object} config The config object
33577  */
33578
33579 Roo.bootstrap.DocumentSlider = function(config){
33580     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33581     
33582     this.files = [];
33583     
33584     this.addEvents({
33585         /**
33586          * @event initial
33587          * Fire after initEvent
33588          * @param {Roo.bootstrap.DocumentSlider} this
33589          */
33590         "initial" : true,
33591         /**
33592          * @event update
33593          * Fire after update
33594          * @param {Roo.bootstrap.DocumentSlider} this
33595          */
33596         "update" : true,
33597         /**
33598          * @event click
33599          * Fire after click
33600          * @param {Roo.bootstrap.DocumentSlider} this
33601          */
33602         "click" : true
33603     });
33604 };
33605
33606 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33607     
33608     files : false,
33609     
33610     indicator : 0,
33611     
33612     getAutoCreate : function()
33613     {
33614         var cfg = {
33615             tag : 'div',
33616             cls : 'roo-document-slider',
33617             cn : [
33618                 {
33619                     tag : 'div',
33620                     cls : 'roo-document-slider-header',
33621                     cn : [
33622                         {
33623                             tag : 'div',
33624                             cls : 'roo-document-slider-header-title'
33625                         }
33626                     ]
33627                 },
33628                 {
33629                     tag : 'div',
33630                     cls : 'roo-document-slider-body',
33631                     cn : [
33632                         {
33633                             tag : 'div',
33634                             cls : 'roo-document-slider-prev',
33635                             cn : [
33636                                 {
33637                                     tag : 'i',
33638                                     cls : 'fa fa-chevron-left'
33639                                 }
33640                             ]
33641                         },
33642                         {
33643                             tag : 'div',
33644                             cls : 'roo-document-slider-thumb',
33645                             cn : [
33646                                 {
33647                                     tag : 'img',
33648                                     cls : 'roo-document-slider-image'
33649                                 }
33650                             ]
33651                         },
33652                         {
33653                             tag : 'div',
33654                             cls : 'roo-document-slider-next',
33655                             cn : [
33656                                 {
33657                                     tag : 'i',
33658                                     cls : 'fa fa-chevron-right'
33659                                 }
33660                             ]
33661                         }
33662                     ]
33663                 }
33664             ]
33665         };
33666         
33667         return cfg;
33668     },
33669     
33670     initEvents : function()
33671     {
33672         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33673         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33674         
33675         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33676         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33677         
33678         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33679         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33680         
33681         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33682         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33683         
33684         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33685         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33686         
33687         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33688         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33689         
33690         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33691         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33692         
33693         this.thumbEl.on('click', this.onClick, this);
33694         
33695         this.prevIndicator.on('click', this.prev, this);
33696         
33697         this.nextIndicator.on('click', this.next, this);
33698         
33699     },
33700     
33701     initial : function()
33702     {
33703         if(this.files.length){
33704             this.indicator = 1;
33705             this.update()
33706         }
33707         
33708         this.fireEvent('initial', this);
33709     },
33710     
33711     update : function()
33712     {
33713         this.imageEl.attr('src', this.files[this.indicator - 1]);
33714         
33715         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33716         
33717         this.prevIndicator.show();
33718         
33719         if(this.indicator == 1){
33720             this.prevIndicator.hide();
33721         }
33722         
33723         this.nextIndicator.show();
33724         
33725         if(this.indicator == this.files.length){
33726             this.nextIndicator.hide();
33727         }
33728         
33729         this.thumbEl.scrollTo('top');
33730         
33731         this.fireEvent('update', this);
33732     },
33733     
33734     onClick : function(e)
33735     {
33736         e.preventDefault();
33737         
33738         this.fireEvent('click', this);
33739     },
33740     
33741     prev : function(e)
33742     {
33743         e.preventDefault();
33744         
33745         this.indicator = Math.max(1, this.indicator - 1);
33746         
33747         this.update();
33748     },
33749     
33750     next : function(e)
33751     {
33752         e.preventDefault();
33753         
33754         this.indicator = Math.min(this.files.length, this.indicator + 1);
33755         
33756         this.update();
33757     }
33758 });
33759 /*
33760  * - LGPL
33761  *
33762  * RadioSet
33763  *
33764  *
33765  */
33766
33767 /**
33768  * @class Roo.bootstrap.RadioSet
33769  * @extends Roo.bootstrap.Input
33770  * Bootstrap RadioSet class
33771  * @cfg {String} indicatorpos (left|right) default left
33772  * @cfg {Boolean} inline (true|false) inline the element (default true)
33773  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33774  * @constructor
33775  * Create a new RadioSet
33776  * @param {Object} config The config object
33777  */
33778
33779 Roo.bootstrap.RadioSet = function(config){
33780     
33781     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33782     
33783     this.radioes = [];
33784     
33785     Roo.bootstrap.RadioSet.register(this);
33786     
33787     this.addEvents({
33788         /**
33789         * @event check
33790         * Fires when the element is checked or unchecked.
33791         * @param {Roo.bootstrap.RadioSet} this This radio
33792         * @param {Roo.bootstrap.Radio} item The checked item
33793         */
33794        check : true,
33795        /**
33796         * @event click
33797         * Fires when the element is click.
33798         * @param {Roo.bootstrap.RadioSet} this This radio set
33799         * @param {Roo.bootstrap.Radio} item The checked item
33800         * @param {Roo.EventObject} e The event object
33801         */
33802        click : true
33803     });
33804     
33805 };
33806
33807 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33808
33809     radioes : false,
33810     
33811     inline : true,
33812     
33813     weight : '',
33814     
33815     indicatorpos : 'left',
33816     
33817     getAutoCreate : function()
33818     {
33819         var label = {
33820             tag : 'label',
33821             cls : 'roo-radio-set-label',
33822             cn : [
33823                 {
33824                     tag : 'span',
33825                     html : this.fieldLabel
33826                 }
33827             ]
33828         };
33829         
33830         if(this.indicatorpos == 'left'){
33831             label.cn.unshift({
33832                 tag : 'i',
33833                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33834                 tooltip : 'This field is required'
33835             });
33836         } else {
33837             label.cn.push({
33838                 tag : 'i',
33839                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33840                 tooltip : 'This field is required'
33841             });
33842         }
33843         
33844         var items = {
33845             tag : 'div',
33846             cls : 'roo-radio-set-items'
33847         };
33848         
33849         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33850         
33851         if (align === 'left' && this.fieldLabel.length) {
33852             
33853             items = {
33854                 cls : "roo-radio-set-right", 
33855                 cn: [
33856                     items
33857                 ]
33858             };
33859             
33860             if(this.labelWidth > 12){
33861                 label.style = "width: " + this.labelWidth + 'px';
33862             }
33863             
33864             if(this.labelWidth < 13 && this.labelmd == 0){
33865                 this.labelmd = this.labelWidth;
33866             }
33867             
33868             if(this.labellg > 0){
33869                 label.cls += ' col-lg-' + this.labellg;
33870                 items.cls += ' col-lg-' + (12 - this.labellg);
33871             }
33872             
33873             if(this.labelmd > 0){
33874                 label.cls += ' col-md-' + this.labelmd;
33875                 items.cls += ' col-md-' + (12 - this.labelmd);
33876             }
33877             
33878             if(this.labelsm > 0){
33879                 label.cls += ' col-sm-' + this.labelsm;
33880                 items.cls += ' col-sm-' + (12 - this.labelsm);
33881             }
33882             
33883             if(this.labelxs > 0){
33884                 label.cls += ' col-xs-' + this.labelxs;
33885                 items.cls += ' col-xs-' + (12 - this.labelxs);
33886             }
33887         }
33888         
33889         var cfg = {
33890             tag : 'div',
33891             cls : 'roo-radio-set',
33892             cn : [
33893                 {
33894                     tag : 'input',
33895                     cls : 'roo-radio-set-input',
33896                     type : 'hidden',
33897                     name : this.name,
33898                     value : this.value ? this.value :  ''
33899                 },
33900                 label,
33901                 items
33902             ]
33903         };
33904         
33905         if(this.weight.length){
33906             cfg.cls += ' roo-radio-' + this.weight;
33907         }
33908         
33909         if(this.inline) {
33910             cfg.cls += ' roo-radio-set-inline';
33911         }
33912         
33913         var settings=this;
33914         ['xs','sm','md','lg'].map(function(size){
33915             if (settings[size]) {
33916                 cfg.cls += ' col-' + size + '-' + settings[size];
33917             }
33918         });
33919         
33920         return cfg;
33921         
33922     },
33923
33924     initEvents : function()
33925     {
33926         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33927         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33928         
33929         if(!this.fieldLabel.length){
33930             this.labelEl.hide();
33931         }
33932         
33933         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33934         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33935         
33936         this.indicator = this.indicatorEl();
33937         
33938         if(this.indicator){
33939             this.indicator.addClass('invisible');
33940         }
33941         
33942         this.originalValue = this.getValue();
33943         
33944     },
33945     
33946     inputEl: function ()
33947     {
33948         return this.el.select('.roo-radio-set-input', true).first();
33949     },
33950     
33951     getChildContainer : function()
33952     {
33953         return this.itemsEl;
33954     },
33955     
33956     register : function(item)
33957     {
33958         this.radioes.push(item);
33959         
33960     },
33961     
33962     validate : function()
33963     {   
33964         if(this.getVisibilityEl().hasClass('hidden')){
33965             return true;
33966         }
33967         
33968         var valid = false;
33969         
33970         Roo.each(this.radioes, function(i){
33971             if(!i.checked){
33972                 return;
33973             }
33974             
33975             valid = true;
33976             return false;
33977         });
33978         
33979         if(this.allowBlank) {
33980             return true;
33981         }
33982         
33983         if(this.disabled || valid){
33984             this.markValid();
33985             return true;
33986         }
33987         
33988         this.markInvalid();
33989         return false;
33990         
33991     },
33992     
33993     markValid : function()
33994     {
33995         if(this.labelEl.isVisible(true)){
33996             this.indicatorEl().removeClass('visible');
33997             this.indicatorEl().addClass('invisible');
33998         }
33999         
34000         this.el.removeClass([this.invalidClass, this.validClass]);
34001         this.el.addClass(this.validClass);
34002         
34003         this.fireEvent('valid', this);
34004     },
34005     
34006     markInvalid : function(msg)
34007     {
34008         if(this.allowBlank || this.disabled){
34009             return;
34010         }
34011         
34012         if(this.labelEl.isVisible(true)){
34013             this.indicatorEl().removeClass('invisible');
34014             this.indicatorEl().addClass('visible');
34015         }
34016         
34017         this.el.removeClass([this.invalidClass, this.validClass]);
34018         this.el.addClass(this.invalidClass);
34019         
34020         this.fireEvent('invalid', this, msg);
34021         
34022     },
34023     
34024     setValue : function(v, suppressEvent)
34025     {   
34026         if(this.value === v){
34027             return;
34028         }
34029         
34030         this.value = v;
34031         
34032         if(this.rendered){
34033             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34034         }
34035         
34036         Roo.each(this.radioes, function(i){
34037             i.checked = false;
34038             i.el.removeClass('checked');
34039         });
34040         
34041         Roo.each(this.radioes, function(i){
34042             
34043             if(i.value === v || i.value.toString() === v.toString()){
34044                 i.checked = true;
34045                 i.el.addClass('checked');
34046                 
34047                 if(suppressEvent !== true){
34048                     this.fireEvent('check', this, i);
34049                 }
34050                 
34051                 return false;
34052             }
34053             
34054         }, this);
34055         
34056         this.validate();
34057     },
34058     
34059     clearInvalid : function(){
34060         
34061         if(!this.el || this.preventMark){
34062             return;
34063         }
34064         
34065         this.el.removeClass([this.invalidClass]);
34066         
34067         this.fireEvent('valid', this);
34068     }
34069     
34070 });
34071
34072 Roo.apply(Roo.bootstrap.RadioSet, {
34073     
34074     groups: {},
34075     
34076     register : function(set)
34077     {
34078         this.groups[set.name] = set;
34079     },
34080     
34081     get: function(name) 
34082     {
34083         if (typeof(this.groups[name]) == 'undefined') {
34084             return false;
34085         }
34086         
34087         return this.groups[name] ;
34088     }
34089     
34090 });
34091 /*
34092  * Based on:
34093  * Ext JS Library 1.1.1
34094  * Copyright(c) 2006-2007, Ext JS, LLC.
34095  *
34096  * Originally Released Under LGPL - original licence link has changed is not relivant.
34097  *
34098  * Fork - LGPL
34099  * <script type="text/javascript">
34100  */
34101
34102
34103 /**
34104  * @class Roo.bootstrap.SplitBar
34105  * @extends Roo.util.Observable
34106  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34107  * <br><br>
34108  * Usage:
34109  * <pre><code>
34110 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34111                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34112 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34113 split.minSize = 100;
34114 split.maxSize = 600;
34115 split.animate = true;
34116 split.on('moved', splitterMoved);
34117 </code></pre>
34118  * @constructor
34119  * Create a new SplitBar
34120  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34121  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34122  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34123  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34124                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34125                         position of the SplitBar).
34126  */
34127 Roo.bootstrap.SplitBar = function(cfg){
34128     
34129     /** @private */
34130     
34131     //{
34132     //  dragElement : elm
34133     //  resizingElement: el,
34134         // optional..
34135     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34136     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34137         // existingProxy ???
34138     //}
34139     
34140     this.el = Roo.get(cfg.dragElement, true);
34141     this.el.dom.unselectable = "on";
34142     /** @private */
34143     this.resizingEl = Roo.get(cfg.resizingElement, true);
34144
34145     /**
34146      * @private
34147      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34148      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34149      * @type Number
34150      */
34151     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34152     
34153     /**
34154      * The minimum size of the resizing element. (Defaults to 0)
34155      * @type Number
34156      */
34157     this.minSize = 0;
34158     
34159     /**
34160      * The maximum size of the resizing element. (Defaults to 2000)
34161      * @type Number
34162      */
34163     this.maxSize = 2000;
34164     
34165     /**
34166      * Whether to animate the transition to the new size
34167      * @type Boolean
34168      */
34169     this.animate = false;
34170     
34171     /**
34172      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34173      * @type Boolean
34174      */
34175     this.useShim = false;
34176     
34177     /** @private */
34178     this.shim = null;
34179     
34180     if(!cfg.existingProxy){
34181         /** @private */
34182         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34183     }else{
34184         this.proxy = Roo.get(cfg.existingProxy).dom;
34185     }
34186     /** @private */
34187     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34188     
34189     /** @private */
34190     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34191     
34192     /** @private */
34193     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34194     
34195     /** @private */
34196     this.dragSpecs = {};
34197     
34198     /**
34199      * @private The adapter to use to positon and resize elements
34200      */
34201     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34202     this.adapter.init(this);
34203     
34204     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34205         /** @private */
34206         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34207         this.el.addClass("roo-splitbar-h");
34208     }else{
34209         /** @private */
34210         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34211         this.el.addClass("roo-splitbar-v");
34212     }
34213     
34214     this.addEvents({
34215         /**
34216          * @event resize
34217          * Fires when the splitter is moved (alias for {@link #event-moved})
34218          * @param {Roo.bootstrap.SplitBar} this
34219          * @param {Number} newSize the new width or height
34220          */
34221         "resize" : true,
34222         /**
34223          * @event moved
34224          * Fires when the splitter is moved
34225          * @param {Roo.bootstrap.SplitBar} this
34226          * @param {Number} newSize the new width or height
34227          */
34228         "moved" : true,
34229         /**
34230          * @event beforeresize
34231          * Fires before the splitter is dragged
34232          * @param {Roo.bootstrap.SplitBar} this
34233          */
34234         "beforeresize" : true,
34235
34236         "beforeapply" : true
34237     });
34238
34239     Roo.util.Observable.call(this);
34240 };
34241
34242 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34243     onStartProxyDrag : function(x, y){
34244         this.fireEvent("beforeresize", this);
34245         if(!this.overlay){
34246             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34247             o.unselectable();
34248             o.enableDisplayMode("block");
34249             // all splitbars share the same overlay
34250             Roo.bootstrap.SplitBar.prototype.overlay = o;
34251         }
34252         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34253         this.overlay.show();
34254         Roo.get(this.proxy).setDisplayed("block");
34255         var size = this.adapter.getElementSize(this);
34256         this.activeMinSize = this.getMinimumSize();;
34257         this.activeMaxSize = this.getMaximumSize();;
34258         var c1 = size - this.activeMinSize;
34259         var c2 = Math.max(this.activeMaxSize - size, 0);
34260         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34261             this.dd.resetConstraints();
34262             this.dd.setXConstraint(
34263                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34264                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34265             );
34266             this.dd.setYConstraint(0, 0);
34267         }else{
34268             this.dd.resetConstraints();
34269             this.dd.setXConstraint(0, 0);
34270             this.dd.setYConstraint(
34271                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34272                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34273             );
34274          }
34275         this.dragSpecs.startSize = size;
34276         this.dragSpecs.startPoint = [x, y];
34277         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34278     },
34279     
34280     /** 
34281      * @private Called after the drag operation by the DDProxy
34282      */
34283     onEndProxyDrag : function(e){
34284         Roo.get(this.proxy).setDisplayed(false);
34285         var endPoint = Roo.lib.Event.getXY(e);
34286         if(this.overlay){
34287             this.overlay.hide();
34288         }
34289         var newSize;
34290         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34291             newSize = this.dragSpecs.startSize + 
34292                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34293                     endPoint[0] - this.dragSpecs.startPoint[0] :
34294                     this.dragSpecs.startPoint[0] - endPoint[0]
34295                 );
34296         }else{
34297             newSize = this.dragSpecs.startSize + 
34298                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34299                     endPoint[1] - this.dragSpecs.startPoint[1] :
34300                     this.dragSpecs.startPoint[1] - endPoint[1]
34301                 );
34302         }
34303         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34304         if(newSize != this.dragSpecs.startSize){
34305             if(this.fireEvent('beforeapply', this, newSize) !== false){
34306                 this.adapter.setElementSize(this, newSize);
34307                 this.fireEvent("moved", this, newSize);
34308                 this.fireEvent("resize", this, newSize);
34309             }
34310         }
34311     },
34312     
34313     /**
34314      * Get the adapter this SplitBar uses
34315      * @return The adapter object
34316      */
34317     getAdapter : function(){
34318         return this.adapter;
34319     },
34320     
34321     /**
34322      * Set the adapter this SplitBar uses
34323      * @param {Object} adapter A SplitBar adapter object
34324      */
34325     setAdapter : function(adapter){
34326         this.adapter = adapter;
34327         this.adapter.init(this);
34328     },
34329     
34330     /**
34331      * Gets the minimum size for the resizing element
34332      * @return {Number} The minimum size
34333      */
34334     getMinimumSize : function(){
34335         return this.minSize;
34336     },
34337     
34338     /**
34339      * Sets the minimum size for the resizing element
34340      * @param {Number} minSize The minimum size
34341      */
34342     setMinimumSize : function(minSize){
34343         this.minSize = minSize;
34344     },
34345     
34346     /**
34347      * Gets the maximum size for the resizing element
34348      * @return {Number} The maximum size
34349      */
34350     getMaximumSize : function(){
34351         return this.maxSize;
34352     },
34353     
34354     /**
34355      * Sets the maximum size for the resizing element
34356      * @param {Number} maxSize The maximum size
34357      */
34358     setMaximumSize : function(maxSize){
34359         this.maxSize = maxSize;
34360     },
34361     
34362     /**
34363      * Sets the initialize size for the resizing element
34364      * @param {Number} size The initial size
34365      */
34366     setCurrentSize : function(size){
34367         var oldAnimate = this.animate;
34368         this.animate = false;
34369         this.adapter.setElementSize(this, size);
34370         this.animate = oldAnimate;
34371     },
34372     
34373     /**
34374      * Destroy this splitbar. 
34375      * @param {Boolean} removeEl True to remove the element
34376      */
34377     destroy : function(removeEl){
34378         if(this.shim){
34379             this.shim.remove();
34380         }
34381         this.dd.unreg();
34382         this.proxy.parentNode.removeChild(this.proxy);
34383         if(removeEl){
34384             this.el.remove();
34385         }
34386     }
34387 });
34388
34389 /**
34390  * @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.
34391  */
34392 Roo.bootstrap.SplitBar.createProxy = function(dir){
34393     var proxy = new Roo.Element(document.createElement("div"));
34394     proxy.unselectable();
34395     var cls = 'roo-splitbar-proxy';
34396     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34397     document.body.appendChild(proxy.dom);
34398     return proxy.dom;
34399 };
34400
34401 /** 
34402  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34403  * Default Adapter. It assumes the splitter and resizing element are not positioned
34404  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34405  */
34406 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34407 };
34408
34409 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34410     // do nothing for now
34411     init : function(s){
34412     
34413     },
34414     /**
34415      * Called before drag operations to get the current size of the resizing element. 
34416      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34417      */
34418      getElementSize : function(s){
34419         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34420             return s.resizingEl.getWidth();
34421         }else{
34422             return s.resizingEl.getHeight();
34423         }
34424     },
34425     
34426     /**
34427      * Called after drag operations to set the size of the resizing element.
34428      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34429      * @param {Number} newSize The new size to set
34430      * @param {Function} onComplete A function to be invoked when resizing is complete
34431      */
34432     setElementSize : function(s, newSize, onComplete){
34433         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34434             if(!s.animate){
34435                 s.resizingEl.setWidth(newSize);
34436                 if(onComplete){
34437                     onComplete(s, newSize);
34438                 }
34439             }else{
34440                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34441             }
34442         }else{
34443             
34444             if(!s.animate){
34445                 s.resizingEl.setHeight(newSize);
34446                 if(onComplete){
34447                     onComplete(s, newSize);
34448                 }
34449             }else{
34450                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34451             }
34452         }
34453     }
34454 };
34455
34456 /** 
34457  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34458  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34459  * Adapter that  moves the splitter element to align with the resized sizing element. 
34460  * Used with an absolute positioned SplitBar.
34461  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34462  * document.body, make sure you assign an id to the body element.
34463  */
34464 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34465     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34466     this.container = Roo.get(container);
34467 };
34468
34469 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34470     init : function(s){
34471         this.basic.init(s);
34472     },
34473     
34474     getElementSize : function(s){
34475         return this.basic.getElementSize(s);
34476     },
34477     
34478     setElementSize : function(s, newSize, onComplete){
34479         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34480     },
34481     
34482     moveSplitter : function(s){
34483         var yes = Roo.bootstrap.SplitBar;
34484         switch(s.placement){
34485             case yes.LEFT:
34486                 s.el.setX(s.resizingEl.getRight());
34487                 break;
34488             case yes.RIGHT:
34489                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34490                 break;
34491             case yes.TOP:
34492                 s.el.setY(s.resizingEl.getBottom());
34493                 break;
34494             case yes.BOTTOM:
34495                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34496                 break;
34497         }
34498     }
34499 };
34500
34501 /**
34502  * Orientation constant - Create a vertical SplitBar
34503  * @static
34504  * @type Number
34505  */
34506 Roo.bootstrap.SplitBar.VERTICAL = 1;
34507
34508 /**
34509  * Orientation constant - Create a horizontal SplitBar
34510  * @static
34511  * @type Number
34512  */
34513 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34514
34515 /**
34516  * Placement constant - The resizing element is to the left of the splitter element
34517  * @static
34518  * @type Number
34519  */
34520 Roo.bootstrap.SplitBar.LEFT = 1;
34521
34522 /**
34523  * Placement constant - The resizing element is to the right of the splitter element
34524  * @static
34525  * @type Number
34526  */
34527 Roo.bootstrap.SplitBar.RIGHT = 2;
34528
34529 /**
34530  * Placement constant - The resizing element is positioned above the splitter element
34531  * @static
34532  * @type Number
34533  */
34534 Roo.bootstrap.SplitBar.TOP = 3;
34535
34536 /**
34537  * Placement constant - The resizing element is positioned under splitter element
34538  * @static
34539  * @type Number
34540  */
34541 Roo.bootstrap.SplitBar.BOTTOM = 4;
34542 Roo.namespace("Roo.bootstrap.layout");/*
34543  * Based on:
34544  * Ext JS Library 1.1.1
34545  * Copyright(c) 2006-2007, Ext JS, LLC.
34546  *
34547  * Originally Released Under LGPL - original licence link has changed is not relivant.
34548  *
34549  * Fork - LGPL
34550  * <script type="text/javascript">
34551  */
34552
34553 /**
34554  * @class Roo.bootstrap.layout.Manager
34555  * @extends Roo.bootstrap.Component
34556  * Base class for layout managers.
34557  */
34558 Roo.bootstrap.layout.Manager = function(config)
34559 {
34560     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34561
34562
34563
34564
34565
34566     /** false to disable window resize monitoring @type Boolean */
34567     this.monitorWindowResize = true;
34568     this.regions = {};
34569     this.addEvents({
34570         /**
34571          * @event layout
34572          * Fires when a layout is performed.
34573          * @param {Roo.LayoutManager} this
34574          */
34575         "layout" : true,
34576         /**
34577          * @event regionresized
34578          * Fires when the user resizes a region.
34579          * @param {Roo.LayoutRegion} region The resized region
34580          * @param {Number} newSize The new size (width for east/west, height for north/south)
34581          */
34582         "regionresized" : true,
34583         /**
34584          * @event regioncollapsed
34585          * Fires when a region is collapsed.
34586          * @param {Roo.LayoutRegion} region The collapsed region
34587          */
34588         "regioncollapsed" : true,
34589         /**
34590          * @event regionexpanded
34591          * Fires when a region is expanded.
34592          * @param {Roo.LayoutRegion} region The expanded region
34593          */
34594         "regionexpanded" : true
34595     });
34596     this.updating = false;
34597
34598     if (config.el) {
34599         this.el = Roo.get(config.el);
34600         this.initEvents();
34601     }
34602
34603 };
34604
34605 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34606
34607
34608     regions : null,
34609
34610     monitorWindowResize : true,
34611
34612
34613     updating : false,
34614
34615
34616     onRender : function(ct, position)
34617     {
34618         if(!this.el){
34619             this.el = Roo.get(ct);
34620             this.initEvents();
34621         }
34622         //this.fireEvent('render',this);
34623     },
34624
34625
34626     initEvents: function()
34627     {
34628
34629
34630         // ie scrollbar fix
34631         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34632             document.body.scroll = "no";
34633         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34634             this.el.position('relative');
34635         }
34636         this.id = this.el.id;
34637         this.el.addClass("roo-layout-container");
34638         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34639         if(this.el.dom != document.body ) {
34640             this.el.on('resize', this.layout,this);
34641             this.el.on('show', this.layout,this);
34642         }
34643
34644     },
34645
34646     /**
34647      * Returns true if this layout is currently being updated
34648      * @return {Boolean}
34649      */
34650     isUpdating : function(){
34651         return this.updating;
34652     },
34653
34654     /**
34655      * Suspend the LayoutManager from doing auto-layouts while
34656      * making multiple add or remove calls
34657      */
34658     beginUpdate : function(){
34659         this.updating = true;
34660     },
34661
34662     /**
34663      * Restore auto-layouts and optionally disable the manager from performing a layout
34664      * @param {Boolean} noLayout true to disable a layout update
34665      */
34666     endUpdate : function(noLayout){
34667         this.updating = false;
34668         if(!noLayout){
34669             this.layout();
34670         }
34671     },
34672
34673     layout: function(){
34674         // abstract...
34675     },
34676
34677     onRegionResized : function(region, newSize){
34678         this.fireEvent("regionresized", region, newSize);
34679         this.layout();
34680     },
34681
34682     onRegionCollapsed : function(region){
34683         this.fireEvent("regioncollapsed", region);
34684     },
34685
34686     onRegionExpanded : function(region){
34687         this.fireEvent("regionexpanded", region);
34688     },
34689
34690     /**
34691      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34692      * performs box-model adjustments.
34693      * @return {Object} The size as an object {width: (the width), height: (the height)}
34694      */
34695     getViewSize : function()
34696     {
34697         var size;
34698         if(this.el.dom != document.body){
34699             size = this.el.getSize();
34700         }else{
34701             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34702         }
34703         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34704         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34705         return size;
34706     },
34707
34708     /**
34709      * Returns the Element this layout is bound to.
34710      * @return {Roo.Element}
34711      */
34712     getEl : function(){
34713         return this.el;
34714     },
34715
34716     /**
34717      * Returns the specified region.
34718      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34719      * @return {Roo.LayoutRegion}
34720      */
34721     getRegion : function(target){
34722         return this.regions[target.toLowerCase()];
34723     },
34724
34725     onWindowResize : function(){
34726         if(this.monitorWindowResize){
34727             this.layout();
34728         }
34729     }
34730 });
34731 /*
34732  * Based on:
34733  * Ext JS Library 1.1.1
34734  * Copyright(c) 2006-2007, Ext JS, LLC.
34735  *
34736  * Originally Released Under LGPL - original licence link has changed is not relivant.
34737  *
34738  * Fork - LGPL
34739  * <script type="text/javascript">
34740  */
34741 /**
34742  * @class Roo.bootstrap.layout.Border
34743  * @extends Roo.bootstrap.layout.Manager
34744  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34745  * please see: examples/bootstrap/nested.html<br><br>
34746  
34747 <b>The container the layout is rendered into can be either the body element or any other element.
34748 If it is not the body element, the container needs to either be an absolute positioned element,
34749 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34750 the container size if it is not the body element.</b>
34751
34752 * @constructor
34753 * Create a new Border
34754 * @param {Object} config Configuration options
34755  */
34756 Roo.bootstrap.layout.Border = function(config){
34757     config = config || {};
34758     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34759     
34760     
34761     
34762     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34763         if(config[region]){
34764             config[region].region = region;
34765             this.addRegion(config[region]);
34766         }
34767     },this);
34768     
34769 };
34770
34771 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34772
34773 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34774     /**
34775      * Creates and adds a new region if it doesn't already exist.
34776      * @param {String} target The target region key (north, south, east, west or center).
34777      * @param {Object} config The regions config object
34778      * @return {BorderLayoutRegion} The new region
34779      */
34780     addRegion : function(config)
34781     {
34782         if(!this.regions[config.region]){
34783             var r = this.factory(config);
34784             this.bindRegion(r);
34785         }
34786         return this.regions[config.region];
34787     },
34788
34789     // private (kinda)
34790     bindRegion : function(r){
34791         this.regions[r.config.region] = r;
34792         
34793         r.on("visibilitychange",    this.layout, this);
34794         r.on("paneladded",          this.layout, this);
34795         r.on("panelremoved",        this.layout, this);
34796         r.on("invalidated",         this.layout, this);
34797         r.on("resized",             this.onRegionResized, this);
34798         r.on("collapsed",           this.onRegionCollapsed, this);
34799         r.on("expanded",            this.onRegionExpanded, this);
34800     },
34801
34802     /**
34803      * Performs a layout update.
34804      */
34805     layout : function()
34806     {
34807         if(this.updating) {
34808             return;
34809         }
34810         
34811         // render all the rebions if they have not been done alreayd?
34812         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34813             if(this.regions[region] && !this.regions[region].bodyEl){
34814                 this.regions[region].onRender(this.el)
34815             }
34816         },this);
34817         
34818         var size = this.getViewSize();
34819         var w = size.width;
34820         var h = size.height;
34821         var centerW = w;
34822         var centerH = h;
34823         var centerY = 0;
34824         var centerX = 0;
34825         //var x = 0, y = 0;
34826
34827         var rs = this.regions;
34828         var north = rs["north"];
34829         var south = rs["south"]; 
34830         var west = rs["west"];
34831         var east = rs["east"];
34832         var center = rs["center"];
34833         //if(this.hideOnLayout){ // not supported anymore
34834             //c.el.setStyle("display", "none");
34835         //}
34836         if(north && north.isVisible()){
34837             var b = north.getBox();
34838             var m = north.getMargins();
34839             b.width = w - (m.left+m.right);
34840             b.x = m.left;
34841             b.y = m.top;
34842             centerY = b.height + b.y + m.bottom;
34843             centerH -= centerY;
34844             north.updateBox(this.safeBox(b));
34845         }
34846         if(south && south.isVisible()){
34847             var b = south.getBox();
34848             var m = south.getMargins();
34849             b.width = w - (m.left+m.right);
34850             b.x = m.left;
34851             var totalHeight = (b.height + m.top + m.bottom);
34852             b.y = h - totalHeight + m.top;
34853             centerH -= totalHeight;
34854             south.updateBox(this.safeBox(b));
34855         }
34856         if(west && west.isVisible()){
34857             var b = west.getBox();
34858             var m = west.getMargins();
34859             b.height = centerH - (m.top+m.bottom);
34860             b.x = m.left;
34861             b.y = centerY + m.top;
34862             var totalWidth = (b.width + m.left + m.right);
34863             centerX += totalWidth;
34864             centerW -= totalWidth;
34865             west.updateBox(this.safeBox(b));
34866         }
34867         if(east && east.isVisible()){
34868             var b = east.getBox();
34869             var m = east.getMargins();
34870             b.height = centerH - (m.top+m.bottom);
34871             var totalWidth = (b.width + m.left + m.right);
34872             b.x = w - totalWidth + m.left;
34873             b.y = centerY + m.top;
34874             centerW -= totalWidth;
34875             east.updateBox(this.safeBox(b));
34876         }
34877         if(center){
34878             var m = center.getMargins();
34879             var centerBox = {
34880                 x: centerX + m.left,
34881                 y: centerY + m.top,
34882                 width: centerW - (m.left+m.right),
34883                 height: centerH - (m.top+m.bottom)
34884             };
34885             //if(this.hideOnLayout){
34886                 //center.el.setStyle("display", "block");
34887             //}
34888             center.updateBox(this.safeBox(centerBox));
34889         }
34890         this.el.repaint();
34891         this.fireEvent("layout", this);
34892     },
34893
34894     // private
34895     safeBox : function(box){
34896         box.width = Math.max(0, box.width);
34897         box.height = Math.max(0, box.height);
34898         return box;
34899     },
34900
34901     /**
34902      * Adds a ContentPanel (or subclass) to this layout.
34903      * @param {String} target The target region key (north, south, east, west or center).
34904      * @param {Roo.ContentPanel} panel The panel to add
34905      * @return {Roo.ContentPanel} The added panel
34906      */
34907     add : function(target, panel){
34908          
34909         target = target.toLowerCase();
34910         return this.regions[target].add(panel);
34911     },
34912
34913     /**
34914      * Remove a ContentPanel (or subclass) to this layout.
34915      * @param {String} target The target region key (north, south, east, west or center).
34916      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34917      * @return {Roo.ContentPanel} The removed panel
34918      */
34919     remove : function(target, panel){
34920         target = target.toLowerCase();
34921         return this.regions[target].remove(panel);
34922     },
34923
34924     /**
34925      * Searches all regions for a panel with the specified id
34926      * @param {String} panelId
34927      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34928      */
34929     findPanel : function(panelId){
34930         var rs = this.regions;
34931         for(var target in rs){
34932             if(typeof rs[target] != "function"){
34933                 var p = rs[target].getPanel(panelId);
34934                 if(p){
34935                     return p;
34936                 }
34937             }
34938         }
34939         return null;
34940     },
34941
34942     /**
34943      * Searches all regions for a panel with the specified id and activates (shows) it.
34944      * @param {String/ContentPanel} panelId The panels id or the panel itself
34945      * @return {Roo.ContentPanel} The shown panel or null
34946      */
34947     showPanel : function(panelId) {
34948       var rs = this.regions;
34949       for(var target in rs){
34950          var r = rs[target];
34951          if(typeof r != "function"){
34952             if(r.hasPanel(panelId)){
34953                return r.showPanel(panelId);
34954             }
34955          }
34956       }
34957       return null;
34958    },
34959
34960    /**
34961      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34962      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34963      */
34964    /*
34965     restoreState : function(provider){
34966         if(!provider){
34967             provider = Roo.state.Manager;
34968         }
34969         var sm = new Roo.LayoutStateManager();
34970         sm.init(this, provider);
34971     },
34972 */
34973  
34974  
34975     /**
34976      * Adds a xtype elements to the layout.
34977      * <pre><code>
34978
34979 layout.addxtype({
34980        xtype : 'ContentPanel',
34981        region: 'west',
34982        items: [ .... ]
34983    }
34984 );
34985
34986 layout.addxtype({
34987         xtype : 'NestedLayoutPanel',
34988         region: 'west',
34989         layout: {
34990            center: { },
34991            west: { }   
34992         },
34993         items : [ ... list of content panels or nested layout panels.. ]
34994    }
34995 );
34996 </code></pre>
34997      * @param {Object} cfg Xtype definition of item to add.
34998      */
34999     addxtype : function(cfg)
35000     {
35001         // basically accepts a pannel...
35002         // can accept a layout region..!?!?
35003         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35004         
35005         
35006         // theory?  children can only be panels??
35007         
35008         //if (!cfg.xtype.match(/Panel$/)) {
35009         //    return false;
35010         //}
35011         var ret = false;
35012         
35013         if (typeof(cfg.region) == 'undefined') {
35014             Roo.log("Failed to add Panel, region was not set");
35015             Roo.log(cfg);
35016             return false;
35017         }
35018         var region = cfg.region;
35019         delete cfg.region;
35020         
35021           
35022         var xitems = [];
35023         if (cfg.items) {
35024             xitems = cfg.items;
35025             delete cfg.items;
35026         }
35027         var nb = false;
35028         
35029         switch(cfg.xtype) 
35030         {
35031             case 'Content':  // ContentPanel (el, cfg)
35032             case 'Scroll':  // ContentPanel (el, cfg)
35033             case 'View': 
35034                 cfg.autoCreate = true;
35035                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35036                 //} else {
35037                 //    var el = this.el.createChild();
35038                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35039                 //}
35040                 
35041                 this.add(region, ret);
35042                 break;
35043             
35044             /*
35045             case 'TreePanel': // our new panel!
35046                 cfg.el = this.el.createChild();
35047                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35048                 this.add(region, ret);
35049                 break;
35050             */
35051             
35052             case 'Nest': 
35053                 // create a new Layout (which is  a Border Layout...
35054                 
35055                 var clayout = cfg.layout;
35056                 clayout.el  = this.el.createChild();
35057                 clayout.items   = clayout.items  || [];
35058                 
35059                 delete cfg.layout;
35060                 
35061                 // replace this exitems with the clayout ones..
35062                 xitems = clayout.items;
35063                  
35064                 // force background off if it's in center...
35065                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35066                     cfg.background = false;
35067                 }
35068                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35069                 
35070                 
35071                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35072                 //console.log('adding nested layout panel '  + cfg.toSource());
35073                 this.add(region, ret);
35074                 nb = {}; /// find first...
35075                 break;
35076             
35077             case 'Grid':
35078                 
35079                 // needs grid and region
35080                 
35081                 //var el = this.getRegion(region).el.createChild();
35082                 /*
35083                  *var el = this.el.createChild();
35084                 // create the grid first...
35085                 cfg.grid.container = el;
35086                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35087                 */
35088                 
35089                 if (region == 'center' && this.active ) {
35090                     cfg.background = false;
35091                 }
35092                 
35093                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35094                 
35095                 this.add(region, ret);
35096                 /*
35097                 if (cfg.background) {
35098                     // render grid on panel activation (if panel background)
35099                     ret.on('activate', function(gp) {
35100                         if (!gp.grid.rendered) {
35101                     //        gp.grid.render(el);
35102                         }
35103                     });
35104                 } else {
35105                   //  cfg.grid.render(el);
35106                 }
35107                 */
35108                 break;
35109            
35110            
35111             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35112                 // it was the old xcomponent building that caused this before.
35113                 // espeically if border is the top element in the tree.
35114                 ret = this;
35115                 break; 
35116                 
35117                     
35118                 
35119                 
35120                 
35121             default:
35122                 /*
35123                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35124                     
35125                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35126                     this.add(region, ret);
35127                 } else {
35128                 */
35129                     Roo.log(cfg);
35130                     throw "Can not add '" + cfg.xtype + "' to Border";
35131                     return null;
35132              
35133                                 
35134              
35135         }
35136         this.beginUpdate();
35137         // add children..
35138         var region = '';
35139         var abn = {};
35140         Roo.each(xitems, function(i)  {
35141             region = nb && i.region ? i.region : false;
35142             
35143             var add = ret.addxtype(i);
35144            
35145             if (region) {
35146                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35147                 if (!i.background) {
35148                     abn[region] = nb[region] ;
35149                 }
35150             }
35151             
35152         });
35153         this.endUpdate();
35154
35155         // make the last non-background panel active..
35156         //if (nb) { Roo.log(abn); }
35157         if (nb) {
35158             
35159             for(var r in abn) {
35160                 region = this.getRegion(r);
35161                 if (region) {
35162                     // tried using nb[r], but it does not work..
35163                      
35164                     region.showPanel(abn[r]);
35165                    
35166                 }
35167             }
35168         }
35169         return ret;
35170         
35171     },
35172     
35173     
35174 // private
35175     factory : function(cfg)
35176     {
35177         
35178         var validRegions = Roo.bootstrap.layout.Border.regions;
35179
35180         var target = cfg.region;
35181         cfg.mgr = this;
35182         
35183         var r = Roo.bootstrap.layout;
35184         Roo.log(target);
35185         switch(target){
35186             case "north":
35187                 return new r.North(cfg);
35188             case "south":
35189                 return new r.South(cfg);
35190             case "east":
35191                 return new r.East(cfg);
35192             case "west":
35193                 return new r.West(cfg);
35194             case "center":
35195                 return new r.Center(cfg);
35196         }
35197         throw 'Layout region "'+target+'" not supported.';
35198     }
35199     
35200     
35201 });
35202  /*
35203  * Based on:
35204  * Ext JS Library 1.1.1
35205  * Copyright(c) 2006-2007, Ext JS, LLC.
35206  *
35207  * Originally Released Under LGPL - original licence link has changed is not relivant.
35208  *
35209  * Fork - LGPL
35210  * <script type="text/javascript">
35211  */
35212  
35213 /**
35214  * @class Roo.bootstrap.layout.Basic
35215  * @extends Roo.util.Observable
35216  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35217  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35218  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35219  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35220  * @cfg {string}   region  the region that it inhabits..
35221  * @cfg {bool}   skipConfig skip config?
35222  * 
35223
35224  */
35225 Roo.bootstrap.layout.Basic = function(config){
35226     
35227     this.mgr = config.mgr;
35228     
35229     this.position = config.region;
35230     
35231     var skipConfig = config.skipConfig;
35232     
35233     this.events = {
35234         /**
35235          * @scope Roo.BasicLayoutRegion
35236          */
35237         
35238         /**
35239          * @event beforeremove
35240          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35241          * @param {Roo.LayoutRegion} this
35242          * @param {Roo.ContentPanel} panel The panel
35243          * @param {Object} e The cancel event object
35244          */
35245         "beforeremove" : true,
35246         /**
35247          * @event invalidated
35248          * Fires when the layout for this region is changed.
35249          * @param {Roo.LayoutRegion} this
35250          */
35251         "invalidated" : true,
35252         /**
35253          * @event visibilitychange
35254          * Fires when this region is shown or hidden 
35255          * @param {Roo.LayoutRegion} this
35256          * @param {Boolean} visibility true or false
35257          */
35258         "visibilitychange" : true,
35259         /**
35260          * @event paneladded
35261          * Fires when a panel is added. 
35262          * @param {Roo.LayoutRegion} this
35263          * @param {Roo.ContentPanel} panel The panel
35264          */
35265         "paneladded" : true,
35266         /**
35267          * @event panelremoved
35268          * Fires when a panel is removed. 
35269          * @param {Roo.LayoutRegion} this
35270          * @param {Roo.ContentPanel} panel The panel
35271          */
35272         "panelremoved" : true,
35273         /**
35274          * @event beforecollapse
35275          * Fires when this region before collapse.
35276          * @param {Roo.LayoutRegion} this
35277          */
35278         "beforecollapse" : true,
35279         /**
35280          * @event collapsed
35281          * Fires when this region is collapsed.
35282          * @param {Roo.LayoutRegion} this
35283          */
35284         "collapsed" : true,
35285         /**
35286          * @event expanded
35287          * Fires when this region is expanded.
35288          * @param {Roo.LayoutRegion} this
35289          */
35290         "expanded" : true,
35291         /**
35292          * @event slideshow
35293          * Fires when this region is slid into view.
35294          * @param {Roo.LayoutRegion} this
35295          */
35296         "slideshow" : true,
35297         /**
35298          * @event slidehide
35299          * Fires when this region slides out of view. 
35300          * @param {Roo.LayoutRegion} this
35301          */
35302         "slidehide" : true,
35303         /**
35304          * @event panelactivated
35305          * Fires when a panel is activated. 
35306          * @param {Roo.LayoutRegion} this
35307          * @param {Roo.ContentPanel} panel The activated panel
35308          */
35309         "panelactivated" : true,
35310         /**
35311          * @event resized
35312          * Fires when the user resizes this region. 
35313          * @param {Roo.LayoutRegion} this
35314          * @param {Number} newSize The new size (width for east/west, height for north/south)
35315          */
35316         "resized" : true
35317     };
35318     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35319     this.panels = new Roo.util.MixedCollection();
35320     this.panels.getKey = this.getPanelId.createDelegate(this);
35321     this.box = null;
35322     this.activePanel = null;
35323     // ensure listeners are added...
35324     
35325     if (config.listeners || config.events) {
35326         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35327             listeners : config.listeners || {},
35328             events : config.events || {}
35329         });
35330     }
35331     
35332     if(skipConfig !== true){
35333         this.applyConfig(config);
35334     }
35335 };
35336
35337 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35338 {
35339     getPanelId : function(p){
35340         return p.getId();
35341     },
35342     
35343     applyConfig : function(config){
35344         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35345         this.config = config;
35346         
35347     },
35348     
35349     /**
35350      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35351      * the width, for horizontal (north, south) the height.
35352      * @param {Number} newSize The new width or height
35353      */
35354     resizeTo : function(newSize){
35355         var el = this.el ? this.el :
35356                  (this.activePanel ? this.activePanel.getEl() : null);
35357         if(el){
35358             switch(this.position){
35359                 case "east":
35360                 case "west":
35361                     el.setWidth(newSize);
35362                     this.fireEvent("resized", this, newSize);
35363                 break;
35364                 case "north":
35365                 case "south":
35366                     el.setHeight(newSize);
35367                     this.fireEvent("resized", this, newSize);
35368                 break;                
35369             }
35370         }
35371     },
35372     
35373     getBox : function(){
35374         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35375     },
35376     
35377     getMargins : function(){
35378         return this.margins;
35379     },
35380     
35381     updateBox : function(box){
35382         this.box = box;
35383         var el = this.activePanel.getEl();
35384         el.dom.style.left = box.x + "px";
35385         el.dom.style.top = box.y + "px";
35386         this.activePanel.setSize(box.width, box.height);
35387     },
35388     
35389     /**
35390      * Returns the container element for this region.
35391      * @return {Roo.Element}
35392      */
35393     getEl : function(){
35394         return this.activePanel;
35395     },
35396     
35397     /**
35398      * Returns true if this region is currently visible.
35399      * @return {Boolean}
35400      */
35401     isVisible : function(){
35402         return this.activePanel ? true : false;
35403     },
35404     
35405     setActivePanel : function(panel){
35406         panel = this.getPanel(panel);
35407         if(this.activePanel && this.activePanel != panel){
35408             this.activePanel.setActiveState(false);
35409             this.activePanel.getEl().setLeftTop(-10000,-10000);
35410         }
35411         this.activePanel = panel;
35412         panel.setActiveState(true);
35413         if(this.box){
35414             panel.setSize(this.box.width, this.box.height);
35415         }
35416         this.fireEvent("panelactivated", this, panel);
35417         this.fireEvent("invalidated");
35418     },
35419     
35420     /**
35421      * Show the specified panel.
35422      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35423      * @return {Roo.ContentPanel} The shown panel or null
35424      */
35425     showPanel : function(panel){
35426         panel = this.getPanel(panel);
35427         if(panel){
35428             this.setActivePanel(panel);
35429         }
35430         return panel;
35431     },
35432     
35433     /**
35434      * Get the active panel for this region.
35435      * @return {Roo.ContentPanel} The active panel or null
35436      */
35437     getActivePanel : function(){
35438         return this.activePanel;
35439     },
35440     
35441     /**
35442      * Add the passed ContentPanel(s)
35443      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35444      * @return {Roo.ContentPanel} The panel added (if only one was added)
35445      */
35446     add : function(panel){
35447         if(arguments.length > 1){
35448             for(var i = 0, len = arguments.length; i < len; i++) {
35449                 this.add(arguments[i]);
35450             }
35451             return null;
35452         }
35453         if(this.hasPanel(panel)){
35454             this.showPanel(panel);
35455             return panel;
35456         }
35457         var el = panel.getEl();
35458         if(el.dom.parentNode != this.mgr.el.dom){
35459             this.mgr.el.dom.appendChild(el.dom);
35460         }
35461         if(panel.setRegion){
35462             panel.setRegion(this);
35463         }
35464         this.panels.add(panel);
35465         el.setStyle("position", "absolute");
35466         if(!panel.background){
35467             this.setActivePanel(panel);
35468             if(this.config.initialSize && this.panels.getCount()==1){
35469                 this.resizeTo(this.config.initialSize);
35470             }
35471         }
35472         this.fireEvent("paneladded", this, panel);
35473         return panel;
35474     },
35475     
35476     /**
35477      * Returns true if the panel is in this region.
35478      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35479      * @return {Boolean}
35480      */
35481     hasPanel : function(panel){
35482         if(typeof panel == "object"){ // must be panel obj
35483             panel = panel.getId();
35484         }
35485         return this.getPanel(panel) ? true : false;
35486     },
35487     
35488     /**
35489      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35490      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35491      * @param {Boolean} preservePanel Overrides the config preservePanel option
35492      * @return {Roo.ContentPanel} The panel that was removed
35493      */
35494     remove : function(panel, preservePanel){
35495         panel = this.getPanel(panel);
35496         if(!panel){
35497             return null;
35498         }
35499         var e = {};
35500         this.fireEvent("beforeremove", this, panel, e);
35501         if(e.cancel === true){
35502             return null;
35503         }
35504         var panelId = panel.getId();
35505         this.panels.removeKey(panelId);
35506         return panel;
35507     },
35508     
35509     /**
35510      * Returns the panel specified or null if it's not in this region.
35511      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35512      * @return {Roo.ContentPanel}
35513      */
35514     getPanel : function(id){
35515         if(typeof id == "object"){ // must be panel obj
35516             return id;
35517         }
35518         return this.panels.get(id);
35519     },
35520     
35521     /**
35522      * Returns this regions position (north/south/east/west/center).
35523      * @return {String} 
35524      */
35525     getPosition: function(){
35526         return this.position;    
35527     }
35528 });/*
35529  * Based on:
35530  * Ext JS Library 1.1.1
35531  * Copyright(c) 2006-2007, Ext JS, LLC.
35532  *
35533  * Originally Released Under LGPL - original licence link has changed is not relivant.
35534  *
35535  * Fork - LGPL
35536  * <script type="text/javascript">
35537  */
35538  
35539 /**
35540  * @class Roo.bootstrap.layout.Region
35541  * @extends Roo.bootstrap.layout.Basic
35542  * This class represents a region in a layout manager.
35543  
35544  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35545  * @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})
35546  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35547  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35548  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35549  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35550  * @cfg {String}    title           The title for the region (overrides panel titles)
35551  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35552  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35553  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35554  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35555  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35556  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35557  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35558  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35559  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35560  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35561
35562  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35563  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35564  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35565  * @cfg {Number}    width           For East/West panels
35566  * @cfg {Number}    height          For North/South panels
35567  * @cfg {Boolean}   split           To show the splitter
35568  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35569  * 
35570  * @cfg {string}   cls             Extra CSS classes to add to region
35571  * 
35572  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35573  * @cfg {string}   region  the region that it inhabits..
35574  *
35575
35576  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35577  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35578
35579  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35580  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35581  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35582  */
35583 Roo.bootstrap.layout.Region = function(config)
35584 {
35585     this.applyConfig(config);
35586
35587     var mgr = config.mgr;
35588     var pos = config.region;
35589     config.skipConfig = true;
35590     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35591     
35592     if (mgr.el) {
35593         this.onRender(mgr.el);   
35594     }
35595      
35596     this.visible = true;
35597     this.collapsed = false;
35598     this.unrendered_panels = [];
35599 };
35600
35601 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35602
35603     position: '', // set by wrapper (eg. north/south etc..)
35604     unrendered_panels : null,  // unrendered panels.
35605     createBody : function(){
35606         /** This region's body element 
35607         * @type Roo.Element */
35608         this.bodyEl = this.el.createChild({
35609                 tag: "div",
35610                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35611         });
35612     },
35613
35614     onRender: function(ctr, pos)
35615     {
35616         var dh = Roo.DomHelper;
35617         /** This region's container element 
35618         * @type Roo.Element */
35619         this.el = dh.append(ctr.dom, {
35620                 tag: "div",
35621                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35622             }, true);
35623         /** This region's title element 
35624         * @type Roo.Element */
35625     
35626         this.titleEl = dh.append(this.el.dom,
35627             {
35628                     tag: "div",
35629                     unselectable: "on",
35630                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35631                     children:[
35632                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35633                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35634                     ]}, true);
35635         
35636         this.titleEl.enableDisplayMode();
35637         /** This region's title text element 
35638         * @type HTMLElement */
35639         this.titleTextEl = this.titleEl.dom.firstChild;
35640         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35641         /*
35642         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35643         this.closeBtn.enableDisplayMode();
35644         this.closeBtn.on("click", this.closeClicked, this);
35645         this.closeBtn.hide();
35646     */
35647         this.createBody(this.config);
35648         if(this.config.hideWhenEmpty){
35649             this.hide();
35650             this.on("paneladded", this.validateVisibility, this);
35651             this.on("panelremoved", this.validateVisibility, this);
35652         }
35653         if(this.autoScroll){
35654             this.bodyEl.setStyle("overflow", "auto");
35655         }else{
35656             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35657         }
35658         //if(c.titlebar !== false){
35659             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35660                 this.titleEl.hide();
35661             }else{
35662                 this.titleEl.show();
35663                 if(this.config.title){
35664                     this.titleTextEl.innerHTML = this.config.title;
35665                 }
35666             }
35667         //}
35668         if(this.config.collapsed){
35669             this.collapse(true);
35670         }
35671         if(this.config.hidden){
35672             this.hide();
35673         }
35674         
35675         if (this.unrendered_panels && this.unrendered_panels.length) {
35676             for (var i =0;i< this.unrendered_panels.length; i++) {
35677                 this.add(this.unrendered_panels[i]);
35678             }
35679             this.unrendered_panels = null;
35680             
35681         }
35682         
35683     },
35684     
35685     applyConfig : function(c)
35686     {
35687         /*
35688          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35689             var dh = Roo.DomHelper;
35690             if(c.titlebar !== false){
35691                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35692                 this.collapseBtn.on("click", this.collapse, this);
35693                 this.collapseBtn.enableDisplayMode();
35694                 /*
35695                 if(c.showPin === true || this.showPin){
35696                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35697                     this.stickBtn.enableDisplayMode();
35698                     this.stickBtn.on("click", this.expand, this);
35699                     this.stickBtn.hide();
35700                 }
35701                 
35702             }
35703             */
35704             /** This region's collapsed element
35705             * @type Roo.Element */
35706             /*
35707              *
35708             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35709                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35710             ]}, true);
35711             
35712             if(c.floatable !== false){
35713                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35714                this.collapsedEl.on("click", this.collapseClick, this);
35715             }
35716
35717             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35718                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35719                    id: "message", unselectable: "on", style:{"float":"left"}});
35720                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35721              }
35722             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35723             this.expandBtn.on("click", this.expand, this);
35724             
35725         }
35726         
35727         if(this.collapseBtn){
35728             this.collapseBtn.setVisible(c.collapsible == true);
35729         }
35730         
35731         this.cmargins = c.cmargins || this.cmargins ||
35732                          (this.position == "west" || this.position == "east" ?
35733                              {top: 0, left: 2, right:2, bottom: 0} :
35734                              {top: 2, left: 0, right:0, bottom: 2});
35735         */
35736         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35737         
35738         
35739         this.bottomTabs = c.tabPosition != "top";
35740         
35741         this.autoScroll = c.autoScroll || false;
35742         
35743         
35744        
35745         
35746         this.duration = c.duration || .30;
35747         this.slideDuration = c.slideDuration || .45;
35748         this.config = c;
35749        
35750     },
35751     /**
35752      * Returns true if this region is currently visible.
35753      * @return {Boolean}
35754      */
35755     isVisible : function(){
35756         return this.visible;
35757     },
35758
35759     /**
35760      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35761      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35762      */
35763     //setCollapsedTitle : function(title){
35764     //    title = title || "&#160;";
35765      //   if(this.collapsedTitleTextEl){
35766       //      this.collapsedTitleTextEl.innerHTML = title;
35767        // }
35768     //},
35769
35770     getBox : function(){
35771         var b;
35772       //  if(!this.collapsed){
35773             b = this.el.getBox(false, true);
35774        // }else{
35775           //  b = this.collapsedEl.getBox(false, true);
35776         //}
35777         return b;
35778     },
35779
35780     getMargins : function(){
35781         return this.margins;
35782         //return this.collapsed ? this.cmargins : this.margins;
35783     },
35784 /*
35785     highlight : function(){
35786         this.el.addClass("x-layout-panel-dragover");
35787     },
35788
35789     unhighlight : function(){
35790         this.el.removeClass("x-layout-panel-dragover");
35791     },
35792 */
35793     updateBox : function(box)
35794     {
35795         if (!this.bodyEl) {
35796             return; // not rendered yet..
35797         }
35798         
35799         this.box = box;
35800         if(!this.collapsed){
35801             this.el.dom.style.left = box.x + "px";
35802             this.el.dom.style.top = box.y + "px";
35803             this.updateBody(box.width, box.height);
35804         }else{
35805             this.collapsedEl.dom.style.left = box.x + "px";
35806             this.collapsedEl.dom.style.top = box.y + "px";
35807             this.collapsedEl.setSize(box.width, box.height);
35808         }
35809         if(this.tabs){
35810             this.tabs.autoSizeTabs();
35811         }
35812     },
35813
35814     updateBody : function(w, h)
35815     {
35816         if(w !== null){
35817             this.el.setWidth(w);
35818             w -= this.el.getBorderWidth("rl");
35819             if(this.config.adjustments){
35820                 w += this.config.adjustments[0];
35821             }
35822         }
35823         if(h !== null && h > 0){
35824             this.el.setHeight(h);
35825             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35826             h -= this.el.getBorderWidth("tb");
35827             if(this.config.adjustments){
35828                 h += this.config.adjustments[1];
35829             }
35830             this.bodyEl.setHeight(h);
35831             if(this.tabs){
35832                 h = this.tabs.syncHeight(h);
35833             }
35834         }
35835         if(this.panelSize){
35836             w = w !== null ? w : this.panelSize.width;
35837             h = h !== null ? h : this.panelSize.height;
35838         }
35839         if(this.activePanel){
35840             var el = this.activePanel.getEl();
35841             w = w !== null ? w : el.getWidth();
35842             h = h !== null ? h : el.getHeight();
35843             this.panelSize = {width: w, height: h};
35844             this.activePanel.setSize(w, h);
35845         }
35846         if(Roo.isIE && this.tabs){
35847             this.tabs.el.repaint();
35848         }
35849     },
35850
35851     /**
35852      * Returns the container element for this region.
35853      * @return {Roo.Element}
35854      */
35855     getEl : function(){
35856         return this.el;
35857     },
35858
35859     /**
35860      * Hides this region.
35861      */
35862     hide : function(){
35863         //if(!this.collapsed){
35864             this.el.dom.style.left = "-2000px";
35865             this.el.hide();
35866         //}else{
35867          //   this.collapsedEl.dom.style.left = "-2000px";
35868          //   this.collapsedEl.hide();
35869        // }
35870         this.visible = false;
35871         this.fireEvent("visibilitychange", this, false);
35872     },
35873
35874     /**
35875      * Shows this region if it was previously hidden.
35876      */
35877     show : function(){
35878         //if(!this.collapsed){
35879             this.el.show();
35880         //}else{
35881         //    this.collapsedEl.show();
35882        // }
35883         this.visible = true;
35884         this.fireEvent("visibilitychange", this, true);
35885     },
35886 /*
35887     closeClicked : function(){
35888         if(this.activePanel){
35889             this.remove(this.activePanel);
35890         }
35891     },
35892
35893     collapseClick : function(e){
35894         if(this.isSlid){
35895            e.stopPropagation();
35896            this.slideIn();
35897         }else{
35898            e.stopPropagation();
35899            this.slideOut();
35900         }
35901     },
35902 */
35903     /**
35904      * Collapses this region.
35905      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35906      */
35907     /*
35908     collapse : function(skipAnim, skipCheck = false){
35909         if(this.collapsed) {
35910             return;
35911         }
35912         
35913         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35914             
35915             this.collapsed = true;
35916             if(this.split){
35917                 this.split.el.hide();
35918             }
35919             if(this.config.animate && skipAnim !== true){
35920                 this.fireEvent("invalidated", this);
35921                 this.animateCollapse();
35922             }else{
35923                 this.el.setLocation(-20000,-20000);
35924                 this.el.hide();
35925                 this.collapsedEl.show();
35926                 this.fireEvent("collapsed", this);
35927                 this.fireEvent("invalidated", this);
35928             }
35929         }
35930         
35931     },
35932 */
35933     animateCollapse : function(){
35934         // overridden
35935     },
35936
35937     /**
35938      * Expands this region if it was previously collapsed.
35939      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35940      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35941      */
35942     /*
35943     expand : function(e, skipAnim){
35944         if(e) {
35945             e.stopPropagation();
35946         }
35947         if(!this.collapsed || this.el.hasActiveFx()) {
35948             return;
35949         }
35950         if(this.isSlid){
35951             this.afterSlideIn();
35952             skipAnim = true;
35953         }
35954         this.collapsed = false;
35955         if(this.config.animate && skipAnim !== true){
35956             this.animateExpand();
35957         }else{
35958             this.el.show();
35959             if(this.split){
35960                 this.split.el.show();
35961             }
35962             this.collapsedEl.setLocation(-2000,-2000);
35963             this.collapsedEl.hide();
35964             this.fireEvent("invalidated", this);
35965             this.fireEvent("expanded", this);
35966         }
35967     },
35968 */
35969     animateExpand : function(){
35970         // overridden
35971     },
35972
35973     initTabs : function()
35974     {
35975         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35976         
35977         var ts = new Roo.bootstrap.panel.Tabs({
35978                 el: this.bodyEl.dom,
35979                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35980                 disableTooltips: this.config.disableTabTips,
35981                 toolbar : this.config.toolbar
35982             });
35983         
35984         if(this.config.hideTabs){
35985             ts.stripWrap.setDisplayed(false);
35986         }
35987         this.tabs = ts;
35988         ts.resizeTabs = this.config.resizeTabs === true;
35989         ts.minTabWidth = this.config.minTabWidth || 40;
35990         ts.maxTabWidth = this.config.maxTabWidth || 250;
35991         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35992         ts.monitorResize = false;
35993         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35994         ts.bodyEl.addClass('roo-layout-tabs-body');
35995         this.panels.each(this.initPanelAsTab, this);
35996     },
35997
35998     initPanelAsTab : function(panel){
35999         var ti = this.tabs.addTab(
36000             panel.getEl().id,
36001             panel.getTitle(),
36002             null,
36003             this.config.closeOnTab && panel.isClosable(),
36004             panel.tpl
36005         );
36006         if(panel.tabTip !== undefined){
36007             ti.setTooltip(panel.tabTip);
36008         }
36009         ti.on("activate", function(){
36010               this.setActivePanel(panel);
36011         }, this);
36012         
36013         if(this.config.closeOnTab){
36014             ti.on("beforeclose", function(t, e){
36015                 e.cancel = true;
36016                 this.remove(panel);
36017             }, this);
36018         }
36019         
36020         panel.tabItem = ti;
36021         
36022         return ti;
36023     },
36024
36025     updatePanelTitle : function(panel, title)
36026     {
36027         if(this.activePanel == panel){
36028             this.updateTitle(title);
36029         }
36030         if(this.tabs){
36031             var ti = this.tabs.getTab(panel.getEl().id);
36032             ti.setText(title);
36033             if(panel.tabTip !== undefined){
36034                 ti.setTooltip(panel.tabTip);
36035             }
36036         }
36037     },
36038
36039     updateTitle : function(title){
36040         if(this.titleTextEl && !this.config.title){
36041             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36042         }
36043     },
36044
36045     setActivePanel : function(panel)
36046     {
36047         panel = this.getPanel(panel);
36048         if(this.activePanel && this.activePanel != panel){
36049             if(this.activePanel.setActiveState(false) === false){
36050                 return;
36051             }
36052         }
36053         this.activePanel = panel;
36054         panel.setActiveState(true);
36055         if(this.panelSize){
36056             panel.setSize(this.panelSize.width, this.panelSize.height);
36057         }
36058         if(this.closeBtn){
36059             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36060         }
36061         this.updateTitle(panel.getTitle());
36062         if(this.tabs){
36063             this.fireEvent("invalidated", this);
36064         }
36065         this.fireEvent("panelactivated", this, panel);
36066     },
36067
36068     /**
36069      * Shows the specified panel.
36070      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36071      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36072      */
36073     showPanel : function(panel)
36074     {
36075         panel = this.getPanel(panel);
36076         if(panel){
36077             if(this.tabs){
36078                 var tab = this.tabs.getTab(panel.getEl().id);
36079                 if(tab.isHidden()){
36080                     this.tabs.unhideTab(tab.id);
36081                 }
36082                 tab.activate();
36083             }else{
36084                 this.setActivePanel(panel);
36085             }
36086         }
36087         return panel;
36088     },
36089
36090     /**
36091      * Get the active panel for this region.
36092      * @return {Roo.ContentPanel} The active panel or null
36093      */
36094     getActivePanel : function(){
36095         return this.activePanel;
36096     },
36097
36098     validateVisibility : function(){
36099         if(this.panels.getCount() < 1){
36100             this.updateTitle("&#160;");
36101             this.closeBtn.hide();
36102             this.hide();
36103         }else{
36104             if(!this.isVisible()){
36105                 this.show();
36106             }
36107         }
36108     },
36109
36110     /**
36111      * Adds the passed ContentPanel(s) to this region.
36112      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36113      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36114      */
36115     add : function(panel)
36116     {
36117         if(arguments.length > 1){
36118             for(var i = 0, len = arguments.length; i < len; i++) {
36119                 this.add(arguments[i]);
36120             }
36121             return null;
36122         }
36123         
36124         // if we have not been rendered yet, then we can not really do much of this..
36125         if (!this.bodyEl) {
36126             this.unrendered_panels.push(panel);
36127             return panel;
36128         }
36129         
36130         
36131         
36132         
36133         if(this.hasPanel(panel)){
36134             this.showPanel(panel);
36135             return panel;
36136         }
36137         panel.setRegion(this);
36138         this.panels.add(panel);
36139        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36140             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36141             // and hide them... ???
36142             this.bodyEl.dom.appendChild(panel.getEl().dom);
36143             if(panel.background !== true){
36144                 this.setActivePanel(panel);
36145             }
36146             this.fireEvent("paneladded", this, panel);
36147             return panel;
36148         }
36149         */
36150         if(!this.tabs){
36151             this.initTabs();
36152         }else{
36153             this.initPanelAsTab(panel);
36154         }
36155         
36156         
36157         if(panel.background !== true){
36158             this.tabs.activate(panel.getEl().id);
36159         }
36160         this.fireEvent("paneladded", this, panel);
36161         return panel;
36162     },
36163
36164     /**
36165      * Hides the tab for the specified panel.
36166      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36167      */
36168     hidePanel : function(panel){
36169         if(this.tabs && (panel = this.getPanel(panel))){
36170             this.tabs.hideTab(panel.getEl().id);
36171         }
36172     },
36173
36174     /**
36175      * Unhides the tab for a previously hidden panel.
36176      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36177      */
36178     unhidePanel : function(panel){
36179         if(this.tabs && (panel = this.getPanel(panel))){
36180             this.tabs.unhideTab(panel.getEl().id);
36181         }
36182     },
36183
36184     clearPanels : function(){
36185         while(this.panels.getCount() > 0){
36186              this.remove(this.panels.first());
36187         }
36188     },
36189
36190     /**
36191      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36192      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36193      * @param {Boolean} preservePanel Overrides the config preservePanel option
36194      * @return {Roo.ContentPanel} The panel that was removed
36195      */
36196     remove : function(panel, preservePanel)
36197     {
36198         panel = this.getPanel(panel);
36199         if(!panel){
36200             return null;
36201         }
36202         var e = {};
36203         this.fireEvent("beforeremove", this, panel, e);
36204         if(e.cancel === true){
36205             return null;
36206         }
36207         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36208         var panelId = panel.getId();
36209         this.panels.removeKey(panelId);
36210         if(preservePanel){
36211             document.body.appendChild(panel.getEl().dom);
36212         }
36213         if(this.tabs){
36214             this.tabs.removeTab(panel.getEl().id);
36215         }else if (!preservePanel){
36216             this.bodyEl.dom.removeChild(panel.getEl().dom);
36217         }
36218         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36219             var p = this.panels.first();
36220             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36221             tempEl.appendChild(p.getEl().dom);
36222             this.bodyEl.update("");
36223             this.bodyEl.dom.appendChild(p.getEl().dom);
36224             tempEl = null;
36225             this.updateTitle(p.getTitle());
36226             this.tabs = null;
36227             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36228             this.setActivePanel(p);
36229         }
36230         panel.setRegion(null);
36231         if(this.activePanel == panel){
36232             this.activePanel = null;
36233         }
36234         if(this.config.autoDestroy !== false && preservePanel !== true){
36235             try{panel.destroy();}catch(e){}
36236         }
36237         this.fireEvent("panelremoved", this, panel);
36238         return panel;
36239     },
36240
36241     /**
36242      * Returns the TabPanel component used by this region
36243      * @return {Roo.TabPanel}
36244      */
36245     getTabs : function(){
36246         return this.tabs;
36247     },
36248
36249     createTool : function(parentEl, className){
36250         var btn = Roo.DomHelper.append(parentEl, {
36251             tag: "div",
36252             cls: "x-layout-tools-button",
36253             children: [ {
36254                 tag: "div",
36255                 cls: "roo-layout-tools-button-inner " + className,
36256                 html: "&#160;"
36257             }]
36258         }, true);
36259         btn.addClassOnOver("roo-layout-tools-button-over");
36260         return btn;
36261     }
36262 });/*
36263  * Based on:
36264  * Ext JS Library 1.1.1
36265  * Copyright(c) 2006-2007, Ext JS, LLC.
36266  *
36267  * Originally Released Under LGPL - original licence link has changed is not relivant.
36268  *
36269  * Fork - LGPL
36270  * <script type="text/javascript">
36271  */
36272  
36273
36274
36275 /**
36276  * @class Roo.SplitLayoutRegion
36277  * @extends Roo.LayoutRegion
36278  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36279  */
36280 Roo.bootstrap.layout.Split = function(config){
36281     this.cursor = config.cursor;
36282     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36283 };
36284
36285 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36286 {
36287     splitTip : "Drag to resize.",
36288     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36289     useSplitTips : false,
36290
36291     applyConfig : function(config){
36292         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36293     },
36294     
36295     onRender : function(ctr,pos) {
36296         
36297         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36298         if(!this.config.split){
36299             return;
36300         }
36301         if(!this.split){
36302             
36303             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36304                             tag: "div",
36305                             id: this.el.id + "-split",
36306                             cls: "roo-layout-split roo-layout-split-"+this.position,
36307                             html: "&#160;"
36308             });
36309             /** The SplitBar for this region 
36310             * @type Roo.SplitBar */
36311             // does not exist yet...
36312             Roo.log([this.position, this.orientation]);
36313             
36314             this.split = new Roo.bootstrap.SplitBar({
36315                 dragElement : splitEl,
36316                 resizingElement: this.el,
36317                 orientation : this.orientation
36318             });
36319             
36320             this.split.on("moved", this.onSplitMove, this);
36321             this.split.useShim = this.config.useShim === true;
36322             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36323             if(this.useSplitTips){
36324                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36325             }
36326             //if(config.collapsible){
36327             //    this.split.el.on("dblclick", this.collapse,  this);
36328             //}
36329         }
36330         if(typeof this.config.minSize != "undefined"){
36331             this.split.minSize = this.config.minSize;
36332         }
36333         if(typeof this.config.maxSize != "undefined"){
36334             this.split.maxSize = this.config.maxSize;
36335         }
36336         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36337             this.hideSplitter();
36338         }
36339         
36340     },
36341
36342     getHMaxSize : function(){
36343          var cmax = this.config.maxSize || 10000;
36344          var center = this.mgr.getRegion("center");
36345          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36346     },
36347
36348     getVMaxSize : function(){
36349          var cmax = this.config.maxSize || 10000;
36350          var center = this.mgr.getRegion("center");
36351          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36352     },
36353
36354     onSplitMove : function(split, newSize){
36355         this.fireEvent("resized", this, newSize);
36356     },
36357     
36358     /** 
36359      * Returns the {@link Roo.SplitBar} for this region.
36360      * @return {Roo.SplitBar}
36361      */
36362     getSplitBar : function(){
36363         return this.split;
36364     },
36365     
36366     hide : function(){
36367         this.hideSplitter();
36368         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36369     },
36370
36371     hideSplitter : function(){
36372         if(this.split){
36373             this.split.el.setLocation(-2000,-2000);
36374             this.split.el.hide();
36375         }
36376     },
36377
36378     show : function(){
36379         if(this.split){
36380             this.split.el.show();
36381         }
36382         Roo.bootstrap.layout.Split.superclass.show.call(this);
36383     },
36384     
36385     beforeSlide: function(){
36386         if(Roo.isGecko){// firefox overflow auto bug workaround
36387             this.bodyEl.clip();
36388             if(this.tabs) {
36389                 this.tabs.bodyEl.clip();
36390             }
36391             if(this.activePanel){
36392                 this.activePanel.getEl().clip();
36393                 
36394                 if(this.activePanel.beforeSlide){
36395                     this.activePanel.beforeSlide();
36396                 }
36397             }
36398         }
36399     },
36400     
36401     afterSlide : function(){
36402         if(Roo.isGecko){// firefox overflow auto bug workaround
36403             this.bodyEl.unclip();
36404             if(this.tabs) {
36405                 this.tabs.bodyEl.unclip();
36406             }
36407             if(this.activePanel){
36408                 this.activePanel.getEl().unclip();
36409                 if(this.activePanel.afterSlide){
36410                     this.activePanel.afterSlide();
36411                 }
36412             }
36413         }
36414     },
36415
36416     initAutoHide : function(){
36417         if(this.autoHide !== false){
36418             if(!this.autoHideHd){
36419                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36420                 this.autoHideHd = {
36421                     "mouseout": function(e){
36422                         if(!e.within(this.el, true)){
36423                             st.delay(500);
36424                         }
36425                     },
36426                     "mouseover" : function(e){
36427                         st.cancel();
36428                     },
36429                     scope : this
36430                 };
36431             }
36432             this.el.on(this.autoHideHd);
36433         }
36434     },
36435
36436     clearAutoHide : function(){
36437         if(this.autoHide !== false){
36438             this.el.un("mouseout", this.autoHideHd.mouseout);
36439             this.el.un("mouseover", this.autoHideHd.mouseover);
36440         }
36441     },
36442
36443     clearMonitor : function(){
36444         Roo.get(document).un("click", this.slideInIf, this);
36445     },
36446
36447     // these names are backwards but not changed for compat
36448     slideOut : function(){
36449         if(this.isSlid || this.el.hasActiveFx()){
36450             return;
36451         }
36452         this.isSlid = true;
36453         if(this.collapseBtn){
36454             this.collapseBtn.hide();
36455         }
36456         this.closeBtnState = this.closeBtn.getStyle('display');
36457         this.closeBtn.hide();
36458         if(this.stickBtn){
36459             this.stickBtn.show();
36460         }
36461         this.el.show();
36462         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36463         this.beforeSlide();
36464         this.el.setStyle("z-index", 10001);
36465         this.el.slideIn(this.getSlideAnchor(), {
36466             callback: function(){
36467                 this.afterSlide();
36468                 this.initAutoHide();
36469                 Roo.get(document).on("click", this.slideInIf, this);
36470                 this.fireEvent("slideshow", this);
36471             },
36472             scope: this,
36473             block: true
36474         });
36475     },
36476
36477     afterSlideIn : function(){
36478         this.clearAutoHide();
36479         this.isSlid = false;
36480         this.clearMonitor();
36481         this.el.setStyle("z-index", "");
36482         if(this.collapseBtn){
36483             this.collapseBtn.show();
36484         }
36485         this.closeBtn.setStyle('display', this.closeBtnState);
36486         if(this.stickBtn){
36487             this.stickBtn.hide();
36488         }
36489         this.fireEvent("slidehide", this);
36490     },
36491
36492     slideIn : function(cb){
36493         if(!this.isSlid || this.el.hasActiveFx()){
36494             Roo.callback(cb);
36495             return;
36496         }
36497         this.isSlid = false;
36498         this.beforeSlide();
36499         this.el.slideOut(this.getSlideAnchor(), {
36500             callback: function(){
36501                 this.el.setLeftTop(-10000, -10000);
36502                 this.afterSlide();
36503                 this.afterSlideIn();
36504                 Roo.callback(cb);
36505             },
36506             scope: this,
36507             block: true
36508         });
36509     },
36510     
36511     slideInIf : function(e){
36512         if(!e.within(this.el)){
36513             this.slideIn();
36514         }
36515     },
36516
36517     animateCollapse : function(){
36518         this.beforeSlide();
36519         this.el.setStyle("z-index", 20000);
36520         var anchor = this.getSlideAnchor();
36521         this.el.slideOut(anchor, {
36522             callback : function(){
36523                 this.el.setStyle("z-index", "");
36524                 this.collapsedEl.slideIn(anchor, {duration:.3});
36525                 this.afterSlide();
36526                 this.el.setLocation(-10000,-10000);
36527                 this.el.hide();
36528                 this.fireEvent("collapsed", this);
36529             },
36530             scope: this,
36531             block: true
36532         });
36533     },
36534
36535     animateExpand : function(){
36536         this.beforeSlide();
36537         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36538         this.el.setStyle("z-index", 20000);
36539         this.collapsedEl.hide({
36540             duration:.1
36541         });
36542         this.el.slideIn(this.getSlideAnchor(), {
36543             callback : function(){
36544                 this.el.setStyle("z-index", "");
36545                 this.afterSlide();
36546                 if(this.split){
36547                     this.split.el.show();
36548                 }
36549                 this.fireEvent("invalidated", this);
36550                 this.fireEvent("expanded", this);
36551             },
36552             scope: this,
36553             block: true
36554         });
36555     },
36556
36557     anchors : {
36558         "west" : "left",
36559         "east" : "right",
36560         "north" : "top",
36561         "south" : "bottom"
36562     },
36563
36564     sanchors : {
36565         "west" : "l",
36566         "east" : "r",
36567         "north" : "t",
36568         "south" : "b"
36569     },
36570
36571     canchors : {
36572         "west" : "tl-tr",
36573         "east" : "tr-tl",
36574         "north" : "tl-bl",
36575         "south" : "bl-tl"
36576     },
36577
36578     getAnchor : function(){
36579         return this.anchors[this.position];
36580     },
36581
36582     getCollapseAnchor : function(){
36583         return this.canchors[this.position];
36584     },
36585
36586     getSlideAnchor : function(){
36587         return this.sanchors[this.position];
36588     },
36589
36590     getAlignAdj : function(){
36591         var cm = this.cmargins;
36592         switch(this.position){
36593             case "west":
36594                 return [0, 0];
36595             break;
36596             case "east":
36597                 return [0, 0];
36598             break;
36599             case "north":
36600                 return [0, 0];
36601             break;
36602             case "south":
36603                 return [0, 0];
36604             break;
36605         }
36606     },
36607
36608     getExpandAdj : function(){
36609         var c = this.collapsedEl, cm = this.cmargins;
36610         switch(this.position){
36611             case "west":
36612                 return [-(cm.right+c.getWidth()+cm.left), 0];
36613             break;
36614             case "east":
36615                 return [cm.right+c.getWidth()+cm.left, 0];
36616             break;
36617             case "north":
36618                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36619             break;
36620             case "south":
36621                 return [0, cm.top+cm.bottom+c.getHeight()];
36622             break;
36623         }
36624     }
36625 });/*
36626  * Based on:
36627  * Ext JS Library 1.1.1
36628  * Copyright(c) 2006-2007, Ext JS, LLC.
36629  *
36630  * Originally Released Under LGPL - original licence link has changed is not relivant.
36631  *
36632  * Fork - LGPL
36633  * <script type="text/javascript">
36634  */
36635 /*
36636  * These classes are private internal classes
36637  */
36638 Roo.bootstrap.layout.Center = function(config){
36639     config.region = "center";
36640     Roo.bootstrap.layout.Region.call(this, config);
36641     this.visible = true;
36642     this.minWidth = config.minWidth || 20;
36643     this.minHeight = config.minHeight || 20;
36644 };
36645
36646 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36647     hide : function(){
36648         // center panel can't be hidden
36649     },
36650     
36651     show : function(){
36652         // center panel can't be hidden
36653     },
36654     
36655     getMinWidth: function(){
36656         return this.minWidth;
36657     },
36658     
36659     getMinHeight: function(){
36660         return this.minHeight;
36661     }
36662 });
36663
36664
36665
36666
36667  
36668
36669
36670
36671
36672
36673 Roo.bootstrap.layout.North = function(config)
36674 {
36675     config.region = 'north';
36676     config.cursor = 'n-resize';
36677     
36678     Roo.bootstrap.layout.Split.call(this, config);
36679     
36680     
36681     if(this.split){
36682         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36683         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36684         this.split.el.addClass("roo-layout-split-v");
36685     }
36686     var size = config.initialSize || config.height;
36687     if(typeof size != "undefined"){
36688         this.el.setHeight(size);
36689     }
36690 };
36691 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36692 {
36693     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36694     
36695     
36696     
36697     getBox : function(){
36698         if(this.collapsed){
36699             return this.collapsedEl.getBox();
36700         }
36701         var box = this.el.getBox();
36702         if(this.split){
36703             box.height += this.split.el.getHeight();
36704         }
36705         return box;
36706     },
36707     
36708     updateBox : function(box){
36709         if(this.split && !this.collapsed){
36710             box.height -= this.split.el.getHeight();
36711             this.split.el.setLeft(box.x);
36712             this.split.el.setTop(box.y+box.height);
36713             this.split.el.setWidth(box.width);
36714         }
36715         if(this.collapsed){
36716             this.updateBody(box.width, null);
36717         }
36718         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36719     }
36720 });
36721
36722
36723
36724
36725
36726 Roo.bootstrap.layout.South = function(config){
36727     config.region = 'south';
36728     config.cursor = 's-resize';
36729     Roo.bootstrap.layout.Split.call(this, config);
36730     if(this.split){
36731         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36732         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36733         this.split.el.addClass("roo-layout-split-v");
36734     }
36735     var size = config.initialSize || config.height;
36736     if(typeof size != "undefined"){
36737         this.el.setHeight(size);
36738     }
36739 };
36740
36741 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36742     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36743     getBox : function(){
36744         if(this.collapsed){
36745             return this.collapsedEl.getBox();
36746         }
36747         var box = this.el.getBox();
36748         if(this.split){
36749             var sh = this.split.el.getHeight();
36750             box.height += sh;
36751             box.y -= sh;
36752         }
36753         return box;
36754     },
36755     
36756     updateBox : function(box){
36757         if(this.split && !this.collapsed){
36758             var sh = this.split.el.getHeight();
36759             box.height -= sh;
36760             box.y += sh;
36761             this.split.el.setLeft(box.x);
36762             this.split.el.setTop(box.y-sh);
36763             this.split.el.setWidth(box.width);
36764         }
36765         if(this.collapsed){
36766             this.updateBody(box.width, null);
36767         }
36768         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36769     }
36770 });
36771
36772 Roo.bootstrap.layout.East = function(config){
36773     config.region = "east";
36774     config.cursor = "e-resize";
36775     Roo.bootstrap.layout.Split.call(this, config);
36776     if(this.split){
36777         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36778         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36779         this.split.el.addClass("roo-layout-split-h");
36780     }
36781     var size = config.initialSize || config.width;
36782     if(typeof size != "undefined"){
36783         this.el.setWidth(size);
36784     }
36785 };
36786 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36787     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36788     getBox : function(){
36789         if(this.collapsed){
36790             return this.collapsedEl.getBox();
36791         }
36792         var box = this.el.getBox();
36793         if(this.split){
36794             var sw = this.split.el.getWidth();
36795             box.width += sw;
36796             box.x -= sw;
36797         }
36798         return box;
36799     },
36800
36801     updateBox : function(box){
36802         if(this.split && !this.collapsed){
36803             var sw = this.split.el.getWidth();
36804             box.width -= sw;
36805             this.split.el.setLeft(box.x);
36806             this.split.el.setTop(box.y);
36807             this.split.el.setHeight(box.height);
36808             box.x += sw;
36809         }
36810         if(this.collapsed){
36811             this.updateBody(null, box.height);
36812         }
36813         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36814     }
36815 });
36816
36817 Roo.bootstrap.layout.West = function(config){
36818     config.region = "west";
36819     config.cursor = "w-resize";
36820     
36821     Roo.bootstrap.layout.Split.call(this, config);
36822     if(this.split){
36823         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36824         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36825         this.split.el.addClass("roo-layout-split-h");
36826     }
36827     
36828 };
36829 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36830     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36831     
36832     onRender: function(ctr, pos)
36833     {
36834         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36835         var size = this.config.initialSize || this.config.width;
36836         if(typeof size != "undefined"){
36837             this.el.setWidth(size);
36838         }
36839     },
36840     
36841     getBox : function(){
36842         if(this.collapsed){
36843             return this.collapsedEl.getBox();
36844         }
36845         var box = this.el.getBox();
36846         if(this.split){
36847             box.width += this.split.el.getWidth();
36848         }
36849         return box;
36850     },
36851     
36852     updateBox : function(box){
36853         if(this.split && !this.collapsed){
36854             var sw = this.split.el.getWidth();
36855             box.width -= sw;
36856             this.split.el.setLeft(box.x+box.width);
36857             this.split.el.setTop(box.y);
36858             this.split.el.setHeight(box.height);
36859         }
36860         if(this.collapsed){
36861             this.updateBody(null, box.height);
36862         }
36863         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36864     }
36865 });
36866 Roo.namespace("Roo.bootstrap.panel");/*
36867  * Based on:
36868  * Ext JS Library 1.1.1
36869  * Copyright(c) 2006-2007, Ext JS, LLC.
36870  *
36871  * Originally Released Under LGPL - original licence link has changed is not relivant.
36872  *
36873  * Fork - LGPL
36874  * <script type="text/javascript">
36875  */
36876 /**
36877  * @class Roo.ContentPanel
36878  * @extends Roo.util.Observable
36879  * A basic ContentPanel element.
36880  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36881  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36882  * @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
36883  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36884  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36885  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36886  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36887  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36888  * @cfg {String} title          The title for this panel
36889  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36890  * @cfg {String} url            Calls {@link #setUrl} with this value
36891  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36892  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36893  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36894  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36895  * @cfg {Boolean} badges render the badges
36896
36897  * @constructor
36898  * Create a new ContentPanel.
36899  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36900  * @param {String/Object} config A string to set only the title or a config object
36901  * @param {String} content (optional) Set the HTML content for this panel
36902  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36903  */
36904 Roo.bootstrap.panel.Content = function( config){
36905     
36906     this.tpl = config.tpl || false;
36907     
36908     var el = config.el;
36909     var content = config.content;
36910
36911     if(config.autoCreate){ // xtype is available if this is called from factory
36912         el = Roo.id();
36913     }
36914     this.el = Roo.get(el);
36915     if(!this.el && config && config.autoCreate){
36916         if(typeof config.autoCreate == "object"){
36917             if(!config.autoCreate.id){
36918                 config.autoCreate.id = config.id||el;
36919             }
36920             this.el = Roo.DomHelper.append(document.body,
36921                         config.autoCreate, true);
36922         }else{
36923             var elcfg =  {   tag: "div",
36924                             cls: "roo-layout-inactive-content",
36925                             id: config.id||el
36926                             };
36927             if (config.html) {
36928                 elcfg.html = config.html;
36929                 
36930             }
36931                         
36932             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36933         }
36934     } 
36935     this.closable = false;
36936     this.loaded = false;
36937     this.active = false;
36938    
36939       
36940     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36941         
36942         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36943         
36944         this.wrapEl = this.el; //this.el.wrap();
36945         var ti = [];
36946         if (config.toolbar.items) {
36947             ti = config.toolbar.items ;
36948             delete config.toolbar.items ;
36949         }
36950         
36951         var nitems = [];
36952         this.toolbar.render(this.wrapEl, 'before');
36953         for(var i =0;i < ti.length;i++) {
36954           //  Roo.log(['add child', items[i]]);
36955             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36956         }
36957         this.toolbar.items = nitems;
36958         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36959         delete config.toolbar;
36960         
36961     }
36962     /*
36963     // xtype created footer. - not sure if will work as we normally have to render first..
36964     if (this.footer && !this.footer.el && this.footer.xtype) {
36965         if (!this.wrapEl) {
36966             this.wrapEl = this.el.wrap();
36967         }
36968     
36969         this.footer.container = this.wrapEl.createChild();
36970          
36971         this.footer = Roo.factory(this.footer, Roo);
36972         
36973     }
36974     */
36975     
36976      if(typeof config == "string"){
36977         this.title = config;
36978     }else{
36979         Roo.apply(this, config);
36980     }
36981     
36982     if(this.resizeEl){
36983         this.resizeEl = Roo.get(this.resizeEl, true);
36984     }else{
36985         this.resizeEl = this.el;
36986     }
36987     // handle view.xtype
36988     
36989  
36990     
36991     
36992     this.addEvents({
36993         /**
36994          * @event activate
36995          * Fires when this panel is activated. 
36996          * @param {Roo.ContentPanel} this
36997          */
36998         "activate" : true,
36999         /**
37000          * @event deactivate
37001          * Fires when this panel is activated. 
37002          * @param {Roo.ContentPanel} this
37003          */
37004         "deactivate" : true,
37005
37006         /**
37007          * @event resize
37008          * Fires when this panel is resized if fitToFrame is true.
37009          * @param {Roo.ContentPanel} this
37010          * @param {Number} width The width after any component adjustments
37011          * @param {Number} height The height after any component adjustments
37012          */
37013         "resize" : true,
37014         
37015          /**
37016          * @event render
37017          * Fires when this tab is created
37018          * @param {Roo.ContentPanel} this
37019          */
37020         "render" : true
37021         
37022         
37023         
37024     });
37025     
37026
37027     
37028     
37029     if(this.autoScroll){
37030         this.resizeEl.setStyle("overflow", "auto");
37031     } else {
37032         // fix randome scrolling
37033         //this.el.on('scroll', function() {
37034         //    Roo.log('fix random scolling');
37035         //    this.scrollTo('top',0); 
37036         //});
37037     }
37038     content = content || this.content;
37039     if(content){
37040         this.setContent(content);
37041     }
37042     if(config && config.url){
37043         this.setUrl(this.url, this.params, this.loadOnce);
37044     }
37045     
37046     
37047     
37048     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37049     
37050     if (this.view && typeof(this.view.xtype) != 'undefined') {
37051         this.view.el = this.el.appendChild(document.createElement("div"));
37052         this.view = Roo.factory(this.view); 
37053         this.view.render  &&  this.view.render(false, '');  
37054     }
37055     
37056     
37057     this.fireEvent('render', this);
37058 };
37059
37060 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37061     
37062     tabTip : '',
37063     
37064     setRegion : function(region){
37065         this.region = region;
37066         this.setActiveClass(region && !this.background);
37067     },
37068     
37069     
37070     setActiveClass: function(state)
37071     {
37072         if(state){
37073            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37074            this.el.setStyle('position','relative');
37075         }else{
37076            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37077            this.el.setStyle('position', 'absolute');
37078         } 
37079     },
37080     
37081     /**
37082      * Returns the toolbar for this Panel if one was configured. 
37083      * @return {Roo.Toolbar} 
37084      */
37085     getToolbar : function(){
37086         return this.toolbar;
37087     },
37088     
37089     setActiveState : function(active)
37090     {
37091         this.active = active;
37092         this.setActiveClass(active);
37093         if(!active){
37094             if(this.fireEvent("deactivate", this) === false){
37095                 return false;
37096             }
37097             return true;
37098         }
37099         this.fireEvent("activate", this);
37100         return true;
37101     },
37102     /**
37103      * Updates this panel's element
37104      * @param {String} content The new content
37105      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37106     */
37107     setContent : function(content, loadScripts){
37108         this.el.update(content, loadScripts);
37109     },
37110
37111     ignoreResize : function(w, h){
37112         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37113             return true;
37114         }else{
37115             this.lastSize = {width: w, height: h};
37116             return false;
37117         }
37118     },
37119     /**
37120      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37121      * @return {Roo.UpdateManager} The UpdateManager
37122      */
37123     getUpdateManager : function(){
37124         return this.el.getUpdateManager();
37125     },
37126      /**
37127      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37128      * @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:
37129 <pre><code>
37130 panel.load({
37131     url: "your-url.php",
37132     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37133     callback: yourFunction,
37134     scope: yourObject, //(optional scope)
37135     discardUrl: false,
37136     nocache: false,
37137     text: "Loading...",
37138     timeout: 30,
37139     scripts: false
37140 });
37141 </code></pre>
37142      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37143      * 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.
37144      * @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}
37145      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37146      * @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.
37147      * @return {Roo.ContentPanel} this
37148      */
37149     load : function(){
37150         var um = this.el.getUpdateManager();
37151         um.update.apply(um, arguments);
37152         return this;
37153     },
37154
37155
37156     /**
37157      * 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.
37158      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37159      * @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)
37160      * @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)
37161      * @return {Roo.UpdateManager} The UpdateManager
37162      */
37163     setUrl : function(url, params, loadOnce){
37164         if(this.refreshDelegate){
37165             this.removeListener("activate", this.refreshDelegate);
37166         }
37167         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37168         this.on("activate", this.refreshDelegate);
37169         return this.el.getUpdateManager();
37170     },
37171     
37172     _handleRefresh : function(url, params, loadOnce){
37173         if(!loadOnce || !this.loaded){
37174             var updater = this.el.getUpdateManager();
37175             updater.update(url, params, this._setLoaded.createDelegate(this));
37176         }
37177     },
37178     
37179     _setLoaded : function(){
37180         this.loaded = true;
37181     }, 
37182     
37183     /**
37184      * Returns this panel's id
37185      * @return {String} 
37186      */
37187     getId : function(){
37188         return this.el.id;
37189     },
37190     
37191     /** 
37192      * Returns this panel's element - used by regiosn to add.
37193      * @return {Roo.Element} 
37194      */
37195     getEl : function(){
37196         return this.wrapEl || this.el;
37197     },
37198     
37199    
37200     
37201     adjustForComponents : function(width, height)
37202     {
37203         //Roo.log('adjustForComponents ');
37204         if(this.resizeEl != this.el){
37205             width -= this.el.getFrameWidth('lr');
37206             height -= this.el.getFrameWidth('tb');
37207         }
37208         if(this.toolbar){
37209             var te = this.toolbar.getEl();
37210             te.setWidth(width);
37211             height -= te.getHeight();
37212         }
37213         if(this.footer){
37214             var te = this.footer.getEl();
37215             te.setWidth(width);
37216             height -= te.getHeight();
37217         }
37218         
37219         
37220         if(this.adjustments){
37221             width += this.adjustments[0];
37222             height += this.adjustments[1];
37223         }
37224         return {"width": width, "height": height};
37225     },
37226     
37227     setSize : function(width, height){
37228         if(this.fitToFrame && !this.ignoreResize(width, height)){
37229             if(this.fitContainer && this.resizeEl != this.el){
37230                 this.el.setSize(width, height);
37231             }
37232             var size = this.adjustForComponents(width, height);
37233             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37234             this.fireEvent('resize', this, size.width, size.height);
37235         }
37236     },
37237     
37238     /**
37239      * Returns this panel's title
37240      * @return {String} 
37241      */
37242     getTitle : function(){
37243         
37244         if (typeof(this.title) != 'object') {
37245             return this.title;
37246         }
37247         
37248         var t = '';
37249         for (var k in this.title) {
37250             if (!this.title.hasOwnProperty(k)) {
37251                 continue;
37252             }
37253             
37254             if (k.indexOf('-') >= 0) {
37255                 var s = k.split('-');
37256                 for (var i = 0; i<s.length; i++) {
37257                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37258                 }
37259             } else {
37260                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37261             }
37262         }
37263         return t;
37264     },
37265     
37266     /**
37267      * Set this panel's title
37268      * @param {String} title
37269      */
37270     setTitle : function(title){
37271         this.title = title;
37272         if(this.region){
37273             this.region.updatePanelTitle(this, title);
37274         }
37275     },
37276     
37277     /**
37278      * Returns true is this panel was configured to be closable
37279      * @return {Boolean} 
37280      */
37281     isClosable : function(){
37282         return this.closable;
37283     },
37284     
37285     beforeSlide : function(){
37286         this.el.clip();
37287         this.resizeEl.clip();
37288     },
37289     
37290     afterSlide : function(){
37291         this.el.unclip();
37292         this.resizeEl.unclip();
37293     },
37294     
37295     /**
37296      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37297      *   Will fail silently if the {@link #setUrl} method has not been called.
37298      *   This does not activate the panel, just updates its content.
37299      */
37300     refresh : function(){
37301         if(this.refreshDelegate){
37302            this.loaded = false;
37303            this.refreshDelegate();
37304         }
37305     },
37306     
37307     /**
37308      * Destroys this panel
37309      */
37310     destroy : function(){
37311         this.el.removeAllListeners();
37312         var tempEl = document.createElement("span");
37313         tempEl.appendChild(this.el.dom);
37314         tempEl.innerHTML = "";
37315         this.el.remove();
37316         this.el = null;
37317     },
37318     
37319     /**
37320      * form - if the content panel contains a form - this is a reference to it.
37321      * @type {Roo.form.Form}
37322      */
37323     form : false,
37324     /**
37325      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37326      *    This contains a reference to it.
37327      * @type {Roo.View}
37328      */
37329     view : false,
37330     
37331       /**
37332      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37333      * <pre><code>
37334
37335 layout.addxtype({
37336        xtype : 'Form',
37337        items: [ .... ]
37338    }
37339 );
37340
37341 </code></pre>
37342      * @param {Object} cfg Xtype definition of item to add.
37343      */
37344     
37345     
37346     getChildContainer: function () {
37347         return this.getEl();
37348     }
37349     
37350     
37351     /*
37352         var  ret = new Roo.factory(cfg);
37353         return ret;
37354         
37355         
37356         // add form..
37357         if (cfg.xtype.match(/^Form$/)) {
37358             
37359             var el;
37360             //if (this.footer) {
37361             //    el = this.footer.container.insertSibling(false, 'before');
37362             //} else {
37363                 el = this.el.createChild();
37364             //}
37365
37366             this.form = new  Roo.form.Form(cfg);
37367             
37368             
37369             if ( this.form.allItems.length) {
37370                 this.form.render(el.dom);
37371             }
37372             return this.form;
37373         }
37374         // should only have one of theses..
37375         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37376             // views.. should not be just added - used named prop 'view''
37377             
37378             cfg.el = this.el.appendChild(document.createElement("div"));
37379             // factory?
37380             
37381             var ret = new Roo.factory(cfg);
37382              
37383              ret.render && ret.render(false, ''); // render blank..
37384             this.view = ret;
37385             return ret;
37386         }
37387         return false;
37388     }
37389     \*/
37390 });
37391  
37392 /**
37393  * @class Roo.bootstrap.panel.Grid
37394  * @extends Roo.bootstrap.panel.Content
37395  * @constructor
37396  * Create a new GridPanel.
37397  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37398  * @param {Object} config A the config object
37399   
37400  */
37401
37402
37403
37404 Roo.bootstrap.panel.Grid = function(config)
37405 {
37406     
37407       
37408     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37409         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37410
37411     config.el = this.wrapper;
37412     //this.el = this.wrapper;
37413     
37414       if (config.container) {
37415         // ctor'ed from a Border/panel.grid
37416         
37417         
37418         this.wrapper.setStyle("overflow", "hidden");
37419         this.wrapper.addClass('roo-grid-container');
37420
37421     }
37422     
37423     
37424     if(config.toolbar){
37425         var tool_el = this.wrapper.createChild();    
37426         this.toolbar = Roo.factory(config.toolbar);
37427         var ti = [];
37428         if (config.toolbar.items) {
37429             ti = config.toolbar.items ;
37430             delete config.toolbar.items ;
37431         }
37432         
37433         var nitems = [];
37434         this.toolbar.render(tool_el);
37435         for(var i =0;i < ti.length;i++) {
37436           //  Roo.log(['add child', items[i]]);
37437             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37438         }
37439         this.toolbar.items = nitems;
37440         
37441         delete config.toolbar;
37442     }
37443     
37444     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37445     config.grid.scrollBody = true;;
37446     config.grid.monitorWindowResize = false; // turn off autosizing
37447     config.grid.autoHeight = false;
37448     config.grid.autoWidth = false;
37449     
37450     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37451     
37452     if (config.background) {
37453         // render grid on panel activation (if panel background)
37454         this.on('activate', function(gp) {
37455             if (!gp.grid.rendered) {
37456                 gp.grid.render(this.wrapper);
37457                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37458             }
37459         });
37460             
37461     } else {
37462         this.grid.render(this.wrapper);
37463         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37464
37465     }
37466     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37467     // ??? needed ??? config.el = this.wrapper;
37468     
37469     
37470     
37471   
37472     // xtype created footer. - not sure if will work as we normally have to render first..
37473     if (this.footer && !this.footer.el && this.footer.xtype) {
37474         
37475         var ctr = this.grid.getView().getFooterPanel(true);
37476         this.footer.dataSource = this.grid.dataSource;
37477         this.footer = Roo.factory(this.footer, Roo);
37478         this.footer.render(ctr);
37479         
37480     }
37481     
37482     
37483     
37484     
37485      
37486 };
37487
37488 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37489     getId : function(){
37490         return this.grid.id;
37491     },
37492     
37493     /**
37494      * Returns the grid for this panel
37495      * @return {Roo.bootstrap.Table} 
37496      */
37497     getGrid : function(){
37498         return this.grid;    
37499     },
37500     
37501     setSize : function(width, height){
37502         if(!this.ignoreResize(width, height)){
37503             var grid = this.grid;
37504             var size = this.adjustForComponents(width, height);
37505             var gridel = grid.getGridEl();
37506             gridel.setSize(size.width, size.height);
37507             /*
37508             var thd = grid.getGridEl().select('thead',true).first();
37509             var tbd = grid.getGridEl().select('tbody', true).first();
37510             if (tbd) {
37511                 tbd.setSize(width, height - thd.getHeight());
37512             }
37513             */
37514             grid.autoSize();
37515         }
37516     },
37517      
37518     
37519     
37520     beforeSlide : function(){
37521         this.grid.getView().scroller.clip();
37522     },
37523     
37524     afterSlide : function(){
37525         this.grid.getView().scroller.unclip();
37526     },
37527     
37528     destroy : function(){
37529         this.grid.destroy();
37530         delete this.grid;
37531         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37532     }
37533 });
37534
37535 /**
37536  * @class Roo.bootstrap.panel.Nest
37537  * @extends Roo.bootstrap.panel.Content
37538  * @constructor
37539  * Create a new Panel, that can contain a layout.Border.
37540  * 
37541  * 
37542  * @param {Roo.BorderLayout} layout The layout for this panel
37543  * @param {String/Object} config A string to set only the title or a config object
37544  */
37545 Roo.bootstrap.panel.Nest = function(config)
37546 {
37547     // construct with only one argument..
37548     /* FIXME - implement nicer consturctors
37549     if (layout.layout) {
37550         config = layout;
37551         layout = config.layout;
37552         delete config.layout;
37553     }
37554     if (layout.xtype && !layout.getEl) {
37555         // then layout needs constructing..
37556         layout = Roo.factory(layout, Roo);
37557     }
37558     */
37559     
37560     config.el =  config.layout.getEl();
37561     
37562     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37563     
37564     config.layout.monitorWindowResize = false; // turn off autosizing
37565     this.layout = config.layout;
37566     this.layout.getEl().addClass("roo-layout-nested-layout");
37567     
37568     
37569     
37570     
37571 };
37572
37573 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37574
37575     setSize : function(width, height){
37576         if(!this.ignoreResize(width, height)){
37577             var size = this.adjustForComponents(width, height);
37578             var el = this.layout.getEl();
37579             if (size.height < 1) {
37580                 el.setWidth(size.width);   
37581             } else {
37582                 el.setSize(size.width, size.height);
37583             }
37584             var touch = el.dom.offsetWidth;
37585             this.layout.layout();
37586             // ie requires a double layout on the first pass
37587             if(Roo.isIE && !this.initialized){
37588                 this.initialized = true;
37589                 this.layout.layout();
37590             }
37591         }
37592     },
37593     
37594     // activate all subpanels if not currently active..
37595     
37596     setActiveState : function(active){
37597         this.active = active;
37598         this.setActiveClass(active);
37599         
37600         if(!active){
37601             this.fireEvent("deactivate", this);
37602             return;
37603         }
37604         
37605         this.fireEvent("activate", this);
37606         // not sure if this should happen before or after..
37607         if (!this.layout) {
37608             return; // should not happen..
37609         }
37610         var reg = false;
37611         for (var r in this.layout.regions) {
37612             reg = this.layout.getRegion(r);
37613             if (reg.getActivePanel()) {
37614                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37615                 reg.setActivePanel(reg.getActivePanel());
37616                 continue;
37617             }
37618             if (!reg.panels.length) {
37619                 continue;
37620             }
37621             reg.showPanel(reg.getPanel(0));
37622         }
37623         
37624         
37625         
37626         
37627     },
37628     
37629     /**
37630      * Returns the nested BorderLayout for this panel
37631      * @return {Roo.BorderLayout} 
37632      */
37633     getLayout : function(){
37634         return this.layout;
37635     },
37636     
37637      /**
37638      * Adds a xtype elements to the layout of the nested panel
37639      * <pre><code>
37640
37641 panel.addxtype({
37642        xtype : 'ContentPanel',
37643        region: 'west',
37644        items: [ .... ]
37645    }
37646 );
37647
37648 panel.addxtype({
37649         xtype : 'NestedLayoutPanel',
37650         region: 'west',
37651         layout: {
37652            center: { },
37653            west: { }   
37654         },
37655         items : [ ... list of content panels or nested layout panels.. ]
37656    }
37657 );
37658 </code></pre>
37659      * @param {Object} cfg Xtype definition of item to add.
37660      */
37661     addxtype : function(cfg) {
37662         return this.layout.addxtype(cfg);
37663     
37664     }
37665 });        /*
37666  * Based on:
37667  * Ext JS Library 1.1.1
37668  * Copyright(c) 2006-2007, Ext JS, LLC.
37669  *
37670  * Originally Released Under LGPL - original licence link has changed is not relivant.
37671  *
37672  * Fork - LGPL
37673  * <script type="text/javascript">
37674  */
37675 /**
37676  * @class Roo.TabPanel
37677  * @extends Roo.util.Observable
37678  * A lightweight tab container.
37679  * <br><br>
37680  * Usage:
37681  * <pre><code>
37682 // basic tabs 1, built from existing content
37683 var tabs = new Roo.TabPanel("tabs1");
37684 tabs.addTab("script", "View Script");
37685 tabs.addTab("markup", "View Markup");
37686 tabs.activate("script");
37687
37688 // more advanced tabs, built from javascript
37689 var jtabs = new Roo.TabPanel("jtabs");
37690 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37691
37692 // set up the UpdateManager
37693 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37694 var updater = tab2.getUpdateManager();
37695 updater.setDefaultUrl("ajax1.htm");
37696 tab2.on('activate', updater.refresh, updater, true);
37697
37698 // Use setUrl for Ajax loading
37699 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37700 tab3.setUrl("ajax2.htm", null, true);
37701
37702 // Disabled tab
37703 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37704 tab4.disable();
37705
37706 jtabs.activate("jtabs-1");
37707  * </code></pre>
37708  * @constructor
37709  * Create a new TabPanel.
37710  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37711  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37712  */
37713 Roo.bootstrap.panel.Tabs = function(config){
37714     /**
37715     * The container element for this TabPanel.
37716     * @type Roo.Element
37717     */
37718     this.el = Roo.get(config.el);
37719     delete config.el;
37720     if(config){
37721         if(typeof config == "boolean"){
37722             this.tabPosition = config ? "bottom" : "top";
37723         }else{
37724             Roo.apply(this, config);
37725         }
37726     }
37727     
37728     if(this.tabPosition == "bottom"){
37729         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37730         this.el.addClass("roo-tabs-bottom");
37731     }
37732     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37733     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37734     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37735     if(Roo.isIE){
37736         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37737     }
37738     if(this.tabPosition != "bottom"){
37739         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37740          * @type Roo.Element
37741          */
37742         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37743         this.el.addClass("roo-tabs-top");
37744     }
37745     this.items = [];
37746
37747     this.bodyEl.setStyle("position", "relative");
37748
37749     this.active = null;
37750     this.activateDelegate = this.activate.createDelegate(this);
37751
37752     this.addEvents({
37753         /**
37754          * @event tabchange
37755          * Fires when the active tab changes
37756          * @param {Roo.TabPanel} this
37757          * @param {Roo.TabPanelItem} activePanel The new active tab
37758          */
37759         "tabchange": true,
37760         /**
37761          * @event beforetabchange
37762          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37763          * @param {Roo.TabPanel} this
37764          * @param {Object} e Set cancel to true on this object to cancel the tab change
37765          * @param {Roo.TabPanelItem} tab The tab being changed to
37766          */
37767         "beforetabchange" : true
37768     });
37769
37770     Roo.EventManager.onWindowResize(this.onResize, this);
37771     this.cpad = this.el.getPadding("lr");
37772     this.hiddenCount = 0;
37773
37774
37775     // toolbar on the tabbar support...
37776     if (this.toolbar) {
37777         alert("no toolbar support yet");
37778         this.toolbar  = false;
37779         /*
37780         var tcfg = this.toolbar;
37781         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37782         this.toolbar = new Roo.Toolbar(tcfg);
37783         if (Roo.isSafari) {
37784             var tbl = tcfg.container.child('table', true);
37785             tbl.setAttribute('width', '100%');
37786         }
37787         */
37788         
37789     }
37790    
37791
37792
37793     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37794 };
37795
37796 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37797     /*
37798      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37799      */
37800     tabPosition : "top",
37801     /*
37802      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37803      */
37804     currentTabWidth : 0,
37805     /*
37806      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37807      */
37808     minTabWidth : 40,
37809     /*
37810      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37811      */
37812     maxTabWidth : 250,
37813     /*
37814      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37815      */
37816     preferredTabWidth : 175,
37817     /*
37818      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37819      */
37820     resizeTabs : false,
37821     /*
37822      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37823      */
37824     monitorResize : true,
37825     /*
37826      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37827      */
37828     toolbar : false,
37829
37830     /**
37831      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37832      * @param {String} id The id of the div to use <b>or create</b>
37833      * @param {String} text The text for the tab
37834      * @param {String} content (optional) Content to put in the TabPanelItem body
37835      * @param {Boolean} closable (optional) True to create a close icon on the tab
37836      * @return {Roo.TabPanelItem} The created TabPanelItem
37837      */
37838     addTab : function(id, text, content, closable, tpl)
37839     {
37840         var item = new Roo.bootstrap.panel.TabItem({
37841             panel: this,
37842             id : id,
37843             text : text,
37844             closable : closable,
37845             tpl : tpl
37846         });
37847         this.addTabItem(item);
37848         if(content){
37849             item.setContent(content);
37850         }
37851         return item;
37852     },
37853
37854     /**
37855      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37856      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37857      * @return {Roo.TabPanelItem}
37858      */
37859     getTab : function(id){
37860         return this.items[id];
37861     },
37862
37863     /**
37864      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37865      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37866      */
37867     hideTab : function(id){
37868         var t = this.items[id];
37869         if(!t.isHidden()){
37870            t.setHidden(true);
37871            this.hiddenCount++;
37872            this.autoSizeTabs();
37873         }
37874     },
37875
37876     /**
37877      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37878      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37879      */
37880     unhideTab : function(id){
37881         var t = this.items[id];
37882         if(t.isHidden()){
37883            t.setHidden(false);
37884            this.hiddenCount--;
37885            this.autoSizeTabs();
37886         }
37887     },
37888
37889     /**
37890      * Adds an existing {@link Roo.TabPanelItem}.
37891      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37892      */
37893     addTabItem : function(item){
37894         this.items[item.id] = item;
37895         this.items.push(item);
37896       //  if(this.resizeTabs){
37897     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37898   //         this.autoSizeTabs();
37899 //        }else{
37900 //            item.autoSize();
37901        // }
37902     },
37903
37904     /**
37905      * Removes a {@link Roo.TabPanelItem}.
37906      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37907      */
37908     removeTab : function(id){
37909         var items = this.items;
37910         var tab = items[id];
37911         if(!tab) { return; }
37912         var index = items.indexOf(tab);
37913         if(this.active == tab && items.length > 1){
37914             var newTab = this.getNextAvailable(index);
37915             if(newTab) {
37916                 newTab.activate();
37917             }
37918         }
37919         this.stripEl.dom.removeChild(tab.pnode.dom);
37920         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37921             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37922         }
37923         items.splice(index, 1);
37924         delete this.items[tab.id];
37925         tab.fireEvent("close", tab);
37926         tab.purgeListeners();
37927         this.autoSizeTabs();
37928     },
37929
37930     getNextAvailable : function(start){
37931         var items = this.items;
37932         var index = start;
37933         // look for a next tab that will slide over to
37934         // replace the one being removed
37935         while(index < items.length){
37936             var item = items[++index];
37937             if(item && !item.isHidden()){
37938                 return item;
37939             }
37940         }
37941         // if one isn't found select the previous tab (on the left)
37942         index = start;
37943         while(index >= 0){
37944             var item = items[--index];
37945             if(item && !item.isHidden()){
37946                 return item;
37947             }
37948         }
37949         return null;
37950     },
37951
37952     /**
37953      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37954      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37955      */
37956     disableTab : function(id){
37957         var tab = this.items[id];
37958         if(tab && this.active != tab){
37959             tab.disable();
37960         }
37961     },
37962
37963     /**
37964      * Enables a {@link Roo.TabPanelItem} that is disabled.
37965      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37966      */
37967     enableTab : function(id){
37968         var tab = this.items[id];
37969         tab.enable();
37970     },
37971
37972     /**
37973      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37974      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37975      * @return {Roo.TabPanelItem} The TabPanelItem.
37976      */
37977     activate : function(id){
37978         var tab = this.items[id];
37979         if(!tab){
37980             return null;
37981         }
37982         if(tab == this.active || tab.disabled){
37983             return tab;
37984         }
37985         var e = {};
37986         this.fireEvent("beforetabchange", this, e, tab);
37987         if(e.cancel !== true && !tab.disabled){
37988             if(this.active){
37989                 this.active.hide();
37990             }
37991             this.active = this.items[id];
37992             this.active.show();
37993             this.fireEvent("tabchange", this, this.active);
37994         }
37995         return tab;
37996     },
37997
37998     /**
37999      * Gets the active {@link Roo.TabPanelItem}.
38000      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38001      */
38002     getActiveTab : function(){
38003         return this.active;
38004     },
38005
38006     /**
38007      * Updates the tab body element to fit the height of the container element
38008      * for overflow scrolling
38009      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38010      */
38011     syncHeight : function(targetHeight){
38012         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38013         var bm = this.bodyEl.getMargins();
38014         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38015         this.bodyEl.setHeight(newHeight);
38016         return newHeight;
38017     },
38018
38019     onResize : function(){
38020         if(this.monitorResize){
38021             this.autoSizeTabs();
38022         }
38023     },
38024
38025     /**
38026      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38027      */
38028     beginUpdate : function(){
38029         this.updating = true;
38030     },
38031
38032     /**
38033      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38034      */
38035     endUpdate : function(){
38036         this.updating = false;
38037         this.autoSizeTabs();
38038     },
38039
38040     /**
38041      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38042      */
38043     autoSizeTabs : function(){
38044         var count = this.items.length;
38045         var vcount = count - this.hiddenCount;
38046         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38047             return;
38048         }
38049         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38050         var availWidth = Math.floor(w / vcount);
38051         var b = this.stripBody;
38052         if(b.getWidth() > w){
38053             var tabs = this.items;
38054             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38055             if(availWidth < this.minTabWidth){
38056                 /*if(!this.sleft){    // incomplete scrolling code
38057                     this.createScrollButtons();
38058                 }
38059                 this.showScroll();
38060                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38061             }
38062         }else{
38063             if(this.currentTabWidth < this.preferredTabWidth){
38064                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38065             }
38066         }
38067     },
38068
38069     /**
38070      * Returns the number of tabs in this TabPanel.
38071      * @return {Number}
38072      */
38073      getCount : function(){
38074          return this.items.length;
38075      },
38076
38077     /**
38078      * Resizes all the tabs to the passed width
38079      * @param {Number} The new width
38080      */
38081     setTabWidth : function(width){
38082         this.currentTabWidth = width;
38083         for(var i = 0, len = this.items.length; i < len; i++) {
38084                 if(!this.items[i].isHidden()) {
38085                 this.items[i].setWidth(width);
38086             }
38087         }
38088     },
38089
38090     /**
38091      * Destroys this TabPanel
38092      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38093      */
38094     destroy : function(removeEl){
38095         Roo.EventManager.removeResizeListener(this.onResize, this);
38096         for(var i = 0, len = this.items.length; i < len; i++){
38097             this.items[i].purgeListeners();
38098         }
38099         if(removeEl === true){
38100             this.el.update("");
38101             this.el.remove();
38102         }
38103     },
38104     
38105     createStrip : function(container)
38106     {
38107         var strip = document.createElement("nav");
38108         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38109         container.appendChild(strip);
38110         return strip;
38111     },
38112     
38113     createStripList : function(strip)
38114     {
38115         // div wrapper for retard IE
38116         // returns the "tr" element.
38117         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38118         //'<div class="x-tabs-strip-wrap">'+
38119           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38120           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38121         return strip.firstChild; //.firstChild.firstChild.firstChild;
38122     },
38123     createBody : function(container)
38124     {
38125         var body = document.createElement("div");
38126         Roo.id(body, "tab-body");
38127         //Roo.fly(body).addClass("x-tabs-body");
38128         Roo.fly(body).addClass("tab-content");
38129         container.appendChild(body);
38130         return body;
38131     },
38132     createItemBody :function(bodyEl, id){
38133         var body = Roo.getDom(id);
38134         if(!body){
38135             body = document.createElement("div");
38136             body.id = id;
38137         }
38138         //Roo.fly(body).addClass("x-tabs-item-body");
38139         Roo.fly(body).addClass("tab-pane");
38140          bodyEl.insertBefore(body, bodyEl.firstChild);
38141         return body;
38142     },
38143     /** @private */
38144     createStripElements :  function(stripEl, text, closable, tpl)
38145     {
38146         var td = document.createElement("li"); // was td..
38147         
38148         
38149         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38150         
38151         
38152         stripEl.appendChild(td);
38153         /*if(closable){
38154             td.className = "x-tabs-closable";
38155             if(!this.closeTpl){
38156                 this.closeTpl = new Roo.Template(
38157                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38158                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38159                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38160                 );
38161             }
38162             var el = this.closeTpl.overwrite(td, {"text": text});
38163             var close = el.getElementsByTagName("div")[0];
38164             var inner = el.getElementsByTagName("em")[0];
38165             return {"el": el, "close": close, "inner": inner};
38166         } else {
38167         */
38168         // not sure what this is..
38169 //            if(!this.tabTpl){
38170                 //this.tabTpl = new Roo.Template(
38171                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38172                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38173                 //);
38174 //                this.tabTpl = new Roo.Template(
38175 //                   '<a href="#">' +
38176 //                   '<span unselectable="on"' +
38177 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38178 //                            ' >{text}</span></a>'
38179 //                );
38180 //                
38181 //            }
38182
38183
38184             var template = tpl || this.tabTpl || false;
38185             
38186             if(!template){
38187                 
38188                 template = new Roo.Template(
38189                    '<a href="#">' +
38190                    '<span unselectable="on"' +
38191                             (this.disableTooltips ? '' : ' title="{text}"') +
38192                             ' >{text}</span></a>'
38193                 );
38194             }
38195             
38196             switch (typeof(template)) {
38197                 case 'object' :
38198                     break;
38199                 case 'string' :
38200                     template = new Roo.Template(template);
38201                     break;
38202                 default :
38203                     break;
38204             }
38205             
38206             var el = template.overwrite(td, {"text": text});
38207             
38208             var inner = el.getElementsByTagName("span")[0];
38209             
38210             return {"el": el, "inner": inner};
38211             
38212     }
38213         
38214     
38215 });
38216
38217 /**
38218  * @class Roo.TabPanelItem
38219  * @extends Roo.util.Observable
38220  * Represents an individual item (tab plus body) in a TabPanel.
38221  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38222  * @param {String} id The id of this TabPanelItem
38223  * @param {String} text The text for the tab of this TabPanelItem
38224  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38225  */
38226 Roo.bootstrap.panel.TabItem = function(config){
38227     /**
38228      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38229      * @type Roo.TabPanel
38230      */
38231     this.tabPanel = config.panel;
38232     /**
38233      * The id for this TabPanelItem
38234      * @type String
38235      */
38236     this.id = config.id;
38237     /** @private */
38238     this.disabled = false;
38239     /** @private */
38240     this.text = config.text;
38241     /** @private */
38242     this.loaded = false;
38243     this.closable = config.closable;
38244
38245     /**
38246      * The body element for this TabPanelItem.
38247      * @type Roo.Element
38248      */
38249     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38250     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38251     this.bodyEl.setStyle("display", "block");
38252     this.bodyEl.setStyle("zoom", "1");
38253     //this.hideAction();
38254
38255     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38256     /** @private */
38257     this.el = Roo.get(els.el);
38258     this.inner = Roo.get(els.inner, true);
38259     this.textEl = Roo.get(this.el.dom.firstChild, true);
38260     this.pnode = Roo.get(els.el.parentNode, true);
38261 //    this.el.on("mousedown", this.onTabMouseDown, this);
38262     this.el.on("click", this.onTabClick, this);
38263     /** @private */
38264     if(config.closable){
38265         var c = Roo.get(els.close, true);
38266         c.dom.title = this.closeText;
38267         c.addClassOnOver("close-over");
38268         c.on("click", this.closeClick, this);
38269      }
38270
38271     this.addEvents({
38272          /**
38273          * @event activate
38274          * Fires when this tab becomes the active tab.
38275          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38276          * @param {Roo.TabPanelItem} this
38277          */
38278         "activate": true,
38279         /**
38280          * @event beforeclose
38281          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38282          * @param {Roo.TabPanelItem} this
38283          * @param {Object} e Set cancel to true on this object to cancel the close.
38284          */
38285         "beforeclose": true,
38286         /**
38287          * @event close
38288          * Fires when this tab is closed.
38289          * @param {Roo.TabPanelItem} this
38290          */
38291          "close": true,
38292         /**
38293          * @event deactivate
38294          * Fires when this tab is no longer the active tab.
38295          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38296          * @param {Roo.TabPanelItem} this
38297          */
38298          "deactivate" : true
38299     });
38300     this.hidden = false;
38301
38302     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38303 };
38304
38305 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38306            {
38307     purgeListeners : function(){
38308        Roo.util.Observable.prototype.purgeListeners.call(this);
38309        this.el.removeAllListeners();
38310     },
38311     /**
38312      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38313      */
38314     show : function(){
38315         this.pnode.addClass("active");
38316         this.showAction();
38317         if(Roo.isOpera){
38318             this.tabPanel.stripWrap.repaint();
38319         }
38320         this.fireEvent("activate", this.tabPanel, this);
38321     },
38322
38323     /**
38324      * Returns true if this tab is the active tab.
38325      * @return {Boolean}
38326      */
38327     isActive : function(){
38328         return this.tabPanel.getActiveTab() == this;
38329     },
38330
38331     /**
38332      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38333      */
38334     hide : function(){
38335         this.pnode.removeClass("active");
38336         this.hideAction();
38337         this.fireEvent("deactivate", this.tabPanel, this);
38338     },
38339
38340     hideAction : function(){
38341         this.bodyEl.hide();
38342         this.bodyEl.setStyle("position", "absolute");
38343         this.bodyEl.setLeft("-20000px");
38344         this.bodyEl.setTop("-20000px");
38345     },
38346
38347     showAction : function(){
38348         this.bodyEl.setStyle("position", "relative");
38349         this.bodyEl.setTop("");
38350         this.bodyEl.setLeft("");
38351         this.bodyEl.show();
38352     },
38353
38354     /**
38355      * Set the tooltip for the tab.
38356      * @param {String} tooltip The tab's tooltip
38357      */
38358     setTooltip : function(text){
38359         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38360             this.textEl.dom.qtip = text;
38361             this.textEl.dom.removeAttribute('title');
38362         }else{
38363             this.textEl.dom.title = text;
38364         }
38365     },
38366
38367     onTabClick : function(e){
38368         e.preventDefault();
38369         this.tabPanel.activate(this.id);
38370     },
38371
38372     onTabMouseDown : function(e){
38373         e.preventDefault();
38374         this.tabPanel.activate(this.id);
38375     },
38376 /*
38377     getWidth : function(){
38378         return this.inner.getWidth();
38379     },
38380
38381     setWidth : function(width){
38382         var iwidth = width - this.pnode.getPadding("lr");
38383         this.inner.setWidth(iwidth);
38384         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38385         this.pnode.setWidth(width);
38386     },
38387 */
38388     /**
38389      * Show or hide the tab
38390      * @param {Boolean} hidden True to hide or false to show.
38391      */
38392     setHidden : function(hidden){
38393         this.hidden = hidden;
38394         this.pnode.setStyle("display", hidden ? "none" : "");
38395     },
38396
38397     /**
38398      * Returns true if this tab is "hidden"
38399      * @return {Boolean}
38400      */
38401     isHidden : function(){
38402         return this.hidden;
38403     },
38404
38405     /**
38406      * Returns the text for this tab
38407      * @return {String}
38408      */
38409     getText : function(){
38410         return this.text;
38411     },
38412     /*
38413     autoSize : function(){
38414         //this.el.beginMeasure();
38415         this.textEl.setWidth(1);
38416         /*
38417          *  #2804 [new] Tabs in Roojs
38418          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38419          */
38420         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38421         //this.el.endMeasure();
38422     //},
38423
38424     /**
38425      * Sets the text for the tab (Note: this also sets the tooltip text)
38426      * @param {String} text The tab's text and tooltip
38427      */
38428     setText : function(text){
38429         this.text = text;
38430         this.textEl.update(text);
38431         this.setTooltip(text);
38432         //if(!this.tabPanel.resizeTabs){
38433         //    this.autoSize();
38434         //}
38435     },
38436     /**
38437      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38438      */
38439     activate : function(){
38440         this.tabPanel.activate(this.id);
38441     },
38442
38443     /**
38444      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38445      */
38446     disable : function(){
38447         if(this.tabPanel.active != this){
38448             this.disabled = true;
38449             this.pnode.addClass("disabled");
38450         }
38451     },
38452
38453     /**
38454      * Enables this TabPanelItem if it was previously disabled.
38455      */
38456     enable : function(){
38457         this.disabled = false;
38458         this.pnode.removeClass("disabled");
38459     },
38460
38461     /**
38462      * Sets the content for this TabPanelItem.
38463      * @param {String} content The content
38464      * @param {Boolean} loadScripts true to look for and load scripts
38465      */
38466     setContent : function(content, loadScripts){
38467         this.bodyEl.update(content, loadScripts);
38468     },
38469
38470     /**
38471      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38472      * @return {Roo.UpdateManager} The UpdateManager
38473      */
38474     getUpdateManager : function(){
38475         return this.bodyEl.getUpdateManager();
38476     },
38477
38478     /**
38479      * Set a URL to be used to load the content for this TabPanelItem.
38480      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38481      * @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)
38482      * @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)
38483      * @return {Roo.UpdateManager} The UpdateManager
38484      */
38485     setUrl : function(url, params, loadOnce){
38486         if(this.refreshDelegate){
38487             this.un('activate', this.refreshDelegate);
38488         }
38489         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38490         this.on("activate", this.refreshDelegate);
38491         return this.bodyEl.getUpdateManager();
38492     },
38493
38494     /** @private */
38495     _handleRefresh : function(url, params, loadOnce){
38496         if(!loadOnce || !this.loaded){
38497             var updater = this.bodyEl.getUpdateManager();
38498             updater.update(url, params, this._setLoaded.createDelegate(this));
38499         }
38500     },
38501
38502     /**
38503      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38504      *   Will fail silently if the setUrl method has not been called.
38505      *   This does not activate the panel, just updates its content.
38506      */
38507     refresh : function(){
38508         if(this.refreshDelegate){
38509            this.loaded = false;
38510            this.refreshDelegate();
38511         }
38512     },
38513
38514     /** @private */
38515     _setLoaded : function(){
38516         this.loaded = true;
38517     },
38518
38519     /** @private */
38520     closeClick : function(e){
38521         var o = {};
38522         e.stopEvent();
38523         this.fireEvent("beforeclose", this, o);
38524         if(o.cancel !== true){
38525             this.tabPanel.removeTab(this.id);
38526         }
38527     },
38528     /**
38529      * The text displayed in the tooltip for the close icon.
38530      * @type String
38531      */
38532     closeText : "Close this tab"
38533 });
38534 /**
38535 *    This script refer to:
38536 *    Title: International Telephone Input
38537 *    Author: Jack O'Connor
38538 *    Code version:  v12.1.12
38539 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38540 **/
38541
38542 Roo.bootstrap.PhoneInputData = function() {
38543     var d = [
38544       [
38545         "Afghanistan (‫افغانستان‬‎)",
38546         "af",
38547         "93"
38548       ],
38549       [
38550         "Albania (Shqipëri)",
38551         "al",
38552         "355"
38553       ],
38554       [
38555         "Algeria (‫الجزائر‬‎)",
38556         "dz",
38557         "213"
38558       ],
38559       [
38560         "American Samoa",
38561         "as",
38562         "1684"
38563       ],
38564       [
38565         "Andorra",
38566         "ad",
38567         "376"
38568       ],
38569       [
38570         "Angola",
38571         "ao",
38572         "244"
38573       ],
38574       [
38575         "Anguilla",
38576         "ai",
38577         "1264"
38578       ],
38579       [
38580         "Antigua and Barbuda",
38581         "ag",
38582         "1268"
38583       ],
38584       [
38585         "Argentina",
38586         "ar",
38587         "54"
38588       ],
38589       [
38590         "Armenia (Հայաստան)",
38591         "am",
38592         "374"
38593       ],
38594       [
38595         "Aruba",
38596         "aw",
38597         "297"
38598       ],
38599       [
38600         "Australia",
38601         "au",
38602         "61",
38603         0
38604       ],
38605       [
38606         "Austria (Österreich)",
38607         "at",
38608         "43"
38609       ],
38610       [
38611         "Azerbaijan (Azərbaycan)",
38612         "az",
38613         "994"
38614       ],
38615       [
38616         "Bahamas",
38617         "bs",
38618         "1242"
38619       ],
38620       [
38621         "Bahrain (‫البحرين‬‎)",
38622         "bh",
38623         "973"
38624       ],
38625       [
38626         "Bangladesh (বাংলাদেশ)",
38627         "bd",
38628         "880"
38629       ],
38630       [
38631         "Barbados",
38632         "bb",
38633         "1246"
38634       ],
38635       [
38636         "Belarus (Беларусь)",
38637         "by",
38638         "375"
38639       ],
38640       [
38641         "Belgium (België)",
38642         "be",
38643         "32"
38644       ],
38645       [
38646         "Belize",
38647         "bz",
38648         "501"
38649       ],
38650       [
38651         "Benin (Bénin)",
38652         "bj",
38653         "229"
38654       ],
38655       [
38656         "Bermuda",
38657         "bm",
38658         "1441"
38659       ],
38660       [
38661         "Bhutan (འབྲུག)",
38662         "bt",
38663         "975"
38664       ],
38665       [
38666         "Bolivia",
38667         "bo",
38668         "591"
38669       ],
38670       [
38671         "Bosnia and Herzegovina (Босна и Херцеговина)",
38672         "ba",
38673         "387"
38674       ],
38675       [
38676         "Botswana",
38677         "bw",
38678         "267"
38679       ],
38680       [
38681         "Brazil (Brasil)",
38682         "br",
38683         "55"
38684       ],
38685       [
38686         "British Indian Ocean Territory",
38687         "io",
38688         "246"
38689       ],
38690       [
38691         "British Virgin Islands",
38692         "vg",
38693         "1284"
38694       ],
38695       [
38696         "Brunei",
38697         "bn",
38698         "673"
38699       ],
38700       [
38701         "Bulgaria (България)",
38702         "bg",
38703         "359"
38704       ],
38705       [
38706         "Burkina Faso",
38707         "bf",
38708         "226"
38709       ],
38710       [
38711         "Burundi (Uburundi)",
38712         "bi",
38713         "257"
38714       ],
38715       [
38716         "Cambodia (កម្ពុជា)",
38717         "kh",
38718         "855"
38719       ],
38720       [
38721         "Cameroon (Cameroun)",
38722         "cm",
38723         "237"
38724       ],
38725       [
38726         "Canada",
38727         "ca",
38728         "1",
38729         1,
38730         ["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"]
38731       ],
38732       [
38733         "Cape Verde (Kabu Verdi)",
38734         "cv",
38735         "238"
38736       ],
38737       [
38738         "Caribbean Netherlands",
38739         "bq",
38740         "599",
38741         1
38742       ],
38743       [
38744         "Cayman Islands",
38745         "ky",
38746         "1345"
38747       ],
38748       [
38749         "Central African Republic (République centrafricaine)",
38750         "cf",
38751         "236"
38752       ],
38753       [
38754         "Chad (Tchad)",
38755         "td",
38756         "235"
38757       ],
38758       [
38759         "Chile",
38760         "cl",
38761         "56"
38762       ],
38763       [
38764         "China (中国)",
38765         "cn",
38766         "86"
38767       ],
38768       [
38769         "Christmas Island",
38770         "cx",
38771         "61",
38772         2
38773       ],
38774       [
38775         "Cocos (Keeling) Islands",
38776         "cc",
38777         "61",
38778         1
38779       ],
38780       [
38781         "Colombia",
38782         "co",
38783         "57"
38784       ],
38785       [
38786         "Comoros (‫جزر القمر‬‎)",
38787         "km",
38788         "269"
38789       ],
38790       [
38791         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38792         "cd",
38793         "243"
38794       ],
38795       [
38796         "Congo (Republic) (Congo-Brazzaville)",
38797         "cg",
38798         "242"
38799       ],
38800       [
38801         "Cook Islands",
38802         "ck",
38803         "682"
38804       ],
38805       [
38806         "Costa Rica",
38807         "cr",
38808         "506"
38809       ],
38810       [
38811         "Côte d’Ivoire",
38812         "ci",
38813         "225"
38814       ],
38815       [
38816         "Croatia (Hrvatska)",
38817         "hr",
38818         "385"
38819       ],
38820       [
38821         "Cuba",
38822         "cu",
38823         "53"
38824       ],
38825       [
38826         "Curaçao",
38827         "cw",
38828         "599",
38829         0
38830       ],
38831       [
38832         "Cyprus (Κύπρος)",
38833         "cy",
38834         "357"
38835       ],
38836       [
38837         "Czech Republic (Česká republika)",
38838         "cz",
38839         "420"
38840       ],
38841       [
38842         "Denmark (Danmark)",
38843         "dk",
38844         "45"
38845       ],
38846       [
38847         "Djibouti",
38848         "dj",
38849         "253"
38850       ],
38851       [
38852         "Dominica",
38853         "dm",
38854         "1767"
38855       ],
38856       [
38857         "Dominican Republic (República Dominicana)",
38858         "do",
38859         "1",
38860         2,
38861         ["809", "829", "849"]
38862       ],
38863       [
38864         "Ecuador",
38865         "ec",
38866         "593"
38867       ],
38868       [
38869         "Egypt (‫مصر‬‎)",
38870         "eg",
38871         "20"
38872       ],
38873       [
38874         "El Salvador",
38875         "sv",
38876         "503"
38877       ],
38878       [
38879         "Equatorial Guinea (Guinea Ecuatorial)",
38880         "gq",
38881         "240"
38882       ],
38883       [
38884         "Eritrea",
38885         "er",
38886         "291"
38887       ],
38888       [
38889         "Estonia (Eesti)",
38890         "ee",
38891         "372"
38892       ],
38893       [
38894         "Ethiopia",
38895         "et",
38896         "251"
38897       ],
38898       [
38899         "Falkland Islands (Islas Malvinas)",
38900         "fk",
38901         "500"
38902       ],
38903       [
38904         "Faroe Islands (Føroyar)",
38905         "fo",
38906         "298"
38907       ],
38908       [
38909         "Fiji",
38910         "fj",
38911         "679"
38912       ],
38913       [
38914         "Finland (Suomi)",
38915         "fi",
38916         "358",
38917         0
38918       ],
38919       [
38920         "France",
38921         "fr",
38922         "33"
38923       ],
38924       [
38925         "French Guiana (Guyane française)",
38926         "gf",
38927         "594"
38928       ],
38929       [
38930         "French Polynesia (Polynésie française)",
38931         "pf",
38932         "689"
38933       ],
38934       [
38935         "Gabon",
38936         "ga",
38937         "241"
38938       ],
38939       [
38940         "Gambia",
38941         "gm",
38942         "220"
38943       ],
38944       [
38945         "Georgia (საქართველო)",
38946         "ge",
38947         "995"
38948       ],
38949       [
38950         "Germany (Deutschland)",
38951         "de",
38952         "49"
38953       ],
38954       [
38955         "Ghana (Gaana)",
38956         "gh",
38957         "233"
38958       ],
38959       [
38960         "Gibraltar",
38961         "gi",
38962         "350"
38963       ],
38964       [
38965         "Greece (Ελλάδα)",
38966         "gr",
38967         "30"
38968       ],
38969       [
38970         "Greenland (Kalaallit Nunaat)",
38971         "gl",
38972         "299"
38973       ],
38974       [
38975         "Grenada",
38976         "gd",
38977         "1473"
38978       ],
38979       [
38980         "Guadeloupe",
38981         "gp",
38982         "590",
38983         0
38984       ],
38985       [
38986         "Guam",
38987         "gu",
38988         "1671"
38989       ],
38990       [
38991         "Guatemala",
38992         "gt",
38993         "502"
38994       ],
38995       [
38996         "Guernsey",
38997         "gg",
38998         "44",
38999         1
39000       ],
39001       [
39002         "Guinea (Guinée)",
39003         "gn",
39004         "224"
39005       ],
39006       [
39007         "Guinea-Bissau (Guiné Bissau)",
39008         "gw",
39009         "245"
39010       ],
39011       [
39012         "Guyana",
39013         "gy",
39014         "592"
39015       ],
39016       [
39017         "Haiti",
39018         "ht",
39019         "509"
39020       ],
39021       [
39022         "Honduras",
39023         "hn",
39024         "504"
39025       ],
39026       [
39027         "Hong Kong (香港)",
39028         "hk",
39029         "852"
39030       ],
39031       [
39032         "Hungary (Magyarország)",
39033         "hu",
39034         "36"
39035       ],
39036       [
39037         "Iceland (Ísland)",
39038         "is",
39039         "354"
39040       ],
39041       [
39042         "India (भारत)",
39043         "in",
39044         "91"
39045       ],
39046       [
39047         "Indonesia",
39048         "id",
39049         "62"
39050       ],
39051       [
39052         "Iran (‫ایران‬‎)",
39053         "ir",
39054         "98"
39055       ],
39056       [
39057         "Iraq (‫العراق‬‎)",
39058         "iq",
39059         "964"
39060       ],
39061       [
39062         "Ireland",
39063         "ie",
39064         "353"
39065       ],
39066       [
39067         "Isle of Man",
39068         "im",
39069         "44",
39070         2
39071       ],
39072       [
39073         "Israel (‫ישראל‬‎)",
39074         "il",
39075         "972"
39076       ],
39077       [
39078         "Italy (Italia)",
39079         "it",
39080         "39",
39081         0
39082       ],
39083       [
39084         "Jamaica",
39085         "jm",
39086         "1876"
39087       ],
39088       [
39089         "Japan (日本)",
39090         "jp",
39091         "81"
39092       ],
39093       [
39094         "Jersey",
39095         "je",
39096         "44",
39097         3
39098       ],
39099       [
39100         "Jordan (‫الأردن‬‎)",
39101         "jo",
39102         "962"
39103       ],
39104       [
39105         "Kazakhstan (Казахстан)",
39106         "kz",
39107         "7",
39108         1
39109       ],
39110       [
39111         "Kenya",
39112         "ke",
39113         "254"
39114       ],
39115       [
39116         "Kiribati",
39117         "ki",
39118         "686"
39119       ],
39120       [
39121         "Kosovo",
39122         "xk",
39123         "383"
39124       ],
39125       [
39126         "Kuwait (‫الكويت‬‎)",
39127         "kw",
39128         "965"
39129       ],
39130       [
39131         "Kyrgyzstan (Кыргызстан)",
39132         "kg",
39133         "996"
39134       ],
39135       [
39136         "Laos (ລາວ)",
39137         "la",
39138         "856"
39139       ],
39140       [
39141         "Latvia (Latvija)",
39142         "lv",
39143         "371"
39144       ],
39145       [
39146         "Lebanon (‫لبنان‬‎)",
39147         "lb",
39148         "961"
39149       ],
39150       [
39151         "Lesotho",
39152         "ls",
39153         "266"
39154       ],
39155       [
39156         "Liberia",
39157         "lr",
39158         "231"
39159       ],
39160       [
39161         "Libya (‫ليبيا‬‎)",
39162         "ly",
39163         "218"
39164       ],
39165       [
39166         "Liechtenstein",
39167         "li",
39168         "423"
39169       ],
39170       [
39171         "Lithuania (Lietuva)",
39172         "lt",
39173         "370"
39174       ],
39175       [
39176         "Luxembourg",
39177         "lu",
39178         "352"
39179       ],
39180       [
39181         "Macau (澳門)",
39182         "mo",
39183         "853"
39184       ],
39185       [
39186         "Macedonia (FYROM) (Македонија)",
39187         "mk",
39188         "389"
39189       ],
39190       [
39191         "Madagascar (Madagasikara)",
39192         "mg",
39193         "261"
39194       ],
39195       [
39196         "Malawi",
39197         "mw",
39198         "265"
39199       ],
39200       [
39201         "Malaysia",
39202         "my",
39203         "60"
39204       ],
39205       [
39206         "Maldives",
39207         "mv",
39208         "960"
39209       ],
39210       [
39211         "Mali",
39212         "ml",
39213         "223"
39214       ],
39215       [
39216         "Malta",
39217         "mt",
39218         "356"
39219       ],
39220       [
39221         "Marshall Islands",
39222         "mh",
39223         "692"
39224       ],
39225       [
39226         "Martinique",
39227         "mq",
39228         "596"
39229       ],
39230       [
39231         "Mauritania (‫موريتانيا‬‎)",
39232         "mr",
39233         "222"
39234       ],
39235       [
39236         "Mauritius (Moris)",
39237         "mu",
39238         "230"
39239       ],
39240       [
39241         "Mayotte",
39242         "yt",
39243         "262",
39244         1
39245       ],
39246       [
39247         "Mexico (México)",
39248         "mx",
39249         "52"
39250       ],
39251       [
39252         "Micronesia",
39253         "fm",
39254         "691"
39255       ],
39256       [
39257         "Moldova (Republica Moldova)",
39258         "md",
39259         "373"
39260       ],
39261       [
39262         "Monaco",
39263         "mc",
39264         "377"
39265       ],
39266       [
39267         "Mongolia (Монгол)",
39268         "mn",
39269         "976"
39270       ],
39271       [
39272         "Montenegro (Crna Gora)",
39273         "me",
39274         "382"
39275       ],
39276       [
39277         "Montserrat",
39278         "ms",
39279         "1664"
39280       ],
39281       [
39282         "Morocco (‫المغرب‬‎)",
39283         "ma",
39284         "212",
39285         0
39286       ],
39287       [
39288         "Mozambique (Moçambique)",
39289         "mz",
39290         "258"
39291       ],
39292       [
39293         "Myanmar (Burma) (မြန်မာ)",
39294         "mm",
39295         "95"
39296       ],
39297       [
39298         "Namibia (Namibië)",
39299         "na",
39300         "264"
39301       ],
39302       [
39303         "Nauru",
39304         "nr",
39305         "674"
39306       ],
39307       [
39308         "Nepal (नेपाल)",
39309         "np",
39310         "977"
39311       ],
39312       [
39313         "Netherlands (Nederland)",
39314         "nl",
39315         "31"
39316       ],
39317       [
39318         "New Caledonia (Nouvelle-Calédonie)",
39319         "nc",
39320         "687"
39321       ],
39322       [
39323         "New Zealand",
39324         "nz",
39325         "64"
39326       ],
39327       [
39328         "Nicaragua",
39329         "ni",
39330         "505"
39331       ],
39332       [
39333         "Niger (Nijar)",
39334         "ne",
39335         "227"
39336       ],
39337       [
39338         "Nigeria",
39339         "ng",
39340         "234"
39341       ],
39342       [
39343         "Niue",
39344         "nu",
39345         "683"
39346       ],
39347       [
39348         "Norfolk Island",
39349         "nf",
39350         "672"
39351       ],
39352       [
39353         "North Korea (조선 민주주의 인민 공화국)",
39354         "kp",
39355         "850"
39356       ],
39357       [
39358         "Northern Mariana Islands",
39359         "mp",
39360         "1670"
39361       ],
39362       [
39363         "Norway (Norge)",
39364         "no",
39365         "47",
39366         0
39367       ],
39368       [
39369         "Oman (‫عُمان‬‎)",
39370         "om",
39371         "968"
39372       ],
39373       [
39374         "Pakistan (‫پاکستان‬‎)",
39375         "pk",
39376         "92"
39377       ],
39378       [
39379         "Palau",
39380         "pw",
39381         "680"
39382       ],
39383       [
39384         "Palestine (‫فلسطين‬‎)",
39385         "ps",
39386         "970"
39387       ],
39388       [
39389         "Panama (Panamá)",
39390         "pa",
39391         "507"
39392       ],
39393       [
39394         "Papua New Guinea",
39395         "pg",
39396         "675"
39397       ],
39398       [
39399         "Paraguay",
39400         "py",
39401         "595"
39402       ],
39403       [
39404         "Peru (Perú)",
39405         "pe",
39406         "51"
39407       ],
39408       [
39409         "Philippines",
39410         "ph",
39411         "63"
39412       ],
39413       [
39414         "Poland (Polska)",
39415         "pl",
39416         "48"
39417       ],
39418       [
39419         "Portugal",
39420         "pt",
39421         "351"
39422       ],
39423       [
39424         "Puerto Rico",
39425         "pr",
39426         "1",
39427         3,
39428         ["787", "939"]
39429       ],
39430       [
39431         "Qatar (‫قطر‬‎)",
39432         "qa",
39433         "974"
39434       ],
39435       [
39436         "Réunion (La Réunion)",
39437         "re",
39438         "262",
39439         0
39440       ],
39441       [
39442         "Romania (România)",
39443         "ro",
39444         "40"
39445       ],
39446       [
39447         "Russia (Россия)",
39448         "ru",
39449         "7",
39450         0
39451       ],
39452       [
39453         "Rwanda",
39454         "rw",
39455         "250"
39456       ],
39457       [
39458         "Saint Barthélemy",
39459         "bl",
39460         "590",
39461         1
39462       ],
39463       [
39464         "Saint Helena",
39465         "sh",
39466         "290"
39467       ],
39468       [
39469         "Saint Kitts and Nevis",
39470         "kn",
39471         "1869"
39472       ],
39473       [
39474         "Saint Lucia",
39475         "lc",
39476         "1758"
39477       ],
39478       [
39479         "Saint Martin (Saint-Martin (partie française))",
39480         "mf",
39481         "590",
39482         2
39483       ],
39484       [
39485         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39486         "pm",
39487         "508"
39488       ],
39489       [
39490         "Saint Vincent and the Grenadines",
39491         "vc",
39492         "1784"
39493       ],
39494       [
39495         "Samoa",
39496         "ws",
39497         "685"
39498       ],
39499       [
39500         "San Marino",
39501         "sm",
39502         "378"
39503       ],
39504       [
39505         "São Tomé and Príncipe (São Tomé e Príncipe)",
39506         "st",
39507         "239"
39508       ],
39509       [
39510         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39511         "sa",
39512         "966"
39513       ],
39514       [
39515         "Senegal (Sénégal)",
39516         "sn",
39517         "221"
39518       ],
39519       [
39520         "Serbia (Србија)",
39521         "rs",
39522         "381"
39523       ],
39524       [
39525         "Seychelles",
39526         "sc",
39527         "248"
39528       ],
39529       [
39530         "Sierra Leone",
39531         "sl",
39532         "232"
39533       ],
39534       [
39535         "Singapore",
39536         "sg",
39537         "65"
39538       ],
39539       [
39540         "Sint Maarten",
39541         "sx",
39542         "1721"
39543       ],
39544       [
39545         "Slovakia (Slovensko)",
39546         "sk",
39547         "421"
39548       ],
39549       [
39550         "Slovenia (Slovenija)",
39551         "si",
39552         "386"
39553       ],
39554       [
39555         "Solomon Islands",
39556         "sb",
39557         "677"
39558       ],
39559       [
39560         "Somalia (Soomaaliya)",
39561         "so",
39562         "252"
39563       ],
39564       [
39565         "South Africa",
39566         "za",
39567         "27"
39568       ],
39569       [
39570         "South Korea (대한민국)",
39571         "kr",
39572         "82"
39573       ],
39574       [
39575         "South Sudan (‫جنوب السودان‬‎)",
39576         "ss",
39577         "211"
39578       ],
39579       [
39580         "Spain (España)",
39581         "es",
39582         "34"
39583       ],
39584       [
39585         "Sri Lanka (ශ්‍රී ලංකාව)",
39586         "lk",
39587         "94"
39588       ],
39589       [
39590         "Sudan (‫السودان‬‎)",
39591         "sd",
39592         "249"
39593       ],
39594       [
39595         "Suriname",
39596         "sr",
39597         "597"
39598       ],
39599       [
39600         "Svalbard and Jan Mayen",
39601         "sj",
39602         "47",
39603         1
39604       ],
39605       [
39606         "Swaziland",
39607         "sz",
39608         "268"
39609       ],
39610       [
39611         "Sweden (Sverige)",
39612         "se",
39613         "46"
39614       ],
39615       [
39616         "Switzerland (Schweiz)",
39617         "ch",
39618         "41"
39619       ],
39620       [
39621         "Syria (‫سوريا‬‎)",
39622         "sy",
39623         "963"
39624       ],
39625       [
39626         "Taiwan (台灣)",
39627         "tw",
39628         "886"
39629       ],
39630       [
39631         "Tajikistan",
39632         "tj",
39633         "992"
39634       ],
39635       [
39636         "Tanzania",
39637         "tz",
39638         "255"
39639       ],
39640       [
39641         "Thailand (ไทย)",
39642         "th",
39643         "66"
39644       ],
39645       [
39646         "Timor-Leste",
39647         "tl",
39648         "670"
39649       ],
39650       [
39651         "Togo",
39652         "tg",
39653         "228"
39654       ],
39655       [
39656         "Tokelau",
39657         "tk",
39658         "690"
39659       ],
39660       [
39661         "Tonga",
39662         "to",
39663         "676"
39664       ],
39665       [
39666         "Trinidad and Tobago",
39667         "tt",
39668         "1868"
39669       ],
39670       [
39671         "Tunisia (‫تونس‬‎)",
39672         "tn",
39673         "216"
39674       ],
39675       [
39676         "Turkey (Türkiye)",
39677         "tr",
39678         "90"
39679       ],
39680       [
39681         "Turkmenistan",
39682         "tm",
39683         "993"
39684       ],
39685       [
39686         "Turks and Caicos Islands",
39687         "tc",
39688         "1649"
39689       ],
39690       [
39691         "Tuvalu",
39692         "tv",
39693         "688"
39694       ],
39695       [
39696         "U.S. Virgin Islands",
39697         "vi",
39698         "1340"
39699       ],
39700       [
39701         "Uganda",
39702         "ug",
39703         "256"
39704       ],
39705       [
39706         "Ukraine (Україна)",
39707         "ua",
39708         "380"
39709       ],
39710       [
39711         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39712         "ae",
39713         "971"
39714       ],
39715       [
39716         "United Kingdom",
39717         "gb",
39718         "44",
39719         0
39720       ],
39721       [
39722         "United States",
39723         "us",
39724         "1",
39725         0
39726       ],
39727       [
39728         "Uruguay",
39729         "uy",
39730         "598"
39731       ],
39732       [
39733         "Uzbekistan (Oʻzbekiston)",
39734         "uz",
39735         "998"
39736       ],
39737       [
39738         "Vanuatu",
39739         "vu",
39740         "678"
39741       ],
39742       [
39743         "Vatican City (Città del Vaticano)",
39744         "va",
39745         "39",
39746         1
39747       ],
39748       [
39749         "Venezuela",
39750         "ve",
39751         "58"
39752       ],
39753       [
39754         "Vietnam (Việt Nam)",
39755         "vn",
39756         "84"
39757       ],
39758       [
39759         "Wallis and Futuna (Wallis-et-Futuna)",
39760         "wf",
39761         "681"
39762       ],
39763       [
39764         "Western Sahara (‫الصحراء الغربية‬‎)",
39765         "eh",
39766         "212",
39767         1
39768       ],
39769       [
39770         "Yemen (‫اليمن‬‎)",
39771         "ye",
39772         "967"
39773       ],
39774       [
39775         "Zambia",
39776         "zm",
39777         "260"
39778       ],
39779       [
39780         "Zimbabwe",
39781         "zw",
39782         "263"
39783       ],
39784       [
39785         "Åland Islands",
39786         "ax",
39787         "358",
39788         1
39789       ]
39790   ];
39791   
39792   return d;
39793 }/**
39794 *    This script refer to:
39795 *    Title: International Telephone Input
39796 *    Author: Jack O'Connor
39797 *    Code version:  v12.1.12
39798 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39799 **/
39800
39801 /**
39802  * @class Roo.bootstrap.PhoneInput
39803  * @extends Roo.bootstrap.TriggerField
39804  * An input with International dial-code selection
39805  
39806  * @cfg {String} defaultDialCode default '+852'
39807  * @cfg {Array} preferedCountries default []
39808   
39809  * @constructor
39810  * Create a new PhoneInput.
39811  * @param {Object} config Configuration options
39812  */
39813
39814 Roo.bootstrap.PhoneInput = function(config) {
39815     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39816 };
39817
39818 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39819         
39820         listWidth: undefined,
39821         
39822         selectedClass: 'active',
39823         
39824         invalidClass : "has-warning",
39825         
39826         validClass: 'has-success',
39827         
39828         allowed: '0123456789',
39829         
39830         /**
39831          * @cfg {String} defaultDialCode The default dial code when initializing the input
39832          */
39833         defaultDialCode: '+852',
39834         
39835         /**
39836          * @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
39837          */
39838         preferedCountries: false,
39839         
39840         getAutoCreate : function()
39841         {
39842             var data = Roo.bootstrap.PhoneInputData();
39843             var align = this.labelAlign || this.parentLabelAlign();
39844             var id = Roo.id();
39845             
39846             this.allCountries = [];
39847             this.dialCodeMapping = [];
39848             
39849             for (var i = 0; i < data.length; i++) {
39850               var c = data[i];
39851               this.allCountries[i] = {
39852                 name: c[0],
39853                 iso2: c[1],
39854                 dialCode: c[2],
39855                 priority: c[3] || 0,
39856                 areaCodes: c[4] || null
39857               };
39858               this.dialCodeMapping[c[2]] = {
39859                   name: c[0],
39860                   iso2: c[1],
39861                   priority: c[3] || 0,
39862                   areaCodes: c[4] || null
39863               };
39864             }
39865             
39866             var cfg = {
39867                 cls: 'form-group',
39868                 cn: []
39869             };
39870             
39871             var input =  {
39872                 tag: 'input',
39873                 id : id,
39874                 cls : 'form-control tel-input',
39875                 autocomplete: 'new-password'
39876             };
39877             
39878             var hiddenInput = {
39879                 tag: 'input',
39880                 type: 'hidden',
39881                 cls: 'hidden-tel-input'
39882             };
39883             
39884             if (this.name) {
39885                 hiddenInput.name = this.name;
39886             }
39887             
39888             if (this.disabled) {
39889                 input.disabled = true;
39890             }
39891             
39892             var flag_container = {
39893                 tag: 'div',
39894                 cls: 'flag-box',
39895                 cn: [
39896                     {
39897                         tag: 'div',
39898                         cls: 'flag'
39899                     },
39900                     {
39901                         tag: 'div',
39902                         cls: 'caret'
39903                     }
39904                 ]
39905             };
39906             
39907             var box = {
39908                 tag: 'div',
39909                 cls: this.hasFeedback ? 'has-feedback' : '',
39910                 cn: [
39911                     hiddenInput,
39912                     input,
39913                     {
39914                         tag: 'input',
39915                         cls: 'dial-code-holder',
39916                         disabled: true
39917                     }
39918                 ]
39919             };
39920             
39921             var container = {
39922                 cls: 'roo-select2-container input-group',
39923                 cn: [
39924                     flag_container,
39925                     box
39926                 ]
39927             };
39928             
39929             if (this.fieldLabel.length) {
39930                 var indicator = {
39931                     tag: 'i',
39932                     tooltip: 'This field is required'
39933                 };
39934                 
39935                 var label = {
39936                     tag: 'label',
39937                     'for':  id,
39938                     cls: 'control-label',
39939                     cn: []
39940                 };
39941                 
39942                 var label_text = {
39943                     tag: 'span',
39944                     html: this.fieldLabel
39945                 };
39946                 
39947                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39948                 label.cn = [
39949                     indicator,
39950                     label_text
39951                 ];
39952                 
39953                 if(this.indicatorpos == 'right') {
39954                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39955                     label.cn = [
39956                         label_text,
39957                         indicator
39958                     ];
39959                 }
39960                 
39961                 if(align == 'left') {
39962                     container = {
39963                         tag: 'div',
39964                         cn: [
39965                             container
39966                         ]
39967                     };
39968                     
39969                     if(this.labelWidth > 12){
39970                         label.style = "width: " + this.labelWidth + 'px';
39971                     }
39972                     if(this.labelWidth < 13 && this.labelmd == 0){
39973                         this.labelmd = this.labelWidth;
39974                     }
39975                     if(this.labellg > 0){
39976                         label.cls += ' col-lg-' + this.labellg;
39977                         input.cls += ' col-lg-' + (12 - this.labellg);
39978                     }
39979                     if(this.labelmd > 0){
39980                         label.cls += ' col-md-' + this.labelmd;
39981                         container.cls += ' col-md-' + (12 - this.labelmd);
39982                     }
39983                     if(this.labelsm > 0){
39984                         label.cls += ' col-sm-' + this.labelsm;
39985                         container.cls += ' col-sm-' + (12 - this.labelsm);
39986                     }
39987                     if(this.labelxs > 0){
39988                         label.cls += ' col-xs-' + this.labelxs;
39989                         container.cls += ' col-xs-' + (12 - this.labelxs);
39990                     }
39991                 }
39992             }
39993             
39994             cfg.cn = [
39995                 label,
39996                 container
39997             ];
39998             
39999             var settings = this;
40000             
40001             ['xs','sm','md','lg'].map(function(size){
40002                 if (settings[size]) {
40003                     cfg.cls += ' col-' + size + '-' + settings[size];
40004                 }
40005             });
40006             
40007             this.store = new Roo.data.Store({
40008                 proxy : new Roo.data.MemoryProxy({}),
40009                 reader : new Roo.data.JsonReader({
40010                     fields : [
40011                         {
40012                             'name' : 'name',
40013                             'type' : 'string'
40014                         },
40015                         {
40016                             'name' : 'iso2',
40017                             'type' : 'string'
40018                         },
40019                         {
40020                             'name' : 'dialCode',
40021                             'type' : 'string'
40022                         },
40023                         {
40024                             'name' : 'priority',
40025                             'type' : 'string'
40026                         },
40027                         {
40028                             'name' : 'areaCodes',
40029                             'type' : 'string'
40030                         }
40031                     ]
40032                 })
40033             });
40034             
40035             if(!this.preferedCountries) {
40036                 this.preferedCountries = [
40037                     'hk',
40038                     'gb',
40039                     'us'
40040                 ];
40041             }
40042             
40043             var p = this.preferedCountries.reverse();
40044             
40045             if(p) {
40046                 for (var i = 0; i < p.length; i++) {
40047                     for (var j = 0; j < this.allCountries.length; j++) {
40048                         if(this.allCountries[j].iso2 == p[i]) {
40049                             var t = this.allCountries[j];
40050                             this.allCountries.splice(j,1);
40051                             this.allCountries.unshift(t);
40052                         }
40053                     } 
40054                 }
40055             }
40056             
40057             this.store.proxy.data = {
40058                 success: true,
40059                 data: this.allCountries
40060             };
40061             
40062             return cfg;
40063         },
40064         
40065         initEvents : function()
40066         {
40067             this.createList();
40068             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40069             
40070             this.indicator = this.indicatorEl();
40071             this.flag = this.flagEl();
40072             this.dialCodeHolder = this.dialCodeHolderEl();
40073             
40074             this.trigger = this.el.select('div.flag-box',true).first();
40075             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40076             
40077             var _this = this;
40078             
40079             (function(){
40080                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40081                 _this.list.setWidth(lw);
40082             }).defer(100);
40083             
40084             this.list.on('mouseover', this.onViewOver, this);
40085             this.list.on('mousemove', this.onViewMove, this);
40086             this.inputEl().on("keyup", this.onKeyUp, this);
40087             
40088             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40089
40090             this.view = new Roo.View(this.list, this.tpl, {
40091                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40092             });
40093             
40094             this.view.on('click', this.onViewClick, this);
40095             this.setValue(this.defaultDialCode);
40096         },
40097         
40098         onTriggerClick : function(e)
40099         {
40100             Roo.log('trigger click');
40101             if(this.disabled){
40102                 return;
40103             }
40104             
40105             if(this.isExpanded()){
40106                 this.collapse();
40107                 this.hasFocus = false;
40108             }else {
40109                 this.store.load({});
40110                 this.hasFocus = true;
40111                 this.expand();
40112             }
40113         },
40114         
40115         isExpanded : function()
40116         {
40117             return this.list.isVisible();
40118         },
40119         
40120         collapse : function()
40121         {
40122             if(!this.isExpanded()){
40123                 return;
40124             }
40125             this.list.hide();
40126             Roo.get(document).un('mousedown', this.collapseIf, this);
40127             Roo.get(document).un('mousewheel', this.collapseIf, this);
40128             this.fireEvent('collapse', this);
40129             this.validate();
40130         },
40131         
40132         expand : function()
40133         {
40134             Roo.log('expand');
40135
40136             if(this.isExpanded() || !this.hasFocus){
40137                 return;
40138             }
40139             
40140             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40141             this.list.setWidth(lw);
40142             
40143             this.list.show();
40144             this.restrictHeight();
40145             
40146             Roo.get(document).on('mousedown', this.collapseIf, this);
40147             Roo.get(document).on('mousewheel', this.collapseIf, this);
40148             
40149             this.fireEvent('expand', this);
40150         },
40151         
40152         restrictHeight : function()
40153         {
40154             this.list.alignTo(this.inputEl(), this.listAlign);
40155             this.list.alignTo(this.inputEl(), this.listAlign);
40156         },
40157         
40158         onViewOver : function(e, t)
40159         {
40160             if(this.inKeyMode){
40161                 return;
40162             }
40163             var item = this.view.findItemFromChild(t);
40164             
40165             if(item){
40166                 var index = this.view.indexOf(item);
40167                 this.select(index, false);
40168             }
40169         },
40170
40171         // private
40172         onViewClick : function(view, doFocus, el, e)
40173         {
40174             var index = this.view.getSelectedIndexes()[0];
40175             
40176             var r = this.store.getAt(index);
40177             
40178             if(r){
40179                 this.onSelect(r, index);
40180             }
40181             if(doFocus !== false && !this.blockFocus){
40182                 this.inputEl().focus();
40183             }
40184         },
40185         
40186         onViewMove : function(e, t)
40187         {
40188             this.inKeyMode = false;
40189         },
40190         
40191         select : function(index, scrollIntoView)
40192         {
40193             this.selectedIndex = index;
40194             this.view.select(index);
40195             if(scrollIntoView !== false){
40196                 var el = this.view.getNode(index);
40197                 if(el){
40198                     this.list.scrollChildIntoView(el, false);
40199                 }
40200             }
40201         },
40202         
40203         createList : function()
40204         {
40205             this.list = Roo.get(document.body).createChild({
40206                 tag: 'ul',
40207                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40208                 style: 'display:none'
40209             });
40210             
40211             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40212         },
40213         
40214         collapseIf : function(e)
40215         {
40216             var in_combo  = e.within(this.el);
40217             var in_list =  e.within(this.list);
40218             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40219             
40220             if (in_combo || in_list || is_list) {
40221                 return;
40222             }
40223             this.collapse();
40224         },
40225         
40226         onSelect : function(record, index)
40227         {
40228             if(this.fireEvent('beforeselect', this, record, index) !== false){
40229                 
40230                 this.setFlagClass(record.data.iso2);
40231                 this.setDialCode(record.data.dialCode);
40232                 this.hasFocus = false;
40233                 this.collapse();
40234                 this.fireEvent('select', this, record, index);
40235             }
40236         },
40237         
40238         flagEl : function()
40239         {
40240             var flag = this.el.select('div.flag',true).first();
40241             if(!flag){
40242                 return false;
40243             }
40244             return flag;
40245         },
40246         
40247         dialCodeHolderEl : function()
40248         {
40249             var d = this.el.select('input.dial-code-holder',true).first();
40250             if(!d){
40251                 return false;
40252             }
40253             return d;
40254         },
40255         
40256         setDialCode : function(v)
40257         {
40258             this.dialCodeHolder.dom.value = '+'+v;
40259         },
40260         
40261         setFlagClass : function(n)
40262         {
40263             this.flag.dom.className = 'flag '+n;
40264         },
40265         
40266         getValue : function()
40267         {
40268             var v = this.inputEl().getValue();
40269             if(this.dialCodeHolder) {
40270                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40271             }
40272             return v;
40273         },
40274         
40275         setValue : function(v)
40276         {
40277             var d = this.getDialCode(v);
40278             
40279             //invalid dial code
40280             if(v.length == 0 || !d || d.length == 0) {
40281                 if(this.rendered){
40282                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40283                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40284                 }
40285                 return;
40286             }
40287             
40288             //valid dial code
40289             this.setFlagClass(this.dialCodeMapping[d].iso2);
40290             this.setDialCode(d);
40291             this.inputEl().dom.value = v.replace('+'+d,'');
40292             this.hiddenEl().dom.value = this.getValue();
40293             
40294             this.validate();
40295         },
40296         
40297         getDialCode : function(v)
40298         {
40299             v = v ||  '';
40300             
40301             if (v.length == 0) {
40302                 return this.dialCodeHolder.dom.value;
40303             }
40304             
40305             var dialCode = "";
40306             if (v.charAt(0) != "+") {
40307                 return false;
40308             }
40309             var numericChars = "";
40310             for (var i = 1; i < v.length; i++) {
40311               var c = v.charAt(i);
40312               if (!isNaN(c)) {
40313                 numericChars += c;
40314                 if (this.dialCodeMapping[numericChars]) {
40315                   dialCode = v.substr(1, i);
40316                 }
40317                 if (numericChars.length == 4) {
40318                   break;
40319                 }
40320               }
40321             }
40322             return dialCode;
40323         },
40324         
40325         reset : function()
40326         {
40327             this.setValue(this.defaultDialCode);
40328             this.validate();
40329         },
40330         
40331         hiddenEl : function()
40332         {
40333             return this.el.select('input.hidden-tel-input',true).first();
40334         },
40335         
40336         onKeyUp : function(e){
40337             
40338             var k = e.getKey();
40339             var c = e.getCharCode();
40340             
40341             if(
40342                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40343                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40344             ){
40345                 e.stopEvent();
40346             }
40347             
40348             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40349             //     return;
40350             // }
40351             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40352                 e.stopEvent();
40353             }
40354             
40355             this.setValue(this.getValue());
40356         }
40357         
40358 });
40359 /**
40360  * @class Roo.bootstrap.MoneyField
40361  * @extends Roo.bootstrap.ComboBox
40362  * Bootstrap MoneyField class
40363  * 
40364  * @constructor
40365  * Create a new MoneyField.
40366  * @param {Object} config Configuration options
40367  */
40368
40369 Roo.bootstrap.MoneyField = function(config) {
40370     
40371     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40372     
40373 };
40374
40375 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40376     
40377     /**
40378      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40379      */
40380     allowDecimals : true,
40381     /**
40382      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40383      */
40384     decimalSeparator : ".",
40385     /**
40386      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40387      */
40388     decimalPrecision : 0,
40389     /**
40390      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40391      */
40392     allowNegative : true,
40393     /**
40394      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40395      */
40396     allowZero: true,
40397     /**
40398      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40399      */
40400     minValue : Number.NEGATIVE_INFINITY,
40401     /**
40402      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40403      */
40404     maxValue : Number.MAX_VALUE,
40405     /**
40406      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40407      */
40408     minText : "The minimum value for this field is {0}",
40409     /**
40410      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40411      */
40412     maxText : "The maximum value for this field is {0}",
40413     /**
40414      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40415      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40416      */
40417     nanText : "{0} is not a valid number",
40418     /**
40419      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40420      */
40421     castInt : true,
40422     /**
40423      * @cfg {String} defaults currency of the MoneyField
40424      * value should be in lkey
40425      */
40426     defaultCurrency : false,
40427     /**
40428      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40429      */
40430     thousandsDelimiter : false,
40431     
40432     
40433     inputlg : 9,
40434     inputmd : 9,
40435     inputsm : 9,
40436     inputxs : 6,
40437     
40438     store : false,
40439     
40440     getAutoCreate : function()
40441     {
40442         var align = this.labelAlign || this.parentLabelAlign();
40443         
40444         var id = Roo.id();
40445
40446         var cfg = {
40447             cls: 'form-group',
40448             cn: []
40449         };
40450
40451         var input =  {
40452             tag: 'input',
40453             id : id,
40454             cls : 'form-control roo-money-amount-input',
40455             autocomplete: 'new-password'
40456         };
40457         
40458         var hiddenInput = {
40459             tag: 'input',
40460             type: 'hidden',
40461             id: Roo.id(),
40462             cls: 'hidden-number-input'
40463         };
40464         
40465         if (this.name) {
40466             hiddenInput.name = this.name;
40467         }
40468
40469         if (this.disabled) {
40470             input.disabled = true;
40471         }
40472
40473         var clg = 12 - this.inputlg;
40474         var cmd = 12 - this.inputmd;
40475         var csm = 12 - this.inputsm;
40476         var cxs = 12 - this.inputxs;
40477         
40478         var container = {
40479             tag : 'div',
40480             cls : 'row roo-money-field',
40481             cn : [
40482                 {
40483                     tag : 'div',
40484                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40485                     cn : [
40486                         {
40487                             tag : 'div',
40488                             cls: 'roo-select2-container input-group',
40489                             cn: [
40490                                 {
40491                                     tag : 'input',
40492                                     cls : 'form-control roo-money-currency-input',
40493                                     autocomplete: 'new-password',
40494                                     readOnly : 1,
40495                                     name : this.currencyName
40496                                 },
40497                                 {
40498                                     tag :'span',
40499                                     cls : 'input-group-addon',
40500                                     cn : [
40501                                         {
40502                                             tag: 'span',
40503                                             cls: 'caret'
40504                                         }
40505                                     ]
40506                                 }
40507                             ]
40508                         }
40509                     ]
40510                 },
40511                 {
40512                     tag : 'div',
40513                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40514                     cn : [
40515                         {
40516                             tag: 'div',
40517                             cls: this.hasFeedback ? 'has-feedback' : '',
40518                             cn: [
40519                                 input
40520                             ]
40521                         }
40522                     ]
40523                 }
40524             ]
40525             
40526         };
40527         
40528         if (this.fieldLabel.length) {
40529             var indicator = {
40530                 tag: 'i',
40531                 tooltip: 'This field is required'
40532             };
40533
40534             var label = {
40535                 tag: 'label',
40536                 'for':  id,
40537                 cls: 'control-label',
40538                 cn: []
40539             };
40540
40541             var label_text = {
40542                 tag: 'span',
40543                 html: this.fieldLabel
40544             };
40545
40546             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40547             label.cn = [
40548                 indicator,
40549                 label_text
40550             ];
40551
40552             if(this.indicatorpos == 'right') {
40553                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40554                 label.cn = [
40555                     label_text,
40556                     indicator
40557                 ];
40558             }
40559
40560             if(align == 'left') {
40561                 container = {
40562                     tag: 'div',
40563                     cn: [
40564                         container
40565                     ]
40566                 };
40567
40568                 if(this.labelWidth > 12){
40569                     label.style = "width: " + this.labelWidth + 'px';
40570                 }
40571                 if(this.labelWidth < 13 && this.labelmd == 0){
40572                     this.labelmd = this.labelWidth;
40573                 }
40574                 if(this.labellg > 0){
40575                     label.cls += ' col-lg-' + this.labellg;
40576                     input.cls += ' col-lg-' + (12 - this.labellg);
40577                 }
40578                 if(this.labelmd > 0){
40579                     label.cls += ' col-md-' + this.labelmd;
40580                     container.cls += ' col-md-' + (12 - this.labelmd);
40581                 }
40582                 if(this.labelsm > 0){
40583                     label.cls += ' col-sm-' + this.labelsm;
40584                     container.cls += ' col-sm-' + (12 - this.labelsm);
40585                 }
40586                 if(this.labelxs > 0){
40587                     label.cls += ' col-xs-' + this.labelxs;
40588                     container.cls += ' col-xs-' + (12 - this.labelxs);
40589                 }
40590             }
40591         }
40592
40593         cfg.cn = [
40594             label,
40595             container,
40596             hiddenInput
40597         ];
40598         
40599         var settings = this;
40600
40601         ['xs','sm','md','lg'].map(function(size){
40602             if (settings[size]) {
40603                 cfg.cls += ' col-' + size + '-' + settings[size];
40604             }
40605         });
40606         
40607         return cfg;
40608     },
40609     
40610     initEvents : function()
40611     {
40612         this.indicator = this.indicatorEl();
40613         
40614         this.initCurrencyEvent();
40615         
40616         this.initNumberEvent();
40617     },
40618     
40619     initCurrencyEvent : function()
40620     {
40621         if (!this.store) {
40622             throw "can not find store for combo";
40623         }
40624         
40625         this.store = Roo.factory(this.store, Roo.data);
40626         this.store.parent = this;
40627         
40628         this.createList();
40629         
40630         this.triggerEl = this.el.select('.input-group-addon', true).first();
40631         
40632         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40633         
40634         var _this = this;
40635         
40636         (function(){
40637             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40638             _this.list.setWidth(lw);
40639         }).defer(100);
40640         
40641         this.list.on('mouseover', this.onViewOver, this);
40642         this.list.on('mousemove', this.onViewMove, this);
40643         this.list.on('scroll', this.onViewScroll, this);
40644         
40645         if(!this.tpl){
40646             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40647         }
40648         
40649         this.view = new Roo.View(this.list, this.tpl, {
40650             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40651         });
40652         
40653         this.view.on('click', this.onViewClick, this);
40654         
40655         this.store.on('beforeload', this.onBeforeLoad, this);
40656         this.store.on('load', this.onLoad, this);
40657         this.store.on('loadexception', this.onLoadException, this);
40658         
40659         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40660             "up" : function(e){
40661                 this.inKeyMode = true;
40662                 this.selectPrev();
40663             },
40664
40665             "down" : function(e){
40666                 if(!this.isExpanded()){
40667                     this.onTriggerClick();
40668                 }else{
40669                     this.inKeyMode = true;
40670                     this.selectNext();
40671                 }
40672             },
40673
40674             "enter" : function(e){
40675                 this.collapse();
40676                 
40677                 if(this.fireEvent("specialkey", this, e)){
40678                     this.onViewClick(false);
40679                 }
40680                 
40681                 return true;
40682             },
40683
40684             "esc" : function(e){
40685                 this.collapse();
40686             },
40687
40688             "tab" : function(e){
40689                 this.collapse();
40690                 
40691                 if(this.fireEvent("specialkey", this, e)){
40692                     this.onViewClick(false);
40693                 }
40694                 
40695                 return true;
40696             },
40697
40698             scope : this,
40699
40700             doRelay : function(foo, bar, hname){
40701                 if(hname == 'down' || this.scope.isExpanded()){
40702                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40703                 }
40704                 return true;
40705             },
40706
40707             forceKeyDown: true
40708         });
40709         
40710         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40711         
40712     },
40713     
40714     initNumberEvent : function(e)
40715     {
40716         this.inputEl().on("keydown" , this.fireKey,  this);
40717         this.inputEl().on("focus", this.onFocus,  this);
40718         this.inputEl().on("blur", this.onBlur,  this);
40719         
40720         this.inputEl().relayEvent('keyup', this);
40721         
40722         if(this.indicator){
40723             this.indicator.addClass('invisible');
40724         }
40725  
40726         this.originalValue = this.getValue();
40727         
40728         if(this.validationEvent == 'keyup'){
40729             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40730             this.inputEl().on('keyup', this.filterValidation, this);
40731         }
40732         else if(this.validationEvent !== false){
40733             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40734         }
40735         
40736         if(this.selectOnFocus){
40737             this.on("focus", this.preFocus, this);
40738             
40739         }
40740         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40741             this.inputEl().on("keypress", this.filterKeys, this);
40742         } else {
40743             this.inputEl().relayEvent('keypress', this);
40744         }
40745         
40746         var allowed = "0123456789";
40747         
40748         if(this.allowDecimals){
40749             allowed += this.decimalSeparator;
40750         }
40751         
40752         if(this.allowNegative){
40753             allowed += "-";
40754         }
40755         
40756         if(this.thousandsDelimiter) {
40757             allowed += ",";
40758         }
40759         
40760         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40761         
40762         var keyPress = function(e){
40763             
40764             var k = e.getKey();
40765             
40766             var c = e.getCharCode();
40767             
40768             if(
40769                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40770                     allowed.indexOf(String.fromCharCode(c)) === -1
40771             ){
40772                 e.stopEvent();
40773                 return;
40774             }
40775             
40776             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40777                 return;
40778             }
40779             
40780             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40781                 e.stopEvent();
40782             }
40783         };
40784         
40785         this.inputEl().on("keypress", keyPress, this);
40786         
40787     },
40788     
40789     onTriggerClick : function(e)
40790     {   
40791         if(this.disabled){
40792             return;
40793         }
40794         
40795         this.page = 0;
40796         this.loadNext = false;
40797         
40798         if(this.isExpanded()){
40799             this.collapse();
40800             return;
40801         }
40802         
40803         this.hasFocus = true;
40804         
40805         if(this.triggerAction == 'all') {
40806             this.doQuery(this.allQuery, true);
40807             return;
40808         }
40809         
40810         this.doQuery(this.getRawValue());
40811     },
40812     
40813     getCurrency : function()
40814     {   
40815         var v = this.currencyEl().getValue();
40816         
40817         return v;
40818     },
40819     
40820     restrictHeight : function()
40821     {
40822         this.list.alignTo(this.currencyEl(), this.listAlign);
40823         this.list.alignTo(this.currencyEl(), this.listAlign);
40824     },
40825     
40826     onViewClick : function(view, doFocus, el, e)
40827     {
40828         var index = this.view.getSelectedIndexes()[0];
40829         
40830         var r = this.store.getAt(index);
40831         
40832         if(r){
40833             this.onSelect(r, index);
40834         }
40835     },
40836     
40837     onSelect : function(record, index){
40838         
40839         if(this.fireEvent('beforeselect', this, record, index) !== false){
40840         
40841             this.setFromCurrencyData(index > -1 ? record.data : false);
40842             
40843             this.collapse();
40844             
40845             this.fireEvent('select', this, record, index);
40846         }
40847     },
40848     
40849     setFromCurrencyData : function(o)
40850     {
40851         var currency = '';
40852         
40853         this.lastCurrency = o;
40854         
40855         if (this.currencyField) {
40856             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40857         } else {
40858             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40859         }
40860         
40861         this.lastSelectionText = currency;
40862         
40863         //setting default currency
40864         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40865             this.setCurrency(this.defaultCurrency);
40866             return;
40867         }
40868         
40869         this.setCurrency(currency);
40870     },
40871     
40872     setFromData : function(o)
40873     {
40874         var c = {};
40875         
40876         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40877         
40878         this.setFromCurrencyData(c);
40879         
40880         var value = '';
40881         
40882         if (this.name) {
40883             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40884         } else {
40885             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40886         }
40887         
40888         this.setValue(value);
40889         
40890     },
40891     
40892     setCurrency : function(v)
40893     {   
40894         this.currencyValue = v;
40895         
40896         if(this.rendered){
40897             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40898             this.validate();
40899         }
40900     },
40901     
40902     setValue : function(v)
40903     {
40904         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40905         
40906         this.value = v;
40907         
40908         if(this.rendered){
40909             
40910             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40911             
40912             this.inputEl().dom.value = (v == '') ? '' :
40913                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40914             
40915             if(!this.allowZero && v === '0') {
40916                 this.hiddenEl().dom.value = '';
40917                 this.inputEl().dom.value = '';
40918             }
40919             
40920             this.validate();
40921         }
40922     },
40923     
40924     getRawValue : function()
40925     {
40926         var v = this.inputEl().getValue();
40927         
40928         return v;
40929     },
40930     
40931     getValue : function()
40932     {
40933         return this.fixPrecision(this.parseValue(this.getRawValue()));
40934     },
40935     
40936     parseValue : function(value)
40937     {
40938         if(this.thousandsDelimiter) {
40939             value += "";
40940             r = new RegExp(",", "g");
40941             value = value.replace(r, "");
40942         }
40943         
40944         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40945         return isNaN(value) ? '' : value;
40946         
40947     },
40948     
40949     fixPrecision : function(value)
40950     {
40951         if(this.thousandsDelimiter) {
40952             value += "";
40953             r = new RegExp(",", "g");
40954             value = value.replace(r, "");
40955         }
40956         
40957         var nan = isNaN(value);
40958         
40959         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40960             return nan ? '' : value;
40961         }
40962         return parseFloat(value).toFixed(this.decimalPrecision);
40963     },
40964     
40965     decimalPrecisionFcn : function(v)
40966     {
40967         return Math.floor(v);
40968     },
40969     
40970     validateValue : function(value)
40971     {
40972         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40973             return false;
40974         }
40975         
40976         var num = this.parseValue(value);
40977         
40978         if(isNaN(num)){
40979             this.markInvalid(String.format(this.nanText, value));
40980             return false;
40981         }
40982         
40983         if(num < this.minValue){
40984             this.markInvalid(String.format(this.minText, this.minValue));
40985             return false;
40986         }
40987         
40988         if(num > this.maxValue){
40989             this.markInvalid(String.format(this.maxText, this.maxValue));
40990             return false;
40991         }
40992         
40993         return true;
40994     },
40995     
40996     validate : function()
40997     {
40998         if(this.disabled || this.allowBlank){
40999             this.markValid();
41000             return true;
41001         }
41002         
41003         var currency = this.getCurrency();
41004         
41005         if(this.validateValue(this.getRawValue()) && currency.length){
41006             this.markValid();
41007             return true;
41008         }
41009         
41010         this.markInvalid();
41011         return false;
41012     },
41013     
41014     getName: function()
41015     {
41016         return this.name;
41017     },
41018     
41019     beforeBlur : function()
41020     {
41021         if(!this.castInt){
41022             return;
41023         }
41024         
41025         var v = this.parseValue(this.getRawValue());
41026         
41027         if(v || v == 0){
41028             this.setValue(v);
41029         }
41030     },
41031     
41032     onBlur : function()
41033     {
41034         this.beforeBlur();
41035         
41036         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41037             //this.el.removeClass(this.focusClass);
41038         }
41039         
41040         this.hasFocus = false;
41041         
41042         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41043             this.validate();
41044         }
41045         
41046         var v = this.getValue();
41047         
41048         if(String(v) !== String(this.startValue)){
41049             this.fireEvent('change', this, v, this.startValue);
41050         }
41051         
41052         this.fireEvent("blur", this);
41053     },
41054     
41055     inputEl : function()
41056     {
41057         return this.el.select('.roo-money-amount-input', true).first();
41058     },
41059     
41060     currencyEl : function()
41061     {
41062         return this.el.select('.roo-money-currency-input', true).first();
41063     },
41064     
41065     hiddenEl : function()
41066     {
41067         return this.el.select('input.hidden-number-input',true).first();
41068     }
41069     
41070 });