roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     hide: function() {
921        
922      
923         this.el.hide();   
924     },
925     show: function() {
926        
927         this.el.show();   
928     },
929     setWeight : function(str)
930     {
931         this.el.removeClass(this.weightClass);
932         this.el.addClass('btn-' + str);        
933     }
934     
935     
936 });
937
938  /*
939  * - LGPL
940  *
941  * column
942  * 
943  */
944
945 /**
946  * @class Roo.bootstrap.Column
947  * @extends Roo.bootstrap.Component
948  * Bootstrap Column class
949  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
957  *
958  * 
959  * @cfg {Boolean} hidden (true|false) hide the element
960  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961  * @cfg {String} fa (ban|check|...) font awesome icon
962  * @cfg {Number} fasize (1|2|....) font awsome size
963
964  * @cfg {String} icon (info-sign|check|...) glyphicon name
965
966  * @cfg {String} html content of column.
967  * 
968  * @constructor
969  * Create a new Column
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Column = function(config){
974     Roo.bootstrap.Column.superclass.constructor.call(this, config);
975 };
976
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
978     
979     xs: false,
980     sm: false,
981     md: false,
982     lg: false,
983     xsoff: false,
984     smoff: false,
985     mdoff: false,
986     lgoff: false,
987     html: '',
988     offset: 0,
989     alert: false,
990     fa: false,
991     icon : false,
992     hidden : false,
993     fasize : 1,
994     
995     getAutoCreate : function(){
996         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
997         
998         cfg = {
999             tag: 'div',
1000             cls: 'column'
1001         };
1002         
1003         var settings=this;
1004         ['xs','sm','md','lg'].map(function(size){
1005             //Roo.log( size + ':' + settings[size]);
1006             
1007             if (settings[size+'off'] !== false) {
1008                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1009             }
1010             
1011             if (settings[size] === false) {
1012                 return;
1013             }
1014             
1015             if (!settings[size]) { // 0 = hidden
1016                 cfg.cls += ' hidden-' + size;
1017                 return;
1018             }
1019             cfg.cls += ' col-' + size + '-' + settings[size];
1020             
1021         });
1022         
1023         if (this.hidden) {
1024             cfg.cls += ' hidden';
1025         }
1026         
1027         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028             cfg.cls +=' alert alert-' + this.alert;
1029         }
1030         
1031         
1032         if (this.html.length) {
1033             cfg.html = this.html;
1034         }
1035         if (this.fa) {
1036             var fasize = '';
1037             if (this.fasize > 1) {
1038                 fasize = ' fa-' + this.fasize + 'x';
1039             }
1040             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041             
1042             
1043         }
1044         if (this.icon) {
1045             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1046         }
1047         
1048         return cfg;
1049     }
1050    
1051 });
1052
1053  
1054
1055  /*
1056  * - LGPL
1057  *
1058  * page container.
1059  * 
1060  */
1061
1062
1063 /**
1064  * @class Roo.bootstrap.Container
1065  * @extends Roo.bootstrap.Component
1066  * Bootstrap Container class
1067  * @cfg {Boolean} jumbotron is it a jumbotron element
1068  * @cfg {String} html content of element
1069  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1071  * @cfg {String} header content of header (for panel)
1072  * @cfg {String} footer content of footer (for panel)
1073  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074  * @cfg {String} tag (header|aside|section) type of HTML tag.
1075  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076  * @cfg {String} fa font awesome icon
1077  * @cfg {String} icon (info-sign|check|...) glyphicon name
1078  * @cfg {Boolean} hidden (true|false) hide the element
1079  * @cfg {Boolean} expandable (true|false) default false
1080  * @cfg {Boolean} expanded (true|false) default true
1081  * @cfg {String} rheader contet on the right of header
1082  * @cfg {Boolean} clickable (true|false) default false
1083
1084  *     
1085  * @constructor
1086  * Create a new Container
1087  * @param {Object} config The config object
1088  */
1089
1090 Roo.bootstrap.Container = function(config){
1091     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1092     
1093     this.addEvents({
1094         // raw events
1095          /**
1096          * @event expand
1097          * After the panel has been expand
1098          * 
1099          * @param {Roo.bootstrap.Container} this
1100          */
1101         "expand" : true,
1102         /**
1103          * @event collapse
1104          * After the panel has been collapsed
1105          * 
1106          * @param {Roo.bootstrap.Container} this
1107          */
1108         "collapse" : true,
1109         /**
1110          * @event click
1111          * When a element is chick
1112          * @param {Roo.bootstrap.Container} this
1113          * @param {Roo.EventObject} e
1114          */
1115         "click" : true
1116     });
1117 };
1118
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1120     
1121     jumbotron : false,
1122     well: '',
1123     panel : '',
1124     header: '',
1125     footer : '',
1126     sticky: '',
1127     tag : false,
1128     alert : false,
1129     fa: false,
1130     icon : false,
1131     expandable : false,
1132     rheader : '',
1133     expanded : true,
1134     clickable: false,
1135   
1136      
1137     getChildContainer : function() {
1138         
1139         if(!this.el){
1140             return false;
1141         }
1142         
1143         if (this.panel.length) {
1144             return this.el.select('.panel-body',true).first();
1145         }
1146         
1147         return this.el;
1148     },
1149     
1150     
1151     getAutoCreate : function(){
1152         
1153         var cfg = {
1154             tag : this.tag || 'div',
1155             html : '',
1156             cls : ''
1157         };
1158         if (this.jumbotron) {
1159             cfg.cls = 'jumbotron';
1160         }
1161         
1162         
1163         
1164         // - this is applied by the parent..
1165         //if (this.cls) {
1166         //    cfg.cls = this.cls + '';
1167         //}
1168         
1169         if (this.sticky.length) {
1170             
1171             var bd = Roo.get(document.body);
1172             if (!bd.hasClass('bootstrap-sticky')) {
1173                 bd.addClass('bootstrap-sticky');
1174                 Roo.select('html',true).setStyle('height', '100%');
1175             }
1176              
1177             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1178         }
1179         
1180         
1181         if (this.well.length) {
1182             switch (this.well) {
1183                 case 'lg':
1184                 case 'sm':
1185                     cfg.cls +=' well well-' +this.well;
1186                     break;
1187                 default:
1188                     cfg.cls +=' well';
1189                     break;
1190             }
1191         }
1192         
1193         if (this.hidden) {
1194             cfg.cls += ' hidden';
1195         }
1196         
1197         
1198         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199             cfg.cls +=' alert alert-' + this.alert;
1200         }
1201         
1202         var body = cfg;
1203         
1204         if (this.panel.length) {
1205             cfg.cls += ' panel panel-' + this.panel;
1206             cfg.cn = [];
1207             if (this.header.length) {
1208                 
1209                 var h = [];
1210                 
1211                 if(this.expandable){
1212                     
1213                     cfg.cls = cfg.cls + ' expandable';
1214                     
1215                     h.push({
1216                         tag: 'i',
1217                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1218                     });
1219                     
1220                 }
1221                 
1222                 h.push(
1223                     {
1224                         tag: 'span',
1225                         cls : 'panel-title',
1226                         html : (this.expandable ? '&nbsp;' : '') + this.header
1227                     },
1228                     {
1229                         tag: 'span',
1230                         cls: 'panel-header-right',
1231                         html: this.rheader
1232                     }
1233                 );
1234                 
1235                 cfg.cn.push({
1236                     cls : 'panel-heading',
1237                     style : this.expandable ? 'cursor: pointer' : '',
1238                     cn : h
1239                 });
1240                 
1241             }
1242             
1243             body = false;
1244             cfg.cn.push({
1245                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246                 html : this.html
1247             });
1248             
1249             
1250             if (this.footer.length) {
1251                 cfg.cn.push({
1252                     cls : 'panel-footer',
1253                     html : this.footer
1254                     
1255                 });
1256             }
1257             
1258         }
1259         
1260         if (body) {
1261             body.html = this.html || cfg.html;
1262             // prefix with the icons..
1263             if (this.fa) {
1264                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1265             }
1266             if (this.icon) {
1267                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268             }
1269             
1270             
1271         }
1272         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273             cfg.cls =  'container';
1274         }
1275         
1276         return cfg;
1277     },
1278     
1279     initEvents: function() 
1280     {
1281         if(this.expandable){
1282             var headerEl = this.headerEl();
1283         
1284             if(headerEl){
1285                 headerEl.on('click', this.onToggleClick, this);
1286             }
1287         }
1288         
1289         if(this.clickable){
1290             this.el.on('click', this.onClick, this);
1291         }
1292         
1293     },
1294     
1295     onToggleClick : function()
1296     {
1297         var headerEl = this.headerEl();
1298         
1299         if(!headerEl){
1300             return;
1301         }
1302         
1303         if(this.expanded){
1304             this.collapse();
1305             return;
1306         }
1307         
1308         this.expand();
1309     },
1310     
1311     expand : function()
1312     {
1313         if(this.fireEvent('expand', this)) {
1314             
1315             this.expanded = true;
1316             
1317             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1318             
1319             this.el.select('.panel-body',true).first().removeClass('hide');
1320             
1321             var toggleEl = this.toggleEl();
1322
1323             if(!toggleEl){
1324                 return;
1325             }
1326
1327             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328         }
1329         
1330     },
1331     
1332     collapse : function()
1333     {
1334         if(this.fireEvent('collapse', this)) {
1335             
1336             this.expanded = false;
1337             
1338             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339             this.el.select('.panel-body',true).first().addClass('hide');
1340         
1341             var toggleEl = this.toggleEl();
1342
1343             if(!toggleEl){
1344                 return;
1345             }
1346
1347             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1348         }
1349     },
1350     
1351     toggleEl : function()
1352     {
1353         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1354             return;
1355         }
1356         
1357         return this.el.select('.panel-heading .fa',true).first();
1358     },
1359     
1360     headerEl : function()
1361     {
1362         if(!this.el || !this.panel.length || !this.header.length){
1363             return;
1364         }
1365         
1366         return this.el.select('.panel-heading',true).first()
1367     },
1368     
1369     bodyEl : function()
1370     {
1371         if(!this.el || !this.panel.length){
1372             return;
1373         }
1374         
1375         return this.el.select('.panel-body',true).first()
1376     },
1377     
1378     titleEl : function()
1379     {
1380         if(!this.el || !this.panel.length || !this.header.length){
1381             return;
1382         }
1383         
1384         return this.el.select('.panel-title',true).first();
1385     },
1386     
1387     setTitle : function(v)
1388     {
1389         var titleEl = this.titleEl();
1390         
1391         if(!titleEl){
1392             return;
1393         }
1394         
1395         titleEl.dom.innerHTML = v;
1396     },
1397     
1398     getTitle : function()
1399     {
1400         
1401         var titleEl = this.titleEl();
1402         
1403         if(!titleEl){
1404             return '';
1405         }
1406         
1407         return titleEl.dom.innerHTML;
1408     },
1409     
1410     setRightTitle : function(v)
1411     {
1412         var t = this.el.select('.panel-header-right',true).first();
1413         
1414         if(!t){
1415             return;
1416         }
1417         
1418         t.dom.innerHTML = v;
1419     },
1420     
1421     onClick : function(e)
1422     {
1423         e.preventDefault();
1424         
1425         this.fireEvent('click', this, e);
1426     }
1427 });
1428
1429  /*
1430  * - LGPL
1431  *
1432  * image
1433  * 
1434  */
1435
1436
1437 /**
1438  * @class Roo.bootstrap.Img
1439  * @extends Roo.bootstrap.Component
1440  * Bootstrap Img class
1441  * @cfg {Boolean} imgResponsive false | true
1442  * @cfg {String} border rounded | circle | thumbnail
1443  * @cfg {String} src image source
1444  * @cfg {String} alt image alternative text
1445  * @cfg {String} href a tag href
1446  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447  * @cfg {String} xsUrl xs image source
1448  * @cfg {String} smUrl sm image source
1449  * @cfg {String} mdUrl md image source
1450  * @cfg {String} lgUrl lg image source
1451  * 
1452  * @constructor
1453  * Create a new Input
1454  * @param {Object} config The config object
1455  */
1456
1457 Roo.bootstrap.Img = function(config){
1458     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1459     
1460     this.addEvents({
1461         // img events
1462         /**
1463          * @event click
1464          * The img click event for the img.
1465          * @param {Roo.EventObject} e
1466          */
1467         "click" : true
1468     });
1469 };
1470
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1472     
1473     imgResponsive: true,
1474     border: '',
1475     src: 'about:blank',
1476     href: false,
1477     target: false,
1478     xsUrl: '',
1479     smUrl: '',
1480     mdUrl: '',
1481     lgUrl: '',
1482
1483     getAutoCreate : function()
1484     {   
1485         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486             return this.createSingleImg();
1487         }
1488         
1489         var cfg = {
1490             tag: 'div',
1491             cls: 'roo-image-responsive-group',
1492             cn: []
1493         };
1494         var _this = this;
1495         
1496         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1497             
1498             if(!_this[size + 'Url']){
1499                 return;
1500             }
1501             
1502             var img = {
1503                 tag: 'img',
1504                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505                 html: _this.html || cfg.html,
1506                 src: _this[size + 'Url']
1507             };
1508             
1509             img.cls += ' roo-image-responsive-' + size;
1510             
1511             var s = ['xs', 'sm', 'md', 'lg'];
1512             
1513             s.splice(s.indexOf(size), 1);
1514             
1515             Roo.each(s, function(ss){
1516                 img.cls += ' hidden-' + ss;
1517             });
1518             
1519             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520                 cfg.cls += ' img-' + _this.border;
1521             }
1522             
1523             if(_this.alt){
1524                 cfg.alt = _this.alt;
1525             }
1526             
1527             if(_this.href){
1528                 var a = {
1529                     tag: 'a',
1530                     href: _this.href,
1531                     cn: [
1532                         img
1533                     ]
1534                 };
1535
1536                 if(this.target){
1537                     a.target = _this.target;
1538                 }
1539             }
1540             
1541             cfg.cn.push((_this.href) ? a : img);
1542             
1543         });
1544         
1545         return cfg;
1546     },
1547     
1548     createSingleImg : function()
1549     {
1550         var cfg = {
1551             tag: 'img',
1552             cls: (this.imgResponsive) ? 'img-responsive' : '',
1553             html : null,
1554             src : 'about:blank'  // just incase src get's set to undefined?!?
1555         };
1556         
1557         cfg.html = this.html || cfg.html;
1558         
1559         cfg.src = this.src || cfg.src;
1560         
1561         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562             cfg.cls += ' img-' + this.border;
1563         }
1564         
1565         if(this.alt){
1566             cfg.alt = this.alt;
1567         }
1568         
1569         if(this.href){
1570             var a = {
1571                 tag: 'a',
1572                 href: this.href,
1573                 cn: [
1574                     cfg
1575                 ]
1576             };
1577             
1578             if(this.target){
1579                 a.target = this.target;
1580             }
1581             
1582         }
1583         
1584         return (this.href) ? a : cfg;
1585     },
1586     
1587     initEvents: function() 
1588     {
1589         if(!this.href){
1590             this.el.on('click', this.onClick, this);
1591         }
1592         
1593     },
1594     
1595     onClick : function(e)
1596     {
1597         Roo.log('img onclick');
1598         this.fireEvent('click', this, e);
1599     },
1600     /**
1601      * Sets the url of the image - used to update it
1602      * @param {String} url the url of the image
1603      */
1604     
1605     setSrc : function(url)
1606     {
1607         this.src =  url;
1608         
1609         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610             this.el.dom.src =  url;
1611             return;
1612         }
1613         
1614         this.el.select('img', true).first().dom.src =  url;
1615     }
1616     
1617     
1618    
1619 });
1620
1621  /*
1622  * - LGPL
1623  *
1624  * image
1625  * 
1626  */
1627
1628
1629 /**
1630  * @class Roo.bootstrap.Link
1631  * @extends Roo.bootstrap.Component
1632  * Bootstrap Link Class
1633  * @cfg {String} alt image alternative text
1634  * @cfg {String} href a tag href
1635  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636  * @cfg {String} html the content of the link.
1637  * @cfg {String} anchor name for the anchor link
1638  * @cfg {String} fa - favicon
1639
1640  * @cfg {Boolean} preventDefault (true | false) default false
1641
1642  * 
1643  * @constructor
1644  * Create a new Input
1645  * @param {Object} config The config object
1646  */
1647
1648 Roo.bootstrap.Link = function(config){
1649     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1650     
1651     this.addEvents({
1652         // img events
1653         /**
1654          * @event click
1655          * The img click event for the img.
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1663     
1664     href: false,
1665     target: false,
1666     preventDefault: false,
1667     anchor : false,
1668     alt : false,
1669     fa: false,
1670
1671
1672     getAutoCreate : function()
1673     {
1674         var html = this.html || '';
1675         
1676         if (this.fa !== false) {
1677             html = '<i class="fa fa-' + this.fa + '"></i>';
1678         }
1679         var cfg = {
1680             tag: 'a'
1681         };
1682         // anchor's do not require html/href...
1683         if (this.anchor === false) {
1684             cfg.html = html;
1685             cfg.href = this.href || '#';
1686         } else {
1687             cfg.name = this.anchor;
1688             if (this.html !== false || this.fa !== false) {
1689                 cfg.html = html;
1690             }
1691             if (this.href !== false) {
1692                 cfg.href = this.href;
1693             }
1694         }
1695         
1696         if(this.alt !== false){
1697             cfg.alt = this.alt;
1698         }
1699         
1700         
1701         if(this.target !== false) {
1702             cfg.target = this.target;
1703         }
1704         
1705         return cfg;
1706     },
1707     
1708     initEvents: function() {
1709         
1710         if(!this.href || this.preventDefault){
1711             this.el.on('click', this.onClick, this);
1712         }
1713     },
1714     
1715     onClick : function(e)
1716     {
1717         if(this.preventDefault){
1718             e.preventDefault();
1719         }
1720         //Roo.log('img onclick');
1721         this.fireEvent('click', this, e);
1722     }
1723    
1724 });
1725
1726  /*
1727  * - LGPL
1728  *
1729  * header
1730  * 
1731  */
1732
1733 /**
1734  * @class Roo.bootstrap.Header
1735  * @extends Roo.bootstrap.Component
1736  * Bootstrap Header class
1737  * @cfg {String} html content of header
1738  * @cfg {Number} level (1|2|3|4|5|6) default 1
1739  * 
1740  * @constructor
1741  * Create a new Header
1742  * @param {Object} config The config object
1743  */
1744
1745
1746 Roo.bootstrap.Header  = function(config){
1747     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1748 };
1749
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1751     
1752     //href : false,
1753     html : false,
1754     level : 1,
1755     
1756     
1757     
1758     getAutoCreate : function(){
1759         
1760         
1761         
1762         var cfg = {
1763             tag: 'h' + (1 *this.level),
1764             html: this.html || ''
1765         } ;
1766         
1767         return cfg;
1768     }
1769    
1770 });
1771
1772  
1773
1774  /*
1775  * Based on:
1776  * Ext JS Library 1.1.1
1777  * Copyright(c) 2006-2007, Ext JS, LLC.
1778  *
1779  * Originally Released Under LGPL - original licence link has changed is not relivant.
1780  *
1781  * Fork - LGPL
1782  * <script type="text/javascript">
1783  */
1784  
1785 /**
1786  * @class Roo.bootstrap.MenuMgr
1787  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1788  * @singleton
1789  */
1790 Roo.bootstrap.MenuMgr = function(){
1791    var menus, active, groups = {}, attached = false, lastShow = new Date();
1792
1793    // private - called when first menu is created
1794    function init(){
1795        menus = {};
1796        active = new Roo.util.MixedCollection();
1797        Roo.get(document).addKeyListener(27, function(){
1798            if(active.length > 0){
1799                hideAll();
1800            }
1801        });
1802    }
1803
1804    // private
1805    function hideAll(){
1806        if(active && active.length > 0){
1807            var c = active.clone();
1808            c.each(function(m){
1809                m.hide();
1810            });
1811        }
1812    }
1813
1814    // private
1815    function onHide(m){
1816        active.remove(m);
1817        if(active.length < 1){
1818            Roo.get(document).un("mouseup", onMouseDown);
1819             
1820            attached = false;
1821        }
1822    }
1823
1824    // private
1825    function onShow(m){
1826        var last = active.last();
1827        lastShow = new Date();
1828        active.add(m);
1829        if(!attached){
1830           Roo.get(document).on("mouseup", onMouseDown);
1831            
1832            attached = true;
1833        }
1834        if(m.parentMenu){
1835           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836           m.parentMenu.activeChild = m;
1837        }else if(last && last.isVisible()){
1838           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839        }
1840    }
1841
1842    // private
1843    function onBeforeHide(m){
1844        if(m.activeChild){
1845            m.activeChild.hide();
1846        }
1847        if(m.autoHideTimer){
1848            clearTimeout(m.autoHideTimer);
1849            delete m.autoHideTimer;
1850        }
1851    }
1852
1853    // private
1854    function onBeforeShow(m){
1855        var pm = m.parentMenu;
1856        if(!pm && !m.allowOtherMenus){
1857            hideAll();
1858        }else if(pm && pm.activeChild && active != m){
1859            pm.activeChild.hide();
1860        }
1861    }
1862
1863    // private this should really trigger on mouseup..
1864    function onMouseDown(e){
1865         Roo.log("on Mouse Up");
1866         
1867         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868             Roo.log("MenuManager hideAll");
1869             hideAll();
1870             e.stopEvent();
1871         }
1872         
1873         
1874    }
1875
1876    // private
1877    function onBeforeCheck(mi, state){
1878        if(state){
1879            var g = groups[mi.group];
1880            for(var i = 0, l = g.length; i < l; i++){
1881                if(g[i] != mi){
1882                    g[i].setChecked(false);
1883                }
1884            }
1885        }
1886    }
1887
1888    return {
1889
1890        /**
1891         * Hides all menus that are currently visible
1892         */
1893        hideAll : function(){
1894             hideAll();  
1895        },
1896
1897        // private
1898        register : function(menu){
1899            if(!menus){
1900                init();
1901            }
1902            menus[menu.id] = menu;
1903            menu.on("beforehide", onBeforeHide);
1904            menu.on("hide", onHide);
1905            menu.on("beforeshow", onBeforeShow);
1906            menu.on("show", onShow);
1907            var g = menu.group;
1908            if(g && menu.events["checkchange"]){
1909                if(!groups[g]){
1910                    groups[g] = [];
1911                }
1912                groups[g].push(menu);
1913                menu.on("checkchange", onCheck);
1914            }
1915        },
1916
1917         /**
1918          * Returns a {@link Roo.menu.Menu} object
1919          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920          * be used to generate and return a new Menu instance.
1921          */
1922        get : function(menu){
1923            if(typeof menu == "string"){ // menu id
1924                return menus[menu];
1925            }else if(menu.events){  // menu instance
1926                return menu;
1927            }
1928            /*else if(typeof menu.length == 'number'){ // array of menu items?
1929                return new Roo.bootstrap.Menu({items:menu});
1930            }else{ // otherwise, must be a config
1931                return new Roo.bootstrap.Menu(menu);
1932            }
1933            */
1934            return false;
1935        },
1936
1937        // private
1938        unregister : function(menu){
1939            delete menus[menu.id];
1940            menu.un("beforehide", onBeforeHide);
1941            menu.un("hide", onHide);
1942            menu.un("beforeshow", onBeforeShow);
1943            menu.un("show", onShow);
1944            var g = menu.group;
1945            if(g && menu.events["checkchange"]){
1946                groups[g].remove(menu);
1947                menu.un("checkchange", onCheck);
1948            }
1949        },
1950
1951        // private
1952        registerCheckable : function(menuItem){
1953            var g = menuItem.group;
1954            if(g){
1955                if(!groups[g]){
1956                    groups[g] = [];
1957                }
1958                groups[g].push(menuItem);
1959                menuItem.on("beforecheckchange", onBeforeCheck);
1960            }
1961        },
1962
1963        // private
1964        unregisterCheckable : function(menuItem){
1965            var g = menuItem.group;
1966            if(g){
1967                groups[g].remove(menuItem);
1968                menuItem.un("beforecheckchange", onBeforeCheck);
1969            }
1970        }
1971    };
1972 }();/*
1973  * - LGPL
1974  *
1975  * menu
1976  * 
1977  */
1978
1979 /**
1980  * @class Roo.bootstrap.Menu
1981  * @extends Roo.bootstrap.Component
1982  * Bootstrap Menu class - container for MenuItems
1983  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1985  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1986  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1987  * 
1988  * @constructor
1989  * Create a new Menu
1990  * @param {Object} config The config object
1991  */
1992
1993
1994 Roo.bootstrap.Menu = function(config){
1995     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996     if (this.registerMenu && this.type != 'treeview')  {
1997         Roo.bootstrap.MenuMgr.register(this);
1998     }
1999     this.addEvents({
2000         /**
2001          * @event beforeshow
2002          * Fires before this menu is displayed
2003          * @param {Roo.menu.Menu} this
2004          */
2005         beforeshow : true,
2006         /**
2007          * @event beforehide
2008          * Fires before this menu is hidden
2009          * @param {Roo.menu.Menu} this
2010          */
2011         beforehide : true,
2012         /**
2013          * @event show
2014          * Fires after this menu is displayed
2015          * @param {Roo.menu.Menu} this
2016          */
2017         show : true,
2018         /**
2019          * @event hide
2020          * Fires after this menu is hidden
2021          * @param {Roo.menu.Menu} this
2022          */
2023         hide : true,
2024         /**
2025          * @event click
2026          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029          * @param {Roo.EventObject} e
2030          */
2031         click : true,
2032         /**
2033          * @event mouseover
2034          * Fires when the mouse is hovering over this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseover : true,
2040         /**
2041          * @event mouseout
2042          * Fires when the mouse exits this menu
2043          * @param {Roo.menu.Menu} this
2044          * @param {Roo.EventObject} e
2045          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046          */
2047         mouseout : true,
2048         /**
2049          * @event itemclick
2050          * Fires when a menu item contained in this menu is clicked
2051          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052          * @param {Roo.EventObject} e
2053          */
2054         itemclick: true
2055     });
2056     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2057 };
2058
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2060     
2061    /// html : false,
2062     //align : '',
2063     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2064     type: false,
2065     /**
2066      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2067      */
2068     registerMenu : true,
2069     
2070     menuItems :false, // stores the menu items..
2071     
2072     hidden:true,
2073         
2074     parentMenu : false,
2075     
2076     stopEvent : true,
2077     
2078     isLink : false,
2079     
2080     getChildContainer : function() {
2081         return this.el;  
2082     },
2083     
2084     getAutoCreate : function(){
2085          
2086         //if (['right'].indexOf(this.align)!==-1) {
2087         //    cfg.cn[1].cls += ' pull-right'
2088         //}
2089         
2090         
2091         var cfg = {
2092             tag : 'ul',
2093             cls : 'dropdown-menu' ,
2094             style : 'z-index:1000'
2095             
2096         };
2097         
2098         if (this.type === 'submenu') {
2099             cfg.cls = 'submenu active';
2100         }
2101         if (this.type === 'treeview') {
2102             cfg.cls = 'treeview-menu';
2103         }
2104         
2105         return cfg;
2106     },
2107     initEvents : function() {
2108         
2109        // Roo.log("ADD event");
2110        // Roo.log(this.triggerEl.dom);
2111         
2112         this.triggerEl.on('click', this.onTriggerClick, this);
2113         
2114         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2115         
2116         this.triggerEl.addClass('dropdown-toggle');
2117         
2118         if (Roo.isTouch) {
2119             this.el.on('touchstart'  , this.onTouch, this);
2120         }
2121         this.el.on('click' , this.onClick, this);
2122
2123         this.el.on("mouseover", this.onMouseOver, this);
2124         this.el.on("mouseout", this.onMouseOut, this);
2125         
2126     },
2127     
2128     findTargetItem : function(e)
2129     {
2130         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2131         if(!t){
2132             return false;
2133         }
2134         //Roo.log(t);         Roo.log(t.id);
2135         if(t && t.id){
2136             //Roo.log(this.menuitems);
2137             return this.menuitems.get(t.id);
2138             
2139             //return this.items.get(t.menuItemId);
2140         }
2141         
2142         return false;
2143     },
2144     
2145     onTouch : function(e) 
2146     {
2147         Roo.log("menu.onTouch");
2148         //e.stopEvent(); this make the user popdown broken
2149         this.onClick(e);
2150     },
2151     
2152     onClick : function(e)
2153     {
2154         Roo.log("menu.onClick");
2155         
2156         var t = this.findTargetItem(e);
2157         if(!t || t.isContainer){
2158             return;
2159         }
2160         Roo.log(e);
2161         /*
2162         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2163             if(t == this.activeItem && t.shouldDeactivate(e)){
2164                 this.activeItem.deactivate();
2165                 delete this.activeItem;
2166                 return;
2167             }
2168             if(t.canActivate){
2169                 this.setActiveItem(t, true);
2170             }
2171             return;
2172             
2173             
2174         }
2175         */
2176        
2177         Roo.log('pass click event');
2178         
2179         t.onClick(e);
2180         
2181         this.fireEvent("click", this, t, e);
2182         
2183         var _this = this;
2184         
2185         if(!t.href.length || t.href == '#'){
2186             (function() { _this.hide(); }).defer(100);
2187         }
2188         
2189     },
2190     
2191     onMouseOver : function(e){
2192         var t  = this.findTargetItem(e);
2193         //Roo.log(t);
2194         //if(t){
2195         //    if(t.canActivate && !t.disabled){
2196         //        this.setActiveItem(t, true);
2197         //    }
2198         //}
2199         
2200         this.fireEvent("mouseover", this, e, t);
2201     },
2202     isVisible : function(){
2203         return !this.hidden;
2204     },
2205      onMouseOut : function(e){
2206         var t  = this.findTargetItem(e);
2207         
2208         //if(t ){
2209         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2210         //        this.activeItem.deactivate();
2211         //        delete this.activeItem;
2212         //    }
2213         //}
2214         this.fireEvent("mouseout", this, e, t);
2215     },
2216     
2217     
2218     /**
2219      * Displays this menu relative to another element
2220      * @param {String/HTMLElement/Roo.Element} element The element to align to
2221      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222      * the element (defaults to this.defaultAlign)
2223      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2224      */
2225     show : function(el, pos, parentMenu){
2226         this.parentMenu = parentMenu;
2227         if(!this.el){
2228             this.render();
2229         }
2230         this.fireEvent("beforeshow", this);
2231         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2232     },
2233      /**
2234      * Displays this menu at a specific xy position
2235      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2237      */
2238     showAt : function(xy, parentMenu, /* private: */_e){
2239         this.parentMenu = parentMenu;
2240         if(!this.el){
2241             this.render();
2242         }
2243         if(_e !== false){
2244             this.fireEvent("beforeshow", this);
2245             //xy = this.el.adjustForConstraints(xy);
2246         }
2247         
2248         //this.el.show();
2249         this.hideMenuItems();
2250         this.hidden = false;
2251         this.triggerEl.addClass('open');
2252         
2253         // reassign x when hitting right
2254         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2256         }
2257         
2258         // reassign y when hitting bottom
2259         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2261         }
2262         
2263         // but the list may align on trigger left or trigger top... should it be a properity?
2264         
2265         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266             this.el.setXY(xy);
2267         }
2268         
2269         this.focus();
2270         this.fireEvent("show", this);
2271     },
2272     
2273     focus : function(){
2274         return;
2275         if(!this.hidden){
2276             this.doFocus.defer(50, this);
2277         }
2278     },
2279
2280     doFocus : function(){
2281         if(!this.hidden){
2282             this.focusEl.focus();
2283         }
2284     },
2285
2286     /**
2287      * Hides this menu and optionally all parent menus
2288      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2289      */
2290     hide : function(deep)
2291     {
2292         
2293         this.hideMenuItems();
2294         if(this.el && this.isVisible()){
2295             this.fireEvent("beforehide", this);
2296             if(this.activeItem){
2297                 this.activeItem.deactivate();
2298                 this.activeItem = null;
2299             }
2300             this.triggerEl.removeClass('open');;
2301             this.hidden = true;
2302             this.fireEvent("hide", this);
2303         }
2304         if(deep === true && this.parentMenu){
2305             this.parentMenu.hide(true);
2306         }
2307     },
2308     
2309     onTriggerClick : function(e)
2310     {
2311         Roo.log('trigger click');
2312         
2313         var target = e.getTarget();
2314         
2315         Roo.log(target.nodeName.toLowerCase());
2316         
2317         if(target.nodeName.toLowerCase() === 'i'){
2318             e.preventDefault();
2319         }
2320         
2321     },
2322     
2323     onTriggerPress  : function(e)
2324     {
2325         Roo.log('trigger press');
2326         //Roo.log(e.getTarget());
2327        // Roo.log(this.triggerEl.dom);
2328        
2329         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330         var pel = Roo.get(e.getTarget());
2331         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332             Roo.log('is treeview or dropdown?');
2333             return;
2334         }
2335         
2336         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2337             return;
2338         }
2339         
2340         if (this.isVisible()) {
2341             Roo.log('hide');
2342             this.hide();
2343         } else {
2344             Roo.log('show');
2345             this.show(this.triggerEl, false, false);
2346         }
2347         
2348         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2349             e.stopEvent();
2350         }
2351         
2352     },
2353        
2354     
2355     hideMenuItems : function()
2356     {
2357         Roo.log("hide Menu Items");
2358         if (!this.el) { 
2359             return;
2360         }
2361         //$(backdrop).remove()
2362         this.el.select('.open',true).each(function(aa) {
2363             
2364             aa.removeClass('open');
2365           //var parent = getParent($(this))
2366           //var relatedTarget = { relatedTarget: this }
2367           
2368            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369           //if (e.isDefaultPrevented()) return
2370            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2371         });
2372     },
2373     addxtypeChild : function (tree, cntr) {
2374         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2375           
2376         this.menuitems.add(comp);
2377         return comp;
2378
2379     },
2380     getEl : function()
2381     {
2382         Roo.log(this.el);
2383         return this.el;
2384     },
2385     
2386     clear : function()
2387     {
2388         this.getEl().dom.innerHTML = '';
2389         this.menuitems.clear();
2390     }
2391 });
2392
2393  
2394  /*
2395  * - LGPL
2396  *
2397  * menu item
2398  * 
2399  */
2400
2401
2402 /**
2403  * @class Roo.bootstrap.MenuItem
2404  * @extends Roo.bootstrap.Component
2405  * Bootstrap MenuItem class
2406  * @cfg {String} html the menu label
2407  * @cfg {String} href the link
2408  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2411  * @cfg {String} fa favicon to show on left of menu item.
2412  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2413  * 
2414  * 
2415  * @constructor
2416  * Create a new MenuItem
2417  * @param {Object} config The config object
2418  */
2419
2420
2421 Roo.bootstrap.MenuItem = function(config){
2422     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423     this.addEvents({
2424         // raw events
2425         /**
2426          * @event click
2427          * The raw click event for the entire grid.
2428          * @param {Roo.bootstrap.MenuItem} this
2429          * @param {Roo.EventObject} e
2430          */
2431         "click" : true
2432     });
2433 };
2434
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2436     
2437     href : false,
2438     html : false,
2439     preventDefault: false,
2440     isContainer : false,
2441     active : false,
2442     fa: false,
2443     
2444     getAutoCreate : function(){
2445         
2446         if(this.isContainer){
2447             return {
2448                 tag: 'li',
2449                 cls: 'dropdown-menu-item'
2450             };
2451         }
2452         var ctag = {
2453             tag: 'span',
2454             html: 'Link'
2455         };
2456         
2457         var anc = {
2458             tag : 'a',
2459             href : '#',
2460             cn : [  ]
2461         };
2462         
2463         if (this.fa !== false) {
2464             anc.cn.push({
2465                 tag : 'i',
2466                 cls : 'fa fa-' + this.fa
2467             });
2468         }
2469         
2470         anc.cn.push(ctag);
2471         
2472         
2473         var cfg= {
2474             tag: 'li',
2475             cls: 'dropdown-menu-item',
2476             cn: [ anc ]
2477         };
2478         if (this.parent().type == 'treeview') {
2479             cfg.cls = 'treeview-menu';
2480         }
2481         if (this.active) {
2482             cfg.cls += ' active';
2483         }
2484         
2485         
2486         
2487         anc.href = this.href || cfg.cn[0].href ;
2488         ctag.html = this.html || cfg.cn[0].html ;
2489         return cfg;
2490     },
2491     
2492     initEvents: function()
2493     {
2494         if (this.parent().type == 'treeview') {
2495             this.el.select('a').on('click', this.onClick, this);
2496         }
2497         
2498         if (this.menu) {
2499             this.menu.parentType = this.xtype;
2500             this.menu.triggerEl = this.el;
2501             this.menu = this.addxtype(Roo.apply({}, this.menu));
2502         }
2503         
2504     },
2505     onClick : function(e)
2506     {
2507         Roo.log('item on click ');
2508         
2509         if(this.preventDefault){
2510             e.preventDefault();
2511         }
2512         //this.parent().hideMenuItems();
2513         
2514         this.fireEvent('click', this, e);
2515     },
2516     getEl : function()
2517     {
2518         return this.el;
2519     } 
2520 });
2521
2522  
2523
2524  /*
2525  * - LGPL
2526  *
2527  * menu separator
2528  * 
2529  */
2530
2531
2532 /**
2533  * @class Roo.bootstrap.MenuSeparator
2534  * @extends Roo.bootstrap.Component
2535  * Bootstrap MenuSeparator class
2536  * 
2537  * @constructor
2538  * Create a new MenuItem
2539  * @param {Object} config The config object
2540  */
2541
2542
2543 Roo.bootstrap.MenuSeparator = function(config){
2544     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2545 };
2546
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2548     
2549     getAutoCreate : function(){
2550         var cfg = {
2551             cls: 'divider',
2552             tag : 'li'
2553         };
2554         
2555         return cfg;
2556     }
2557    
2558 });
2559
2560  
2561
2562  
2563 /*
2564 * Licence: LGPL
2565 */
2566
2567 /**
2568  * @class Roo.bootstrap.Modal
2569  * @extends Roo.bootstrap.Component
2570  * Bootstrap Modal class
2571  * @cfg {String} title Title of dialog
2572  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2574  * @cfg {Boolean} specificTitle default false
2575  * @cfg {Array} buttons Array of buttons or standard button set..
2576  * @cfg {String} buttonPosition (left|right|center) default right
2577  * @cfg {Boolean} animate default true
2578  * @cfg {Boolean} allow_close default true
2579  * @cfg {Boolean} fitwindow default false
2580  * @cfg {String} size (sm|lg) default empty
2581  * @cfg {Number} max_width set the max width of modal
2582  *
2583  *
2584  * @constructor
2585  * Create a new Modal Dialog
2586  * @param {Object} config The config object
2587  */
2588
2589 Roo.bootstrap.Modal = function(config){
2590     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2591     this.addEvents({
2592         // raw events
2593         /**
2594          * @event btnclick
2595          * The raw btnclick event for the button
2596          * @param {Roo.EventObject} e
2597          */
2598         "btnclick" : true,
2599         /**
2600          * @event resize
2601          * Fire when dialog resize
2602          * @param {Roo.bootstrap.Modal} this
2603          * @param {Roo.EventObject} e
2604          */
2605         "resize" : true
2606     });
2607     this.buttons = this.buttons || [];
2608
2609     if (this.tmpl) {
2610         this.tmpl = Roo.factory(this.tmpl);
2611     }
2612
2613 };
2614
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2616
2617     title : 'test dialog',
2618
2619     buttons : false,
2620
2621     // set on load...
2622
2623     html: false,
2624
2625     tmp: false,
2626
2627     specificTitle: false,
2628
2629     buttonPosition: 'right',
2630
2631     allow_close : true,
2632
2633     animate : true,
2634
2635     fitwindow: false,
2636
2637
2638      // private
2639     dialogEl: false,
2640     bodyEl:  false,
2641     footerEl:  false,
2642     titleEl:  false,
2643     closeEl:  false,
2644
2645     size: '',
2646     
2647     max_width: 0,
2648     
2649     fit_content: false,
2650
2651     onRender : function(ct, position)
2652     {
2653         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2654
2655         if(!this.el){
2656             var cfg = Roo.apply({},  this.getAutoCreate());
2657             cfg.id = Roo.id();
2658             //if(!cfg.name){
2659             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2660             //}
2661             //if (!cfg.name.length) {
2662             //    delete cfg.name;
2663            // }
2664             if (this.cls) {
2665                 cfg.cls += ' ' + this.cls;
2666             }
2667             if (this.style) {
2668                 cfg.style = this.style;
2669             }
2670             this.el = Roo.get(document.body).createChild(cfg, position);
2671         }
2672         //var type = this.el.dom.type;
2673
2674
2675         if(this.tabIndex !== undefined){
2676             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2677         }
2678
2679         this.dialogEl = this.el.select('.modal-dialog',true).first();
2680         this.bodyEl = this.el.select('.modal-body',true).first();
2681         this.closeEl = this.el.select('.modal-header .close', true).first();
2682         this.headerEl = this.el.select('.modal-header',true).first();
2683         this.titleEl = this.el.select('.modal-title',true).first();
2684         this.footerEl = this.el.select('.modal-footer',true).first();
2685
2686         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2687         
2688         //this.el.addClass("x-dlg-modal");
2689
2690         if (this.buttons.length) {
2691             Roo.each(this.buttons, function(bb) {
2692                 var b = Roo.apply({}, bb);
2693                 b.xns = b.xns || Roo.bootstrap;
2694                 b.xtype = b.xtype || 'Button';
2695                 if (typeof(b.listeners) == 'undefined') {
2696                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2697                 }
2698
2699                 var btn = Roo.factory(b);
2700
2701                 btn.render(this.el.select('.modal-footer div').first());
2702
2703             },this);
2704         }
2705         // render the children.
2706         var nitems = [];
2707
2708         if(typeof(this.items) != 'undefined'){
2709             var items = this.items;
2710             delete this.items;
2711
2712             for(var i =0;i < items.length;i++) {
2713                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2714             }
2715         }
2716
2717         this.items = nitems;
2718
2719         // where are these used - they used to be body/close/footer
2720
2721
2722         this.initEvents();
2723         //this.el.addClass([this.fieldClass, this.cls]);
2724
2725     },
2726
2727     getAutoCreate : function()
2728     {
2729         var bdy = {
2730                 cls : 'modal-body',
2731                 html : this.html || ''
2732         };
2733
2734         var title = {
2735             tag: 'h4',
2736             cls : 'modal-title',
2737             html : this.title
2738         };
2739
2740         if(this.specificTitle){
2741             title = this.title;
2742
2743         };
2744
2745         var header = [];
2746         if (this.allow_close) {
2747             header.push({
2748                 tag: 'button',
2749                 cls : 'close',
2750                 html : '&times'
2751             });
2752         }
2753
2754         header.push(title);
2755
2756         var size = '';
2757
2758         if(this.size.length){
2759             size = 'modal-' + this.size;
2760         }
2761
2762         var modal = {
2763             cls: "modal",
2764              cn : [
2765                 {
2766                     cls: "modal-dialog " + size,
2767                     cn : [
2768                         {
2769                             cls : "modal-content",
2770                             cn : [
2771                                 {
2772                                     cls : 'modal-header',
2773                                     cn : header
2774                                 },
2775                                 bdy,
2776                                 {
2777                                     cls : 'modal-footer',
2778                                     cn : [
2779                                         {
2780                                             tag: 'div',
2781                                             cls: 'btn-' + this.buttonPosition
2782                                         }
2783                                     ]
2784
2785                                 }
2786
2787
2788                             ]
2789
2790                         }
2791                     ]
2792
2793                 }
2794             ]
2795         };
2796
2797         if(this.animate){
2798             modal.cls += ' fade';
2799         }
2800
2801         return modal;
2802
2803     },
2804     getChildContainer : function() {
2805
2806          return this.bodyEl;
2807
2808     },
2809     getButtonContainer : function() {
2810          return this.el.select('.modal-footer div',true).first();
2811
2812     },
2813     initEvents : function()
2814     {
2815         if (this.allow_close) {
2816             this.closeEl.on('click', this.hide, this);
2817         }
2818         Roo.EventManager.onWindowResize(this.resize, this, true);
2819
2820
2821     },
2822
2823     resize : function()
2824     {
2825         this.maskEl.setSize(
2826             Roo.lib.Dom.getViewWidth(true),
2827             Roo.lib.Dom.getViewHeight(true)
2828         );
2829         
2830         if (this.fitwindow) {
2831             this.setSize(
2832                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2833                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2834             );
2835             return;
2836         }
2837         
2838         if(this.max_width !== 0) {
2839             
2840             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2841             
2842             if(this.height) {
2843                 this.setSize(
2844                     w,
2845                     this.height <= Roo.lib.Dom.getViewportHeight(true) - 60 ? 
2846                         this.height : Roo.lib.Dom.getViewportHeight(true) - 60
2847                 );
2848                 return;
2849             }
2850             
2851             if(!this.fit_content) {
2852                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2853                 return;
2854             }
2855             
2856             this.setSize(w, Math.min(
2857                 60 +
2858                 this.headerEl.getHeight() + 
2859                 this.footerEl.getHeight() + 
2860                 this.getChildHeight(this.bodyEl.dom.childNodes),
2861                 Roo.lib.Dom.getViewportHeight(true) - 60)
2862             );
2863         }
2864         
2865     },
2866
2867     setSize : function(w,h)
2868     {
2869         if (!w && !h) {
2870             return;
2871         }
2872         
2873         this.resizeTo(w,h);
2874     },
2875
2876     show : function() {
2877
2878         if (!this.rendered) {
2879             this.render();
2880         }
2881
2882         //this.el.setStyle('display', 'block');
2883         this.el.removeClass('hideing');        
2884         this.el.addClass('show');
2885  
2886         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2887             var _this = this;
2888             (function(){
2889                 this.el.addClass('in');
2890             }).defer(50, this);
2891         }else{
2892             this.el.addClass('in');
2893         }
2894
2895         // not sure how we can show data in here..
2896         //if (this.tmpl) {
2897         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2898         //}
2899
2900         Roo.get(document.body).addClass("x-body-masked");
2901         
2902         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2903         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2904         this.maskEl.addClass('show');
2905         
2906         this.resize();
2907         
2908         this.fireEvent('show', this);
2909
2910         // set zindex here - otherwise it appears to be ignored...
2911         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2912
2913         (function () {
2914             this.items.forEach( function(e) {
2915                 e.layout ? e.layout() : false;
2916
2917             });
2918         }).defer(100,this);
2919
2920     },
2921     hide : function()
2922     {
2923         if(this.fireEvent("beforehide", this) !== false){
2924             this.maskEl.removeClass('show');
2925             Roo.get(document.body).removeClass("x-body-masked");
2926             this.el.removeClass('in');
2927             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2928
2929             if(this.animate){ // why
2930                 this.el.addClass('hideing');
2931                 (function(){
2932                     if (!this.el.hasClass('hideing')) {
2933                         return; // it's been shown again...
2934                     }
2935                     this.el.removeClass('show');
2936                     this.el.removeClass('hideing');
2937                 }).defer(150,this);
2938                 
2939             }else{
2940                  this.el.removeClass('show');
2941             }
2942             this.fireEvent('hide', this);
2943         }
2944     },
2945     isVisible : function()
2946     {
2947         
2948         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2949         
2950     },
2951
2952     addButton : function(str, cb)
2953     {
2954
2955
2956         var b = Roo.apply({}, { html : str } );
2957         b.xns = b.xns || Roo.bootstrap;
2958         b.xtype = b.xtype || 'Button';
2959         if (typeof(b.listeners) == 'undefined') {
2960             b.listeners = { click : cb.createDelegate(this)  };
2961         }
2962
2963         var btn = Roo.factory(b);
2964
2965         btn.render(this.el.select('.modal-footer div').first());
2966
2967         return btn;
2968
2969     },
2970
2971     setDefaultButton : function(btn)
2972     {
2973         //this.el.select('.modal-footer').()
2974     },
2975     diff : false,
2976
2977     resizeTo: function(w,h)
2978     {
2979         // skip.. ?? why??
2980
2981         this.dialogEl.setWidth(w);
2982         if (this.diff === false) {
2983             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2984         }
2985
2986         this.bodyEl.setHeight(h - this.diff);
2987
2988         this.fireEvent('resize', this);
2989
2990     },
2991     setContentSize  : function(w, h)
2992     {
2993
2994     },
2995     onButtonClick: function(btn,e)
2996     {
2997         //Roo.log([a,b,c]);
2998         this.fireEvent('btnclick', btn.name, e);
2999     },
3000      /**
3001      * Set the title of the Dialog
3002      * @param {String} str new Title
3003      */
3004     setTitle: function(str) {
3005         this.titleEl.dom.innerHTML = str;
3006     },
3007     /**
3008      * Set the body of the Dialog
3009      * @param {String} str new Title
3010      */
3011     setBody: function(str) {
3012         this.bodyEl.dom.innerHTML = str;
3013     },
3014     /**
3015      * Set the body of the Dialog using the template
3016      * @param {Obj} data - apply this data to the template and replace the body contents.
3017      */
3018     applyBody: function(obj)
3019     {
3020         if (!this.tmpl) {
3021             Roo.log("Error - using apply Body without a template");
3022             //code
3023         }
3024         this.tmpl.overwrite(this.bodyEl, obj);
3025     },
3026     
3027     getChildHeight : function(child_nodes)
3028     {
3029         if(
3030             !child_nodes ||
3031             child_nodes.length == 0
3032         ) {
3033             return;
3034         }
3035         
3036         var child_height = 0;
3037         
3038         for(var i = 0; i < child_nodes.length; i++) {
3039             
3040             // for modal with tabs...
3041             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3042                 
3043                 var layout_childs = child_nodes[i].childNodes;
3044                 
3045                 for(var j = 0; j < layout_childs.length; j++) {
3046                     
3047                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3048                         
3049                         var layout_body_childs = layout_childs[j].childNodes;
3050                         
3051                         for(var k = 0; k < layout_body_childs.length; k++) {
3052                             
3053                             if(layout_body_childs[k].classList.contains('navbar')) {
3054                                 child_height += layout_body_childs[k].offsetHeight;
3055                                 // Roo.log('nav height: '+ layout_body_childs[k].offsetHeight);
3056                                 continue;
3057                             }
3058                             
3059                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3060                                 
3061                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3062                                 
3063                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3064                                     
3065                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3066                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3067                                         // Roo.log('active panel height: '+this.getChildHeight(layout_body_tab_childs[m].childNodes));
3068                                         continue;
3069                                     }
3070                                     
3071                                 }
3072                                 
3073                             }
3074                             
3075                         }
3076                     }
3077                 }
3078                 continue;
3079             }
3080             
3081             child_height += child_nodes[i].offsetHeight;
3082         }
3083         
3084         return child_height;
3085     }
3086
3087 });
3088
3089
3090 Roo.apply(Roo.bootstrap.Modal,  {
3091     /**
3092          * Button config that displays a single OK button
3093          * @type Object
3094          */
3095         OK :  [{
3096             name : 'ok',
3097             weight : 'primary',
3098             html : 'OK'
3099         }],
3100         /**
3101          * Button config that displays Yes and No buttons
3102          * @type Object
3103          */
3104         YESNO : [
3105             {
3106                 name  : 'no',
3107                 html : 'No'
3108             },
3109             {
3110                 name  :'yes',
3111                 weight : 'primary',
3112                 html : 'Yes'
3113             }
3114         ],
3115
3116         /**
3117          * Button config that displays OK and Cancel buttons
3118          * @type Object
3119          */
3120         OKCANCEL : [
3121             {
3122                name : 'cancel',
3123                 html : 'Cancel'
3124             },
3125             {
3126                 name : 'ok',
3127                 weight : 'primary',
3128                 html : 'OK'
3129             }
3130         ],
3131         /**
3132          * Button config that displays Yes, No and Cancel buttons
3133          * @type Object
3134          */
3135         YESNOCANCEL : [
3136             {
3137                 name : 'yes',
3138                 weight : 'primary',
3139                 html : 'Yes'
3140             },
3141             {
3142                 name : 'no',
3143                 html : 'No'
3144             },
3145             {
3146                 name : 'cancel',
3147                 html : 'Cancel'
3148             }
3149         ],
3150         
3151         zIndex : 10001
3152 });
3153 /*
3154  * - LGPL
3155  *
3156  * messagebox - can be used as a replace
3157  * 
3158  */
3159 /**
3160  * @class Roo.MessageBox
3161  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3162  * Example usage:
3163  *<pre><code>
3164 // Basic alert:
3165 Roo.Msg.alert('Status', 'Changes saved successfully.');
3166
3167 // Prompt for user data:
3168 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3169     if (btn == 'ok'){
3170         // process text value...
3171     }
3172 });
3173
3174 // Show a dialog using config options:
3175 Roo.Msg.show({
3176    title:'Save Changes?',
3177    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3178    buttons: Roo.Msg.YESNOCANCEL,
3179    fn: processResult,
3180    animEl: 'elId'
3181 });
3182 </code></pre>
3183  * @singleton
3184  */
3185 Roo.bootstrap.MessageBox = function(){
3186     var dlg, opt, mask, waitTimer;
3187     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3188     var buttons, activeTextEl, bwidth;
3189
3190     
3191     // private
3192     var handleButton = function(button){
3193         dlg.hide();
3194         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3195     };
3196
3197     // private
3198     var handleHide = function(){
3199         if(opt && opt.cls){
3200             dlg.el.removeClass(opt.cls);
3201         }
3202         //if(waitTimer){
3203         //    Roo.TaskMgr.stop(waitTimer);
3204         //    waitTimer = null;
3205         //}
3206     };
3207
3208     // private
3209     var updateButtons = function(b){
3210         var width = 0;
3211         if(!b){
3212             buttons["ok"].hide();
3213             buttons["cancel"].hide();
3214             buttons["yes"].hide();
3215             buttons["no"].hide();
3216             //dlg.footer.dom.style.display = 'none';
3217             return width;
3218         }
3219         dlg.footerEl.dom.style.display = '';
3220         for(var k in buttons){
3221             if(typeof buttons[k] != "function"){
3222                 if(b[k]){
3223                     buttons[k].show();
3224                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3225                     width += buttons[k].el.getWidth()+15;
3226                 }else{
3227                     buttons[k].hide();
3228                 }
3229             }
3230         }
3231         return width;
3232     };
3233
3234     // private
3235     var handleEsc = function(d, k, e){
3236         if(opt && opt.closable !== false){
3237             dlg.hide();
3238         }
3239         if(e){
3240             e.stopEvent();
3241         }
3242     };
3243
3244     return {
3245         /**
3246          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3247          * @return {Roo.BasicDialog} The BasicDialog element
3248          */
3249         getDialog : function(){
3250            if(!dlg){
3251                 dlg = new Roo.bootstrap.Modal( {
3252                     //draggable: true,
3253                     //resizable:false,
3254                     //constraintoviewport:false,
3255                     //fixedcenter:true,
3256                     //collapsible : false,
3257                     //shim:true,
3258                     //modal: true,
3259                 //    width: 'auto',
3260                   //  height:100,
3261                     //buttonAlign:"center",
3262                     closeClick : function(){
3263                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3264                             handleButton("no");
3265                         }else{
3266                             handleButton("cancel");
3267                         }
3268                     }
3269                 });
3270                 dlg.render();
3271                 dlg.on("hide", handleHide);
3272                 mask = dlg.mask;
3273                 //dlg.addKeyListener(27, handleEsc);
3274                 buttons = {};
3275                 this.buttons = buttons;
3276                 var bt = this.buttonText;
3277                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3278                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3279                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3280                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3281                 //Roo.log(buttons);
3282                 bodyEl = dlg.bodyEl.createChild({
3283
3284                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3285                         '<textarea class="roo-mb-textarea"></textarea>' +
3286                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3287                 });
3288                 msgEl = bodyEl.dom.firstChild;
3289                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3290                 textboxEl.enableDisplayMode();
3291                 textboxEl.addKeyListener([10,13], function(){
3292                     if(dlg.isVisible() && opt && opt.buttons){
3293                         if(opt.buttons.ok){
3294                             handleButton("ok");
3295                         }else if(opt.buttons.yes){
3296                             handleButton("yes");
3297                         }
3298                     }
3299                 });
3300                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3301                 textareaEl.enableDisplayMode();
3302                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3303                 progressEl.enableDisplayMode();
3304                 
3305                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3306                 var pf = progressEl.dom.firstChild;
3307                 if (pf) {
3308                     pp = Roo.get(pf.firstChild);
3309                     pp.setHeight(pf.offsetHeight);
3310                 }
3311                 
3312             }
3313             return dlg;
3314         },
3315
3316         /**
3317          * Updates the message box body text
3318          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3319          * the XHTML-compliant non-breaking space character '&amp;#160;')
3320          * @return {Roo.MessageBox} This message box
3321          */
3322         updateText : function(text)
3323         {
3324             if(!dlg.isVisible() && !opt.width){
3325                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3326                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3327             }
3328             msgEl.innerHTML = text || '&#160;';
3329       
3330             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3331             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3332             var w = Math.max(
3333                     Math.min(opt.width || cw , this.maxWidth), 
3334                     Math.max(opt.minWidth || this.minWidth, bwidth)
3335             );
3336             if(opt.prompt){
3337                 activeTextEl.setWidth(w);
3338             }
3339             if(dlg.isVisible()){
3340                 dlg.fixedcenter = false;
3341             }
3342             // to big, make it scroll. = But as usual stupid IE does not support
3343             // !important..
3344             
3345             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3346                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3347                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3348             } else {
3349                 bodyEl.dom.style.height = '';
3350                 bodyEl.dom.style.overflowY = '';
3351             }
3352             if (cw > w) {
3353                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3354             } else {
3355                 bodyEl.dom.style.overflowX = '';
3356             }
3357             
3358             dlg.setContentSize(w, bodyEl.getHeight());
3359             if(dlg.isVisible()){
3360                 dlg.fixedcenter = true;
3361             }
3362             return this;
3363         },
3364
3365         /**
3366          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3367          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3368          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3369          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3370          * @return {Roo.MessageBox} This message box
3371          */
3372         updateProgress : function(value, text){
3373             if(text){
3374                 this.updateText(text);
3375             }
3376             
3377             if (pp) { // weird bug on my firefox - for some reason this is not defined
3378                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3379                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3380             }
3381             return this;
3382         },        
3383
3384         /**
3385          * Returns true if the message box is currently displayed
3386          * @return {Boolean} True if the message box is visible, else false
3387          */
3388         isVisible : function(){
3389             return dlg && dlg.isVisible();  
3390         },
3391
3392         /**
3393          * Hides the message box if it is displayed
3394          */
3395         hide : function(){
3396             if(this.isVisible()){
3397                 dlg.hide();
3398             }  
3399         },
3400
3401         /**
3402          * Displays a new message box, or reinitializes an existing message box, based on the config options
3403          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3404          * The following config object properties are supported:
3405          * <pre>
3406 Property    Type             Description
3407 ----------  ---------------  ------------------------------------------------------------------------------------
3408 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3409                                    closes (defaults to undefined)
3410 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3411                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3412 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3413                                    progress and wait dialogs will ignore this property and always hide the
3414                                    close button as they can only be closed programmatically.
3415 cls               String           A custom CSS class to apply to the message box element
3416 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3417                                    displayed (defaults to 75)
3418 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3419                                    function will be btn (the name of the button that was clicked, if applicable,
3420                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3421                                    Progress and wait dialogs will ignore this option since they do not respond to
3422                                    user actions and can only be closed programmatically, so any required function
3423                                    should be called by the same code after it closes the dialog.
3424 icon              String           A CSS class that provides a background image to be used as an icon for
3425                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3426 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3427 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3428 modal             Boolean          False to allow user interaction with the page while the message box is
3429                                    displayed (defaults to true)
3430 msg               String           A string that will replace the existing message box body text (defaults
3431                                    to the XHTML-compliant non-breaking space character '&#160;')
3432 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3433 progress          Boolean          True to display a progress bar (defaults to false)
3434 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3435 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3436 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3437 title             String           The title text
3438 value             String           The string value to set into the active textbox element if displayed
3439 wait              Boolean          True to display a progress bar (defaults to false)
3440 width             Number           The width of the dialog in pixels
3441 </pre>
3442          *
3443          * Example usage:
3444          * <pre><code>
3445 Roo.Msg.show({
3446    title: 'Address',
3447    msg: 'Please enter your address:',
3448    width: 300,
3449    buttons: Roo.MessageBox.OKCANCEL,
3450    multiline: true,
3451    fn: saveAddress,
3452    animEl: 'addAddressBtn'
3453 });
3454 </code></pre>
3455          * @param {Object} config Configuration options
3456          * @return {Roo.MessageBox} This message box
3457          */
3458         show : function(options)
3459         {
3460             
3461             // this causes nightmares if you show one dialog after another
3462             // especially on callbacks..
3463              
3464             if(this.isVisible()){
3465                 
3466                 this.hide();
3467                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3468                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3469                 Roo.log("New Dialog Message:" +  options.msg )
3470                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3471                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3472                 
3473             }
3474             var d = this.getDialog();
3475             opt = options;
3476             d.setTitle(opt.title || "&#160;");
3477             d.closeEl.setDisplayed(opt.closable !== false);
3478             activeTextEl = textboxEl;
3479             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3480             if(opt.prompt){
3481                 if(opt.multiline){
3482                     textboxEl.hide();
3483                     textareaEl.show();
3484                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3485                         opt.multiline : this.defaultTextHeight);
3486                     activeTextEl = textareaEl;
3487                 }else{
3488                     textboxEl.show();
3489                     textareaEl.hide();
3490                 }
3491             }else{
3492                 textboxEl.hide();
3493                 textareaEl.hide();
3494             }
3495             progressEl.setDisplayed(opt.progress === true);
3496             this.updateProgress(0);
3497             activeTextEl.dom.value = opt.value || "";
3498             if(opt.prompt){
3499                 dlg.setDefaultButton(activeTextEl);
3500             }else{
3501                 var bs = opt.buttons;
3502                 var db = null;
3503                 if(bs && bs.ok){
3504                     db = buttons["ok"];
3505                 }else if(bs && bs.yes){
3506                     db = buttons["yes"];
3507                 }
3508                 dlg.setDefaultButton(db);
3509             }
3510             bwidth = updateButtons(opt.buttons);
3511             this.updateText(opt.msg);
3512             if(opt.cls){
3513                 d.el.addClass(opt.cls);
3514             }
3515             d.proxyDrag = opt.proxyDrag === true;
3516             d.modal = opt.modal !== false;
3517             d.mask = opt.modal !== false ? mask : false;
3518             if(!d.isVisible()){
3519                 // force it to the end of the z-index stack so it gets a cursor in FF
3520                 document.body.appendChild(dlg.el.dom);
3521                 d.animateTarget = null;
3522                 d.show(options.animEl);
3523             }
3524             return this;
3525         },
3526
3527         /**
3528          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3529          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3530          * and closing the message box when the process is complete.
3531          * @param {String} title The title bar text
3532          * @param {String} msg The message box body text
3533          * @return {Roo.MessageBox} This message box
3534          */
3535         progress : function(title, msg){
3536             this.show({
3537                 title : title,
3538                 msg : msg,
3539                 buttons: false,
3540                 progress:true,
3541                 closable:false,
3542                 minWidth: this.minProgressWidth,
3543                 modal : true
3544             });
3545             return this;
3546         },
3547
3548         /**
3549          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3550          * If a callback function is passed it will be called after the user clicks the button, and the
3551          * id of the button that was clicked will be passed as the only parameter to the callback
3552          * (could also be the top-right close button).
3553          * @param {String} title The title bar text
3554          * @param {String} msg The message box body text
3555          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3556          * @param {Object} scope (optional) The scope of the callback function
3557          * @return {Roo.MessageBox} This message box
3558          */
3559         alert : function(title, msg, fn, scope)
3560         {
3561             this.show({
3562                 title : title,
3563                 msg : msg,
3564                 buttons: this.OK,
3565                 fn: fn,
3566                 closable : false,
3567                 scope : scope,
3568                 modal : true
3569             });
3570             return this;
3571         },
3572
3573         /**
3574          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3575          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3576          * You are responsible for closing the message box when the process is complete.
3577          * @param {String} msg The message box body text
3578          * @param {String} title (optional) The title bar text
3579          * @return {Roo.MessageBox} This message box
3580          */
3581         wait : function(msg, title){
3582             this.show({
3583                 title : title,
3584                 msg : msg,
3585                 buttons: false,
3586                 closable:false,
3587                 progress:true,
3588                 modal:true,
3589                 width:300,
3590                 wait:true
3591             });
3592             waitTimer = Roo.TaskMgr.start({
3593                 run: function(i){
3594                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3595                 },
3596                 interval: 1000
3597             });
3598             return this;
3599         },
3600
3601         /**
3602          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3603          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3604          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3605          * @param {String} title The title bar text
3606          * @param {String} msg The message box body text
3607          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3608          * @param {Object} scope (optional) The scope of the callback function
3609          * @return {Roo.MessageBox} This message box
3610          */
3611         confirm : function(title, msg, fn, scope){
3612             this.show({
3613                 title : title,
3614                 msg : msg,
3615                 buttons: this.YESNO,
3616                 fn: fn,
3617                 scope : scope,
3618                 modal : true
3619             });
3620             return this;
3621         },
3622
3623         /**
3624          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3625          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3626          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3627          * (could also be the top-right close button) and the text that was entered will be passed as the two
3628          * parameters to the callback.
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3632          * @param {Object} scope (optional) The scope of the callback function
3633          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3634          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3635          * @return {Roo.MessageBox} This message box
3636          */
3637         prompt : function(title, msg, fn, scope, multiline){
3638             this.show({
3639                 title : title,
3640                 msg : msg,
3641                 buttons: this.OKCANCEL,
3642                 fn: fn,
3643                 minWidth:250,
3644                 scope : scope,
3645                 prompt:true,
3646                 multiline: multiline,
3647                 modal : true
3648             });
3649             return this;
3650         },
3651
3652         /**
3653          * Button config that displays a single OK button
3654          * @type Object
3655          */
3656         OK : {ok:true},
3657         /**
3658          * Button config that displays Yes and No buttons
3659          * @type Object
3660          */
3661         YESNO : {yes:true, no:true},
3662         /**
3663          * Button config that displays OK and Cancel buttons
3664          * @type Object
3665          */
3666         OKCANCEL : {ok:true, cancel:true},
3667         /**
3668          * Button config that displays Yes, No and Cancel buttons
3669          * @type Object
3670          */
3671         YESNOCANCEL : {yes:true, no:true, cancel:true},
3672
3673         /**
3674          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3675          * @type Number
3676          */
3677         defaultTextHeight : 75,
3678         /**
3679          * The maximum width in pixels of the message box (defaults to 600)
3680          * @type Number
3681          */
3682         maxWidth : 600,
3683         /**
3684          * The minimum width in pixels of the message box (defaults to 100)
3685          * @type Number
3686          */
3687         minWidth : 100,
3688         /**
3689          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3690          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3691          * @type Number
3692          */
3693         minProgressWidth : 250,
3694         /**
3695          * An object containing the default button text strings that can be overriden for localized language support.
3696          * Supported properties are: ok, cancel, yes and no.
3697          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3698          * @type Object
3699          */
3700         buttonText : {
3701             ok : "OK",
3702             cancel : "Cancel",
3703             yes : "Yes",
3704             no : "No"
3705         }
3706     };
3707 }();
3708
3709 /**
3710  * Shorthand for {@link Roo.MessageBox}
3711  */
3712 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3713 Roo.Msg = Roo.Msg || Roo.MessageBox;
3714 /*
3715  * - LGPL
3716  *
3717  * navbar
3718  * 
3719  */
3720
3721 /**
3722  * @class Roo.bootstrap.Navbar
3723  * @extends Roo.bootstrap.Component
3724  * Bootstrap Navbar class
3725
3726  * @constructor
3727  * Create a new Navbar
3728  * @param {Object} config The config object
3729  */
3730
3731
3732 Roo.bootstrap.Navbar = function(config){
3733     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3734     this.addEvents({
3735         // raw events
3736         /**
3737          * @event beforetoggle
3738          * Fire before toggle the menu
3739          * @param {Roo.EventObject} e
3740          */
3741         "beforetoggle" : true
3742     });
3743 };
3744
3745 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3746     
3747     
3748    
3749     // private
3750     navItems : false,
3751     loadMask : false,
3752     
3753     
3754     getAutoCreate : function(){
3755         
3756         
3757         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3758         
3759     },
3760     
3761     initEvents :function ()
3762     {
3763         //Roo.log(this.el.select('.navbar-toggle',true));
3764         this.el.select('.navbar-toggle',true).on('click', function() {
3765             if(this.fireEvent('beforetoggle', this) !== false){
3766                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3767             }
3768             
3769         }, this);
3770         
3771         var mark = {
3772             tag: "div",
3773             cls:"x-dlg-mask"
3774         };
3775         
3776         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3777         
3778         var size = this.el.getSize();
3779         this.maskEl.setSize(size.width, size.height);
3780         this.maskEl.enableDisplayMode("block");
3781         this.maskEl.hide();
3782         
3783         if(this.loadMask){
3784             this.maskEl.show();
3785         }
3786     },
3787     
3788     
3789     getChildContainer : function()
3790     {
3791         if (this.el.select('.collapse').getCount()) {
3792             return this.el.select('.collapse',true).first();
3793         }
3794         
3795         return this.el;
3796     },
3797     
3798     mask : function()
3799     {
3800         this.maskEl.show();
3801     },
3802     
3803     unmask : function()
3804     {
3805         this.maskEl.hide();
3806     } 
3807     
3808     
3809     
3810     
3811 });
3812
3813
3814
3815  
3816
3817  /*
3818  * - LGPL
3819  *
3820  * navbar
3821  * 
3822  */
3823
3824 /**
3825  * @class Roo.bootstrap.NavSimplebar
3826  * @extends Roo.bootstrap.Navbar
3827  * Bootstrap Sidebar class
3828  *
3829  * @cfg {Boolean} inverse is inverted color
3830  * 
3831  * @cfg {String} type (nav | pills | tabs)
3832  * @cfg {Boolean} arrangement stacked | justified
3833  * @cfg {String} align (left | right) alignment
3834  * 
3835  * @cfg {Boolean} main (true|false) main nav bar? default false
3836  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3837  * 
3838  * @cfg {String} tag (header|footer|nav|div) default is nav 
3839
3840  * 
3841  * 
3842  * 
3843  * @constructor
3844  * Create a new Sidebar
3845  * @param {Object} config The config object
3846  */
3847
3848
3849 Roo.bootstrap.NavSimplebar = function(config){
3850     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3851 };
3852
3853 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3854     
3855     inverse: false,
3856     
3857     type: false,
3858     arrangement: '',
3859     align : false,
3860     
3861     
3862     
3863     main : false,
3864     
3865     
3866     tag : false,
3867     
3868     
3869     getAutoCreate : function(){
3870         
3871         
3872         var cfg = {
3873             tag : this.tag || 'div',
3874             cls : 'navbar'
3875         };
3876           
3877         
3878         cfg.cn = [
3879             {
3880                 cls: 'nav',
3881                 tag : 'ul'
3882             }
3883         ];
3884         
3885          
3886         this.type = this.type || 'nav';
3887         if (['tabs','pills'].indexOf(this.type)!==-1) {
3888             cfg.cn[0].cls += ' nav-' + this.type
3889         
3890         
3891         } else {
3892             if (this.type!=='nav') {
3893                 Roo.log('nav type must be nav/tabs/pills')
3894             }
3895             cfg.cn[0].cls += ' navbar-nav'
3896         }
3897         
3898         
3899         
3900         
3901         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3902             cfg.cn[0].cls += ' nav-' + this.arrangement;
3903         }
3904         
3905         
3906         if (this.align === 'right') {
3907             cfg.cn[0].cls += ' navbar-right';
3908         }
3909         
3910         if (this.inverse) {
3911             cfg.cls += ' navbar-inverse';
3912             
3913         }
3914         
3915         
3916         return cfg;
3917     
3918         
3919     }
3920     
3921     
3922     
3923 });
3924
3925
3926
3927  
3928
3929  
3930        /*
3931  * - LGPL
3932  *
3933  * navbar
3934  * 
3935  */
3936
3937 /**
3938  * @class Roo.bootstrap.NavHeaderbar
3939  * @extends Roo.bootstrap.NavSimplebar
3940  * Bootstrap Sidebar class
3941  *
3942  * @cfg {String} brand what is brand
3943  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3944  * @cfg {String} brand_href href of the brand
3945  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3946  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3947  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3948  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3949  * 
3950  * @constructor
3951  * Create a new Sidebar
3952  * @param {Object} config The config object
3953  */
3954
3955
3956 Roo.bootstrap.NavHeaderbar = function(config){
3957     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3958       
3959 };
3960
3961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3962     
3963     position: '',
3964     brand: '',
3965     brand_href: false,
3966     srButton : true,
3967     autohide : false,
3968     desktopCenter : false,
3969    
3970     
3971     getAutoCreate : function(){
3972         
3973         var   cfg = {
3974             tag: this.nav || 'nav',
3975             cls: 'navbar',
3976             role: 'navigation',
3977             cn: []
3978         };
3979         
3980         var cn = cfg.cn;
3981         if (this.desktopCenter) {
3982             cn.push({cls : 'container', cn : []});
3983             cn = cn[0].cn;
3984         }
3985         
3986         if(this.srButton){
3987             cn.push({
3988                 tag: 'div',
3989                 cls: 'navbar-header',
3990                 cn: [
3991                     {
3992                         tag: 'button',
3993                         type: 'button',
3994                         cls: 'navbar-toggle',
3995                         'data-toggle': 'collapse',
3996                         cn: [
3997                             {
3998                                 tag: 'span',
3999                                 cls: 'sr-only',
4000                                 html: 'Toggle navigation'
4001                             },
4002                             {
4003                                 tag: 'span',
4004                                 cls: 'icon-bar'
4005                             },
4006                             {
4007                                 tag: 'span',
4008                                 cls: 'icon-bar'
4009                             },
4010                             {
4011                                 tag: 'span',
4012                                 cls: 'icon-bar'
4013                             }
4014                         ]
4015                     }
4016                 ]
4017             });
4018         }
4019         
4020         cn.push({
4021             tag: 'div',
4022             cls: 'collapse navbar-collapse',
4023             cn : []
4024         });
4025         
4026         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4027         
4028         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4029             cfg.cls += ' navbar-' + this.position;
4030             
4031             // tag can override this..
4032             
4033             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4034         }
4035         
4036         if (this.brand !== '') {
4037             cn[0].cn.push({
4038                 tag: 'a',
4039                 href: this.brand_href ? this.brand_href : '#',
4040                 cls: 'navbar-brand',
4041                 cn: [
4042                 this.brand
4043                 ]
4044             });
4045         }
4046         
4047         if(this.main){
4048             cfg.cls += ' main-nav';
4049         }
4050         
4051         
4052         return cfg;
4053
4054         
4055     },
4056     getHeaderChildContainer : function()
4057     {
4058         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4059             return this.el.select('.navbar-header',true).first();
4060         }
4061         
4062         return this.getChildContainer();
4063     },
4064     
4065     
4066     initEvents : function()
4067     {
4068         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069         
4070         if (this.autohide) {
4071             
4072             var prevScroll = 0;
4073             var ft = this.el;
4074             
4075             Roo.get(document).on('scroll',function(e) {
4076                 var ns = Roo.get(document).getScroll().top;
4077                 var os = prevScroll;
4078                 prevScroll = ns;
4079                 
4080                 if(ns > os){
4081                     ft.removeClass('slideDown');
4082                     ft.addClass('slideUp');
4083                     return;
4084                 }
4085                 ft.removeClass('slideUp');
4086                 ft.addClass('slideDown');
4087                  
4088               
4089           },this);
4090         }
4091     }    
4092     
4093 });
4094
4095
4096
4097  
4098
4099  /*
4100  * - LGPL
4101  *
4102  * navbar
4103  * 
4104  */
4105
4106 /**
4107  * @class Roo.bootstrap.NavSidebar
4108  * @extends Roo.bootstrap.Navbar
4109  * Bootstrap Sidebar class
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavSidebar = function(config){
4118     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4119 };
4120
4121 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4122     
4123     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124     
4125     getAutoCreate : function(){
4126         
4127         
4128         return  {
4129             tag: 'div',
4130             cls: 'sidebar sidebar-nav'
4131         };
4132     
4133         
4134     }
4135     
4136     
4137     
4138 });
4139
4140
4141
4142  
4143
4144  /*
4145  * - LGPL
4146  *
4147  * nav group
4148  * 
4149  */
4150
4151 /**
4152  * @class Roo.bootstrap.NavGroup
4153  * @extends Roo.bootstrap.Component
4154  * Bootstrap NavGroup class
4155  * @cfg {String} align (left|right)
4156  * @cfg {Boolean} inverse
4157  * @cfg {String} type (nav|pills|tab) default nav
4158  * @cfg {String} navId - reference Id for navbar.
4159
4160  * 
4161  * @constructor
4162  * Create a new nav group
4163  * @param {Object} config The config object
4164  */
4165
4166 Roo.bootstrap.NavGroup = function(config){
4167     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4168     this.navItems = [];
4169    
4170     Roo.bootstrap.NavGroup.register(this);
4171      this.addEvents({
4172         /**
4173              * @event changed
4174              * Fires when the active item changes
4175              * @param {Roo.bootstrap.NavGroup} this
4176              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4177              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4178          */
4179         'changed': true
4180      });
4181     
4182 };
4183
4184 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4185     
4186     align: '',
4187     inverse: false,
4188     form: false,
4189     type: 'nav',
4190     navId : '',
4191     // private
4192     
4193     navItems : false, 
4194     
4195     getAutoCreate : function()
4196     {
4197         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4198         
4199         cfg = {
4200             tag : 'ul',
4201             cls: 'nav' 
4202         };
4203         
4204         if (['tabs','pills'].indexOf(this.type)!==-1) {
4205             cfg.cls += ' nav-' + this.type
4206         } else {
4207             if (this.type!=='nav') {
4208                 Roo.log('nav type must be nav/tabs/pills')
4209             }
4210             cfg.cls += ' navbar-nav'
4211         }
4212         
4213         if (this.parent() && this.parent().sidebar) {
4214             cfg = {
4215                 tag: 'ul',
4216                 cls: 'dashboard-menu sidebar-menu'
4217             };
4218             
4219             return cfg;
4220         }
4221         
4222         if (this.form === true) {
4223             cfg = {
4224                 tag: 'form',
4225                 cls: 'navbar-form'
4226             };
4227             
4228             if (this.align === 'right') {
4229                 cfg.cls += ' navbar-right';
4230             } else {
4231                 cfg.cls += ' navbar-left';
4232             }
4233         }
4234         
4235         if (this.align === 'right') {
4236             cfg.cls += ' navbar-right';
4237         }
4238         
4239         if (this.inverse) {
4240             cfg.cls += ' navbar-inverse';
4241             
4242         }
4243         
4244         
4245         return cfg;
4246     },
4247     /**
4248     * sets the active Navigation item
4249     * @param {Roo.bootstrap.NavItem} the new current navitem
4250     */
4251     setActiveItem : function(item)
4252     {
4253         var prev = false;
4254         Roo.each(this.navItems, function(v){
4255             if (v == item) {
4256                 return ;
4257             }
4258             if (v.isActive()) {
4259                 v.setActive(false, true);
4260                 prev = v;
4261                 
4262             }
4263             
4264         });
4265
4266         item.setActive(true, true);
4267         this.fireEvent('changed', this, item, prev);
4268         
4269         
4270     },
4271     /**
4272     * gets the active Navigation item
4273     * @return {Roo.bootstrap.NavItem} the current navitem
4274     */
4275     getActive : function()
4276     {
4277         
4278         var prev = false;
4279         Roo.each(this.navItems, function(v){
4280             
4281             if (v.isActive()) {
4282                 prev = v;
4283                 
4284             }
4285             
4286         });
4287         return prev;
4288     },
4289     
4290     indexOfNav : function()
4291     {
4292         
4293         var prev = false;
4294         Roo.each(this.navItems, function(v,i){
4295             
4296             if (v.isActive()) {
4297                 prev = i;
4298                 
4299             }
4300             
4301         });
4302         return prev;
4303     },
4304     /**
4305     * adds a Navigation item
4306     * @param {Roo.bootstrap.NavItem} the navitem to add
4307     */
4308     addItem : function(cfg)
4309     {
4310         var cn = new Roo.bootstrap.NavItem(cfg);
4311         this.register(cn);
4312         cn.parentId = this.id;
4313         cn.onRender(this.el, null);
4314         return cn;
4315     },
4316     /**
4317     * register a Navigation item
4318     * @param {Roo.bootstrap.NavItem} the navitem to add
4319     */
4320     register : function(item)
4321     {
4322         this.navItems.push( item);
4323         item.navId = this.navId;
4324     
4325     },
4326     
4327     /**
4328     * clear all the Navigation item
4329     */
4330    
4331     clearAll : function()
4332     {
4333         this.navItems = [];
4334         this.el.dom.innerHTML = '';
4335     },
4336     
4337     getNavItem: function(tabId)
4338     {
4339         var ret = false;
4340         Roo.each(this.navItems, function(e) {
4341             if (e.tabId == tabId) {
4342                ret =  e;
4343                return false;
4344             }
4345             return true;
4346             
4347         });
4348         return ret;
4349     },
4350     
4351     setActiveNext : function()
4352     {
4353         var i = this.indexOfNav(this.getActive());
4354         if (i > this.navItems.length) {
4355             return;
4356         }
4357         this.setActiveItem(this.navItems[i+1]);
4358     },
4359     setActivePrev : function()
4360     {
4361         var i = this.indexOfNav(this.getActive());
4362         if (i  < 1) {
4363             return;
4364         }
4365         this.setActiveItem(this.navItems[i-1]);
4366     },
4367     clearWasActive : function(except) {
4368         Roo.each(this.navItems, function(e) {
4369             if (e.tabId != except.tabId && e.was_active) {
4370                e.was_active = false;
4371                return false;
4372             }
4373             return true;
4374             
4375         });
4376     },
4377     getWasActive : function ()
4378     {
4379         var r = false;
4380         Roo.each(this.navItems, function(e) {
4381             if (e.was_active) {
4382                r = e;
4383                return false;
4384             }
4385             return true;
4386             
4387         });
4388         return r;
4389     }
4390     
4391     
4392 });
4393
4394  
4395 Roo.apply(Roo.bootstrap.NavGroup, {
4396     
4397     groups: {},
4398      /**
4399     * register a Navigation Group
4400     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401     */
4402     register : function(navgrp)
4403     {
4404         this.groups[navgrp.navId] = navgrp;
4405         
4406     },
4407     /**
4408     * fetch a Navigation Group based on the navigation ID
4409     * @param {string} the navgroup to add
4410     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4411     */
4412     get: function(navId) {
4413         if (typeof(this.groups[navId]) == 'undefined') {
4414             return false;
4415             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416         }
4417         return this.groups[navId] ;
4418     }
4419     
4420     
4421     
4422 });
4423
4424  /*
4425  * - LGPL
4426  *
4427  * row
4428  * 
4429  */
4430
4431 /**
4432  * @class Roo.bootstrap.NavItem
4433  * @extends Roo.bootstrap.Component
4434  * Bootstrap Navbar.NavItem class
4435  * @cfg {String} href  link to
4436  * @cfg {String} html content of button
4437  * @cfg {String} badge text inside badge
4438  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4439  * @cfg {String} glyphicon name of glyphicon
4440  * @cfg {String} icon name of font awesome icon
4441  * @cfg {Boolean} active Is item active
4442  * @cfg {Boolean} disabled Is item disabled
4443  
4444  * @cfg {Boolean} preventDefault (true | false) default false
4445  * @cfg {String} tabId the tab that this item activates.
4446  * @cfg {String} tagtype (a|span) render as a href or span?
4447  * @cfg {Boolean} animateRef (true|false) link to element default false  
4448   
4449  * @constructor
4450  * Create a new Navbar Item
4451  * @param {Object} config The config object
4452  */
4453 Roo.bootstrap.NavItem = function(config){
4454     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4455     this.addEvents({
4456         // raw events
4457         /**
4458          * @event click
4459          * The raw click event for the entire grid.
4460          * @param {Roo.EventObject} e
4461          */
4462         "click" : true,
4463          /**
4464             * @event changed
4465             * Fires when the active item active state changes
4466             * @param {Roo.bootstrap.NavItem} this
4467             * @param {boolean} state the new state
4468              
4469          */
4470         'changed': true,
4471         /**
4472             * @event scrollto
4473             * Fires when scroll to element
4474             * @param {Roo.bootstrap.NavItem} this
4475             * @param {Object} options
4476             * @param {Roo.EventObject} e
4477              
4478          */
4479         'scrollto': true
4480     });
4481    
4482 };
4483
4484 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4485     
4486     href: false,
4487     html: '',
4488     badge: '',
4489     icon: false,
4490     glyphicon: false,
4491     active: false,
4492     preventDefault : false,
4493     tabId : false,
4494     tagtype : 'a',
4495     disabled : false,
4496     animateRef : false,
4497     was_active : false,
4498     
4499     getAutoCreate : function(){
4500          
4501         var cfg = {
4502             tag: 'li',
4503             cls: 'nav-item'
4504             
4505         };
4506         
4507         if (this.active) {
4508             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509         }
4510         if (this.disabled) {
4511             cfg.cls += ' disabled';
4512         }
4513         
4514         if (this.href || this.html || this.glyphicon || this.icon) {
4515             cfg.cn = [
4516                 {
4517                     tag: this.tagtype,
4518                     href : this.href || "#",
4519                     html: this.html || ''
4520                 }
4521             ];
4522             
4523             if (this.icon) {
4524                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4525             }
4526
4527             if(this.glyphicon) {
4528                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4529             }
4530             
4531             if (this.menu) {
4532                 
4533                 cfg.cn[0].html += " <span class='caret'></span>";
4534              
4535             }
4536             
4537             if (this.badge !== '') {
4538                  
4539                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4540             }
4541         }
4542         
4543         
4544         
4545         return cfg;
4546     },
4547     initEvents: function() 
4548     {
4549         if (typeof (this.menu) != 'undefined') {
4550             this.menu.parentType = this.xtype;
4551             this.menu.triggerEl = this.el;
4552             this.menu = this.addxtype(Roo.apply({}, this.menu));
4553         }
4554         
4555         this.el.select('a',true).on('click', this.onClick, this);
4556         
4557         if(this.tagtype == 'span'){
4558             this.el.select('span',true).on('click', this.onClick, this);
4559         }
4560        
4561         // at this point parent should be available..
4562         this.parent().register(this);
4563     },
4564     
4565     onClick : function(e)
4566     {
4567         if (e.getTarget('.dropdown-menu-item')) {
4568             // did you click on a menu itemm.... - then don't trigger onclick..
4569             return;
4570         }
4571         
4572         if(
4573                 this.preventDefault || 
4574                 this.href == '#' 
4575         ){
4576             Roo.log("NavItem - prevent Default?");
4577             e.preventDefault();
4578         }
4579         
4580         if (this.disabled) {
4581             return;
4582         }
4583         
4584         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4585         if (tg && tg.transition) {
4586             Roo.log("waiting for the transitionend");
4587             return;
4588         }
4589         
4590         
4591         
4592         //Roo.log("fire event clicked");
4593         if(this.fireEvent('click', this, e) === false){
4594             return;
4595         };
4596         
4597         if(this.tagtype == 'span'){
4598             return;
4599         }
4600         
4601         //Roo.log(this.href);
4602         var ael = this.el.select('a',true).first();
4603         //Roo.log(ael);
4604         
4605         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4606             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4607             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4608                 return; // ignore... - it's a 'hash' to another page.
4609             }
4610             Roo.log("NavItem - prevent Default?");
4611             e.preventDefault();
4612             this.scrollToElement(e);
4613         }
4614         
4615         
4616         var p =  this.parent();
4617    
4618         if (['tabs','pills'].indexOf(p.type)!==-1) {
4619             if (typeof(p.setActiveItem) !== 'undefined') {
4620                 p.setActiveItem(this);
4621             }
4622         }
4623         
4624         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4625         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4626             // remove the collapsed menu expand...
4627             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4628         }
4629     },
4630     
4631     isActive: function () {
4632         return this.active
4633     },
4634     setActive : function(state, fire, is_was_active)
4635     {
4636         if (this.active && !state && this.navId) {
4637             this.was_active = true;
4638             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4639             if (nv) {
4640                 nv.clearWasActive(this);
4641             }
4642             
4643         }
4644         this.active = state;
4645         
4646         if (!state ) {
4647             this.el.removeClass('active');
4648         } else if (!this.el.hasClass('active')) {
4649             this.el.addClass('active');
4650         }
4651         if (fire) {
4652             this.fireEvent('changed', this, state);
4653         }
4654         
4655         // show a panel if it's registered and related..
4656         
4657         if (!this.navId || !this.tabId || !state || is_was_active) {
4658             return;
4659         }
4660         
4661         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4662         if (!tg) {
4663             return;
4664         }
4665         var pan = tg.getPanelByName(this.tabId);
4666         if (!pan) {
4667             return;
4668         }
4669         // if we can not flip to new panel - go back to old nav highlight..
4670         if (false == tg.showPanel(pan)) {
4671             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4672             if (nv) {
4673                 var onav = nv.getWasActive();
4674                 if (onav) {
4675                     onav.setActive(true, false, true);
4676                 }
4677             }
4678             
4679         }
4680         
4681         
4682         
4683     },
4684      // this should not be here...
4685     setDisabled : function(state)
4686     {
4687         this.disabled = state;
4688         if (!state ) {
4689             this.el.removeClass('disabled');
4690         } else if (!this.el.hasClass('disabled')) {
4691             this.el.addClass('disabled');
4692         }
4693         
4694     },
4695     
4696     /**
4697      * Fetch the element to display the tooltip on.
4698      * @return {Roo.Element} defaults to this.el
4699      */
4700     tooltipEl : function()
4701     {
4702         return this.el.select('' + this.tagtype + '', true).first();
4703     },
4704     
4705     scrollToElement : function(e)
4706     {
4707         var c = document.body;
4708         
4709         /*
4710          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4711          */
4712         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4713             c = document.documentElement;
4714         }
4715         
4716         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4717         
4718         if(!target){
4719             return;
4720         }
4721
4722         var o = target.calcOffsetsTo(c);
4723         
4724         var options = {
4725             target : target,
4726             value : o[1]
4727         };
4728         
4729         this.fireEvent('scrollto', this, options, e);
4730         
4731         Roo.get(c).scrollTo('top', options.value, true);
4732         
4733         return;
4734     }
4735 });
4736  
4737
4738  /*
4739  * - LGPL
4740  *
4741  * sidebar item
4742  *
4743  *  li
4744  *    <span> icon </span>
4745  *    <span> text </span>
4746  *    <span>badge </span>
4747  */
4748
4749 /**
4750  * @class Roo.bootstrap.NavSidebarItem
4751  * @extends Roo.bootstrap.NavItem
4752  * Bootstrap Navbar.NavSidebarItem class
4753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4754  * {Boolean} open is the menu open
4755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4757  * {String} buttonSize (sm|md|lg)the extra classes for the button
4758  * {Boolean} showArrow show arrow next to the text (default true)
4759  * @constructor
4760  * Create a new Navbar Button
4761  * @param {Object} config The config object
4762  */
4763 Roo.bootstrap.NavSidebarItem = function(config){
4764     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4765     this.addEvents({
4766         // raw events
4767         /**
4768          * @event click
4769          * The raw click event for the entire grid.
4770          * @param {Roo.EventObject} e
4771          */
4772         "click" : true,
4773          /**
4774             * @event changed
4775             * Fires when the active item active state changes
4776             * @param {Roo.bootstrap.NavSidebarItem} this
4777             * @param {boolean} state the new state
4778              
4779          */
4780         'changed': true
4781     });
4782    
4783 };
4784
4785 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4786     
4787     badgeWeight : 'default',
4788     
4789     open: false,
4790     
4791     buttonView : false,
4792     
4793     buttonWeight : 'default',
4794     
4795     buttonSize : 'md',
4796     
4797     showArrow : true,
4798     
4799     getAutoCreate : function(){
4800         
4801         
4802         var a = {
4803                 tag: 'a',
4804                 href : this.href || '#',
4805                 cls: '',
4806                 html : '',
4807                 cn : []
4808         };
4809         
4810         if(this.buttonView){
4811             a = {
4812                 tag: 'button',
4813                 href : this.href || '#',
4814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4815                 html : this.html,
4816                 cn : []
4817             };
4818         }
4819         
4820         var cfg = {
4821             tag: 'li',
4822             cls: '',
4823             cn: [ a ]
4824         };
4825         
4826         if (this.active) {
4827             cfg.cls += ' active';
4828         }
4829         
4830         if (this.disabled) {
4831             cfg.cls += ' disabled';
4832         }
4833         if (this.open) {
4834             cfg.cls += ' open x-open';
4835         }
4836         // left icon..
4837         if (this.glyphicon || this.icon) {
4838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4839             a.cn.push({ tag : 'i', cls : c }) ;
4840         }
4841         
4842         if(!this.buttonView){
4843             var span = {
4844                 tag: 'span',
4845                 html : this.html || ''
4846             };
4847
4848             a.cn.push(span);
4849             
4850         }
4851         
4852         if (this.badge !== '') {
4853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4854         }
4855         
4856         if (this.menu) {
4857             
4858             if(this.showArrow){
4859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4860             }
4861             
4862             a.cls += ' dropdown-toggle treeview' ;
4863         }
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents : function()
4869     { 
4870         if (typeof (this.menu) != 'undefined') {
4871             this.menu.parentType = this.xtype;
4872             this.menu.triggerEl = this.el;
4873             this.menu = this.addxtype(Roo.apply({}, this.menu));
4874         }
4875         
4876         this.el.on('click', this.onClick, this);
4877         
4878         if(this.badge !== ''){
4879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4880         }
4881         
4882     },
4883     
4884     onClick : function(e)
4885     {
4886         if(this.disabled){
4887             e.preventDefault();
4888             return;
4889         }
4890         
4891         if(this.preventDefault){
4892             e.preventDefault();
4893         }
4894         
4895         this.fireEvent('click', this);
4896     },
4897     
4898     disable : function()
4899     {
4900         this.setDisabled(true);
4901     },
4902     
4903     enable : function()
4904     {
4905         this.setDisabled(false);
4906     },
4907     
4908     setDisabled : function(state)
4909     {
4910         if(this.disabled == state){
4911             return;
4912         }
4913         
4914         this.disabled = state;
4915         
4916         if (state) {
4917             this.el.addClass('disabled');
4918             return;
4919         }
4920         
4921         this.el.removeClass('disabled');
4922         
4923         return;
4924     },
4925     
4926     setActive : function(state)
4927     {
4928         if(this.active == state){
4929             return;
4930         }
4931         
4932         this.active = state;
4933         
4934         if (state) {
4935             this.el.addClass('active');
4936             return;
4937         }
4938         
4939         this.el.removeClass('active');
4940         
4941         return;
4942     },
4943     
4944     isActive: function () 
4945     {
4946         return this.active;
4947     },
4948     
4949     setBadge : function(str)
4950     {
4951         if(!this.badgeEl){
4952             return;
4953         }
4954         
4955         this.badgeEl.dom.innerHTML = str;
4956     }
4957     
4958    
4959      
4960  
4961 });
4962  
4963
4964  /*
4965  * - LGPL
4966  *
4967  * row
4968  * 
4969  */
4970
4971 /**
4972  * @class Roo.bootstrap.Row
4973  * @extends Roo.bootstrap.Component
4974  * Bootstrap Row class (contains columns...)
4975  * 
4976  * @constructor
4977  * Create a new Row
4978  * @param {Object} config The config object
4979  */
4980
4981 Roo.bootstrap.Row = function(config){
4982     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4983 };
4984
4985 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4986     
4987     getAutoCreate : function(){
4988        return {
4989             cls: 'row clearfix'
4990        };
4991     }
4992     
4993     
4994 });
4995
4996  
4997
4998  /*
4999  * - LGPL
5000  *
5001  * element
5002  * 
5003  */
5004
5005 /**
5006  * @class Roo.bootstrap.Element
5007  * @extends Roo.bootstrap.Component
5008  * Bootstrap Element class
5009  * @cfg {String} html contents of the element
5010  * @cfg {String} tag tag of the element
5011  * @cfg {String} cls class of the element
5012  * @cfg {Boolean} preventDefault (true|false) default false
5013  * @cfg {Boolean} clickable (true|false) default false
5014  * 
5015  * @constructor
5016  * Create a new Element
5017  * @param {Object} config The config object
5018  */
5019
5020 Roo.bootstrap.Element = function(config){
5021     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5022     
5023     this.addEvents({
5024         // raw events
5025         /**
5026          * @event click
5027          * When a element is chick
5028          * @param {Roo.bootstrap.Element} this
5029          * @param {Roo.EventObject} e
5030          */
5031         "click" : true
5032     });
5033 };
5034
5035 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5036     
5037     tag: 'div',
5038     cls: '',
5039     html: '',
5040     preventDefault: false, 
5041     clickable: false,
5042     
5043     getAutoCreate : function(){
5044         
5045         var cfg = {
5046             tag: this.tag,
5047             // cls: this.cls, double assign in parent class Component.js :: onRender
5048             html: this.html
5049         };
5050         
5051         return cfg;
5052     },
5053     
5054     initEvents: function() 
5055     {
5056         Roo.bootstrap.Element.superclass.initEvents.call(this);
5057         
5058         if(this.clickable){
5059             this.el.on('click', this.onClick, this);
5060         }
5061         
5062     },
5063     
5064     onClick : function(e)
5065     {
5066         if(this.preventDefault){
5067             e.preventDefault();
5068         }
5069         
5070         this.fireEvent('click', this, e);
5071     },
5072     
5073     getValue : function()
5074     {
5075         return this.el.dom.innerHTML;
5076     },
5077     
5078     setValue : function(value)
5079     {
5080         this.el.dom.innerHTML = value;
5081     }
5082    
5083 });
5084
5085  
5086
5087  /*
5088  * - LGPL
5089  *
5090  * pagination
5091  * 
5092  */
5093
5094 /**
5095  * @class Roo.bootstrap.Pagination
5096  * @extends Roo.bootstrap.Component
5097  * Bootstrap Pagination class
5098  * @cfg {String} size xs | sm | md | lg
5099  * @cfg {Boolean} inverse false | true
5100  * 
5101  * @constructor
5102  * Create a new Pagination
5103  * @param {Object} config The config object
5104  */
5105
5106 Roo.bootstrap.Pagination = function(config){
5107     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5108 };
5109
5110 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5111     
5112     cls: false,
5113     size: false,
5114     inverse: false,
5115     
5116     getAutoCreate : function(){
5117         var cfg = {
5118             tag: 'ul',
5119                 cls: 'pagination'
5120         };
5121         if (this.inverse) {
5122             cfg.cls += ' inverse';
5123         }
5124         if (this.html) {
5125             cfg.html=this.html;
5126         }
5127         if (this.cls) {
5128             cfg.cls += " " + this.cls;
5129         }
5130         return cfg;
5131     }
5132    
5133 });
5134
5135  
5136
5137  /*
5138  * - LGPL
5139  *
5140  * Pagination item
5141  * 
5142  */
5143
5144
5145 /**
5146  * @class Roo.bootstrap.PaginationItem
5147  * @extends Roo.bootstrap.Component
5148  * Bootstrap PaginationItem class
5149  * @cfg {String} html text
5150  * @cfg {String} href the link
5151  * @cfg {Boolean} preventDefault (true | false) default true
5152  * @cfg {Boolean} active (true | false) default false
5153  * @cfg {Boolean} disabled default false
5154  * 
5155  * 
5156  * @constructor
5157  * Create a new PaginationItem
5158  * @param {Object} config The config object
5159  */
5160
5161
5162 Roo.bootstrap.PaginationItem = function(config){
5163     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5164     this.addEvents({
5165         // raw events
5166         /**
5167          * @event click
5168          * The raw click event for the entire grid.
5169          * @param {Roo.EventObject} e
5170          */
5171         "click" : true
5172     });
5173 };
5174
5175 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5176     
5177     href : false,
5178     html : false,
5179     preventDefault: true,
5180     active : false,
5181     cls : false,
5182     disabled: false,
5183     
5184     getAutoCreate : function(){
5185         var cfg= {
5186             tag: 'li',
5187             cn: [
5188                 {
5189                     tag : 'a',
5190                     href : this.href ? this.href : '#',
5191                     html : this.html ? this.html : ''
5192                 }
5193             ]
5194         };
5195         
5196         if(this.cls){
5197             cfg.cls = this.cls;
5198         }
5199         
5200         if(this.disabled){
5201             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5202         }
5203         
5204         if(this.active){
5205             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5206         }
5207         
5208         return cfg;
5209     },
5210     
5211     initEvents: function() {
5212         
5213         this.el.on('click', this.onClick, this);
5214         
5215     },
5216     onClick : function(e)
5217     {
5218         Roo.log('PaginationItem on click ');
5219         if(this.preventDefault){
5220             e.preventDefault();
5221         }
5222         
5223         if(this.disabled){
5224             return;
5225         }
5226         
5227         this.fireEvent('click', this, e);
5228     }
5229    
5230 });
5231
5232  
5233
5234  /*
5235  * - LGPL
5236  *
5237  * slider
5238  * 
5239  */
5240
5241
5242 /**
5243  * @class Roo.bootstrap.Slider
5244  * @extends Roo.bootstrap.Component
5245  * Bootstrap Slider class
5246  *    
5247  * @constructor
5248  * Create a new Slider
5249  * @param {Object} config The config object
5250  */
5251
5252 Roo.bootstrap.Slider = function(config){
5253     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5254 };
5255
5256 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5257     
5258     getAutoCreate : function(){
5259         
5260         var cfg = {
5261             tag: 'div',
5262             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5263             cn: [
5264                 {
5265                     tag: 'a',
5266                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5267                 }
5268             ]
5269         };
5270         
5271         return cfg;
5272     }
5273    
5274 });
5275
5276  /*
5277  * Based on:
5278  * Ext JS Library 1.1.1
5279  * Copyright(c) 2006-2007, Ext JS, LLC.
5280  *
5281  * Originally Released Under LGPL - original licence link has changed is not relivant.
5282  *
5283  * Fork - LGPL
5284  * <script type="text/javascript">
5285  */
5286  
5287
5288 /**
5289  * @class Roo.grid.ColumnModel
5290  * @extends Roo.util.Observable
5291  * This is the default implementation of a ColumnModel used by the Grid. It defines
5292  * the columns in the grid.
5293  * <br>Usage:<br>
5294  <pre><code>
5295  var colModel = new Roo.grid.ColumnModel([
5296         {header: "Ticker", width: 60, sortable: true, locked: true},
5297         {header: "Company Name", width: 150, sortable: true},
5298         {header: "Market Cap.", width: 100, sortable: true},
5299         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5300         {header: "Employees", width: 100, sortable: true, resizable: false}
5301  ]);
5302  </code></pre>
5303  * <p>
5304  
5305  * The config options listed for this class are options which may appear in each
5306  * individual column definition.
5307  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5308  * @constructor
5309  * @param {Object} config An Array of column config objects. See this class's
5310  * config objects for details.
5311 */
5312 Roo.grid.ColumnModel = function(config){
5313         /**
5314      * The config passed into the constructor
5315      */
5316     this.config = config;
5317     this.lookup = {};
5318
5319     // if no id, create one
5320     // if the column does not have a dataIndex mapping,
5321     // map it to the order it is in the config
5322     for(var i = 0, len = config.length; i < len; i++){
5323         var c = config[i];
5324         if(typeof c.dataIndex == "undefined"){
5325             c.dataIndex = i;
5326         }
5327         if(typeof c.renderer == "string"){
5328             c.renderer = Roo.util.Format[c.renderer];
5329         }
5330         if(typeof c.id == "undefined"){
5331             c.id = Roo.id();
5332         }
5333         if(c.editor && c.editor.xtype){
5334             c.editor  = Roo.factory(c.editor, Roo.grid);
5335         }
5336         if(c.editor && c.editor.isFormField){
5337             c.editor = new Roo.grid.GridEditor(c.editor);
5338         }
5339         this.lookup[c.id] = c;
5340     }
5341
5342     /**
5343      * The width of columns which have no width specified (defaults to 100)
5344      * @type Number
5345      */
5346     this.defaultWidth = 100;
5347
5348     /**
5349      * Default sortable of columns which have no sortable specified (defaults to false)
5350      * @type Boolean
5351      */
5352     this.defaultSortable = false;
5353
5354     this.addEvents({
5355         /**
5356              * @event widthchange
5357              * Fires when the width of a column changes.
5358              * @param {ColumnModel} this
5359              * @param {Number} columnIndex The column index
5360              * @param {Number} newWidth The new width
5361              */
5362             "widthchange": true,
5363         /**
5364              * @event headerchange
5365              * Fires when the text of a header changes.
5366              * @param {ColumnModel} this
5367              * @param {Number} columnIndex The column index
5368              * @param {Number} newText The new header text
5369              */
5370             "headerchange": true,
5371         /**
5372              * @event hiddenchange
5373              * Fires when a column is hidden or "unhidden".
5374              * @param {ColumnModel} this
5375              * @param {Number} columnIndex The column index
5376              * @param {Boolean} hidden true if hidden, false otherwise
5377              */
5378             "hiddenchange": true,
5379             /**
5380          * @event columnmoved
5381          * Fires when a column is moved.
5382          * @param {ColumnModel} this
5383          * @param {Number} oldIndex
5384          * @param {Number} newIndex
5385          */
5386         "columnmoved" : true,
5387         /**
5388          * @event columlockchange
5389          * Fires when a column's locked state is changed
5390          * @param {ColumnModel} this
5391          * @param {Number} colIndex
5392          * @param {Boolean} locked true if locked
5393          */
5394         "columnlockchange" : true
5395     });
5396     Roo.grid.ColumnModel.superclass.constructor.call(this);
5397 };
5398 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5399     /**
5400      * @cfg {String} header The header text to display in the Grid view.
5401      */
5402     /**
5403      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5404      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5405      * specified, the column's index is used as an index into the Record's data Array.
5406      */
5407     /**
5408      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5409      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5410      */
5411     /**
5412      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5413      * Defaults to the value of the {@link #defaultSortable} property.
5414      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5415      */
5416     /**
5417      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5418      */
5419     /**
5420      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5421      */
5422     /**
5423      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5424      */
5425     /**
5426      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5427      */
5428     /**
5429      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5430      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5431      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5432      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5433      */
5434        /**
5435      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5436      */
5437     /**
5438      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5439      */
5440     /**
5441      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5442      */
5443     /**
5444      * @cfg {String} cursor (Optional)
5445      */
5446     /**
5447      * @cfg {String} tooltip (Optional)
5448      */
5449     /**
5450      * @cfg {Number} xs (Optional)
5451      */
5452     /**
5453      * @cfg {Number} sm (Optional)
5454      */
5455     /**
5456      * @cfg {Number} md (Optional)
5457      */
5458     /**
5459      * @cfg {Number} lg (Optional)
5460      */
5461     /**
5462      * Returns the id of the column at the specified index.
5463      * @param {Number} index The column index
5464      * @return {String} the id
5465      */
5466     getColumnId : function(index){
5467         return this.config[index].id;
5468     },
5469
5470     /**
5471      * Returns the column for a specified id.
5472      * @param {String} id The column id
5473      * @return {Object} the column
5474      */
5475     getColumnById : function(id){
5476         return this.lookup[id];
5477     },
5478
5479     
5480     /**
5481      * Returns the column for a specified dataIndex.
5482      * @param {String} dataIndex The column dataIndex
5483      * @return {Object|Boolean} the column or false if not found
5484      */
5485     getColumnByDataIndex: function(dataIndex){
5486         var index = this.findColumnIndex(dataIndex);
5487         return index > -1 ? this.config[index] : false;
5488     },
5489     
5490     /**
5491      * Returns the index for a specified column id.
5492      * @param {String} id The column id
5493      * @return {Number} the index, or -1 if not found
5494      */
5495     getIndexById : function(id){
5496         for(var i = 0, len = this.config.length; i < len; i++){
5497             if(this.config[i].id == id){
5498                 return i;
5499             }
5500         }
5501         return -1;
5502     },
5503     
5504     /**
5505      * Returns the index for a specified column dataIndex.
5506      * @param {String} dataIndex The column dataIndex
5507      * @return {Number} the index, or -1 if not found
5508      */
5509     
5510     findColumnIndex : function(dataIndex){
5511         for(var i = 0, len = this.config.length; i < len; i++){
5512             if(this.config[i].dataIndex == dataIndex){
5513                 return i;
5514             }
5515         }
5516         return -1;
5517     },
5518     
5519     
5520     moveColumn : function(oldIndex, newIndex){
5521         var c = this.config[oldIndex];
5522         this.config.splice(oldIndex, 1);
5523         this.config.splice(newIndex, 0, c);
5524         this.dataMap = null;
5525         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5526     },
5527
5528     isLocked : function(colIndex){
5529         return this.config[colIndex].locked === true;
5530     },
5531
5532     setLocked : function(colIndex, value, suppressEvent){
5533         if(this.isLocked(colIndex) == value){
5534             return;
5535         }
5536         this.config[colIndex].locked = value;
5537         if(!suppressEvent){
5538             this.fireEvent("columnlockchange", this, colIndex, value);
5539         }
5540     },
5541
5542     getTotalLockedWidth : function(){
5543         var totalWidth = 0;
5544         for(var i = 0; i < this.config.length; i++){
5545             if(this.isLocked(i) && !this.isHidden(i)){
5546                 this.totalWidth += this.getColumnWidth(i);
5547             }
5548         }
5549         return totalWidth;
5550     },
5551
5552     getLockedCount : function(){
5553         for(var i = 0, len = this.config.length; i < len; i++){
5554             if(!this.isLocked(i)){
5555                 return i;
5556             }
5557         }
5558         
5559         return this.config.length;
5560     },
5561
5562     /**
5563      * Returns the number of columns.
5564      * @return {Number}
5565      */
5566     getColumnCount : function(visibleOnly){
5567         if(visibleOnly === true){
5568             var c = 0;
5569             for(var i = 0, len = this.config.length; i < len; i++){
5570                 if(!this.isHidden(i)){
5571                     c++;
5572                 }
5573             }
5574             return c;
5575         }
5576         return this.config.length;
5577     },
5578
5579     /**
5580      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5581      * @param {Function} fn
5582      * @param {Object} scope (optional)
5583      * @return {Array} result
5584      */
5585     getColumnsBy : function(fn, scope){
5586         var r = [];
5587         for(var i = 0, len = this.config.length; i < len; i++){
5588             var c = this.config[i];
5589             if(fn.call(scope||this, c, i) === true){
5590                 r[r.length] = c;
5591             }
5592         }
5593         return r;
5594     },
5595
5596     /**
5597      * Returns true if the specified column is sortable.
5598      * @param {Number} col The column index
5599      * @return {Boolean}
5600      */
5601     isSortable : function(col){
5602         if(typeof this.config[col].sortable == "undefined"){
5603             return this.defaultSortable;
5604         }
5605         return this.config[col].sortable;
5606     },
5607
5608     /**
5609      * Returns the rendering (formatting) function defined for the column.
5610      * @param {Number} col The column index.
5611      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5612      */
5613     getRenderer : function(col){
5614         if(!this.config[col].renderer){
5615             return Roo.grid.ColumnModel.defaultRenderer;
5616         }
5617         return this.config[col].renderer;
5618     },
5619
5620     /**
5621      * Sets the rendering (formatting) function for a column.
5622      * @param {Number} col The column index
5623      * @param {Function} fn The function to use to process the cell's raw data
5624      * to return HTML markup for the grid view. The render function is called with
5625      * the following parameters:<ul>
5626      * <li>Data value.</li>
5627      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5628      * <li>css A CSS style string to apply to the table cell.</li>
5629      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5630      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5631      * <li>Row index</li>
5632      * <li>Column index</li>
5633      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5634      */
5635     setRenderer : function(col, fn){
5636         this.config[col].renderer = fn;
5637     },
5638
5639     /**
5640      * Returns the width for the specified column.
5641      * @param {Number} col The column index
5642      * @return {Number}
5643      */
5644     getColumnWidth : function(col){
5645         return this.config[col].width * 1 || this.defaultWidth;
5646     },
5647
5648     /**
5649      * Sets the width for a column.
5650      * @param {Number} col The column index
5651      * @param {Number} width The new width
5652      */
5653     setColumnWidth : function(col, width, suppressEvent){
5654         this.config[col].width = width;
5655         this.totalWidth = null;
5656         if(!suppressEvent){
5657              this.fireEvent("widthchange", this, col, width);
5658         }
5659     },
5660
5661     /**
5662      * Returns the total width of all columns.
5663      * @param {Boolean} includeHidden True to include hidden column widths
5664      * @return {Number}
5665      */
5666     getTotalWidth : function(includeHidden){
5667         if(!this.totalWidth){
5668             this.totalWidth = 0;
5669             for(var i = 0, len = this.config.length; i < len; i++){
5670                 if(includeHidden || !this.isHidden(i)){
5671                     this.totalWidth += this.getColumnWidth(i);
5672                 }
5673             }
5674         }
5675         return this.totalWidth;
5676     },
5677
5678     /**
5679      * Returns the header for the specified column.
5680      * @param {Number} col The column index
5681      * @return {String}
5682      */
5683     getColumnHeader : function(col){
5684         return this.config[col].header;
5685     },
5686
5687     /**
5688      * Sets the header for a column.
5689      * @param {Number} col The column index
5690      * @param {String} header The new header
5691      */
5692     setColumnHeader : function(col, header){
5693         this.config[col].header = header;
5694         this.fireEvent("headerchange", this, col, header);
5695     },
5696
5697     /**
5698      * Returns the tooltip for the specified column.
5699      * @param {Number} col The column index
5700      * @return {String}
5701      */
5702     getColumnTooltip : function(col){
5703             return this.config[col].tooltip;
5704     },
5705     /**
5706      * Sets the tooltip for a column.
5707      * @param {Number} col The column index
5708      * @param {String} tooltip The new tooltip
5709      */
5710     setColumnTooltip : function(col, tooltip){
5711             this.config[col].tooltip = tooltip;
5712     },
5713
5714     /**
5715      * Returns the dataIndex for the specified column.
5716      * @param {Number} col The column index
5717      * @return {Number}
5718      */
5719     getDataIndex : function(col){
5720         return this.config[col].dataIndex;
5721     },
5722
5723     /**
5724      * Sets the dataIndex for a column.
5725      * @param {Number} col The column index
5726      * @param {Number} dataIndex The new dataIndex
5727      */
5728     setDataIndex : function(col, dataIndex){
5729         this.config[col].dataIndex = dataIndex;
5730     },
5731
5732     
5733     
5734     /**
5735      * Returns true if the cell is editable.
5736      * @param {Number} colIndex The column index
5737      * @param {Number} rowIndex The row index - this is nto actually used..?
5738      * @return {Boolean}
5739      */
5740     isCellEditable : function(colIndex, rowIndex){
5741         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5742     },
5743
5744     /**
5745      * Returns the editor defined for the cell/column.
5746      * return false or null to disable editing.
5747      * @param {Number} colIndex The column index
5748      * @param {Number} rowIndex The row index
5749      * @return {Object}
5750      */
5751     getCellEditor : function(colIndex, rowIndex){
5752         return this.config[colIndex].editor;
5753     },
5754
5755     /**
5756      * Sets if a column is editable.
5757      * @param {Number} col The column index
5758      * @param {Boolean} editable True if the column is editable
5759      */
5760     setEditable : function(col, editable){
5761         this.config[col].editable = editable;
5762     },
5763
5764
5765     /**
5766      * Returns true if the column is hidden.
5767      * @param {Number} colIndex The column index
5768      * @return {Boolean}
5769      */
5770     isHidden : function(colIndex){
5771         return this.config[colIndex].hidden;
5772     },
5773
5774
5775     /**
5776      * Returns true if the column width cannot be changed
5777      */
5778     isFixed : function(colIndex){
5779         return this.config[colIndex].fixed;
5780     },
5781
5782     /**
5783      * Returns true if the column can be resized
5784      * @return {Boolean}
5785      */
5786     isResizable : function(colIndex){
5787         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5788     },
5789     /**
5790      * Sets if a column is hidden.
5791      * @param {Number} colIndex The column index
5792      * @param {Boolean} hidden True if the column is hidden
5793      */
5794     setHidden : function(colIndex, hidden){
5795         this.config[colIndex].hidden = hidden;
5796         this.totalWidth = null;
5797         this.fireEvent("hiddenchange", this, colIndex, hidden);
5798     },
5799
5800     /**
5801      * Sets the editor for a column.
5802      * @param {Number} col The column index
5803      * @param {Object} editor The editor object
5804      */
5805     setEditor : function(col, editor){
5806         this.config[col].editor = editor;
5807     }
5808 });
5809
5810 Roo.grid.ColumnModel.defaultRenderer = function(value)
5811 {
5812     if(typeof value == "object") {
5813         return value;
5814     }
5815         if(typeof value == "string" && value.length < 1){
5816             return "&#160;";
5817         }
5818     
5819         return String.format("{0}", value);
5820 };
5821
5822 // Alias for backwards compatibility
5823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5824 /*
5825  * Based on:
5826  * Ext JS Library 1.1.1
5827  * Copyright(c) 2006-2007, Ext JS, LLC.
5828  *
5829  * Originally Released Under LGPL - original licence link has changed is not relivant.
5830  *
5831  * Fork - LGPL
5832  * <script type="text/javascript">
5833  */
5834  
5835 /**
5836  * @class Roo.LoadMask
5837  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5838  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5839  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5840  * element's UpdateManager load indicator and will be destroyed after the initial load.
5841  * @constructor
5842  * Create a new LoadMask
5843  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5844  * @param {Object} config The config object
5845  */
5846 Roo.LoadMask = function(el, config){
5847     this.el = Roo.get(el);
5848     Roo.apply(this, config);
5849     if(this.store){
5850         this.store.on('beforeload', this.onBeforeLoad, this);
5851         this.store.on('load', this.onLoad, this);
5852         this.store.on('loadexception', this.onLoadException, this);
5853         this.removeMask = false;
5854     }else{
5855         var um = this.el.getUpdateManager();
5856         um.showLoadIndicator = false; // disable the default indicator
5857         um.on('beforeupdate', this.onBeforeLoad, this);
5858         um.on('update', this.onLoad, this);
5859         um.on('failure', this.onLoad, this);
5860         this.removeMask = true;
5861     }
5862 };
5863
5864 Roo.LoadMask.prototype = {
5865     /**
5866      * @cfg {Boolean} removeMask
5867      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5868      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5869      */
5870     /**
5871      * @cfg {String} msg
5872      * The text to display in a centered loading message box (defaults to 'Loading...')
5873      */
5874     msg : 'Loading...',
5875     /**
5876      * @cfg {String} msgCls
5877      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5878      */
5879     msgCls : 'x-mask-loading',
5880
5881     /**
5882      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5883      * @type Boolean
5884      */
5885     disabled: false,
5886
5887     /**
5888      * Disables the mask to prevent it from being displayed
5889      */
5890     disable : function(){
5891        this.disabled = true;
5892     },
5893
5894     /**
5895      * Enables the mask so that it can be displayed
5896      */
5897     enable : function(){
5898         this.disabled = false;
5899     },
5900     
5901     onLoadException : function()
5902     {
5903         Roo.log(arguments);
5904         
5905         if (typeof(arguments[3]) != 'undefined') {
5906             Roo.MessageBox.alert("Error loading",arguments[3]);
5907         } 
5908         /*
5909         try {
5910             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5911                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5912             }   
5913         } catch(e) {
5914             
5915         }
5916         */
5917     
5918         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5919     },
5920     // private
5921     onLoad : function()
5922     {
5923         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5924     },
5925
5926     // private
5927     onBeforeLoad : function(){
5928         if(!this.disabled){
5929             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5930         }
5931     },
5932
5933     // private
5934     destroy : function(){
5935         if(this.store){
5936             this.store.un('beforeload', this.onBeforeLoad, this);
5937             this.store.un('load', this.onLoad, this);
5938             this.store.un('loadexception', this.onLoadException, this);
5939         }else{
5940             var um = this.el.getUpdateManager();
5941             um.un('beforeupdate', this.onBeforeLoad, this);
5942             um.un('update', this.onLoad, this);
5943             um.un('failure', this.onLoad, this);
5944         }
5945     }
5946 };/*
5947  * - LGPL
5948  *
5949  * table
5950  * 
5951  */
5952
5953 /**
5954  * @class Roo.bootstrap.Table
5955  * @extends Roo.bootstrap.Component
5956  * Bootstrap Table class
5957  * @cfg {String} cls table class
5958  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5959  * @cfg {String} bgcolor Specifies the background color for a table
5960  * @cfg {Number} border Specifies whether the table cells should have borders or not
5961  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5962  * @cfg {Number} cellspacing Specifies the space between cells
5963  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5964  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5965  * @cfg {String} sortable Specifies that the table should be sortable
5966  * @cfg {String} summary Specifies a summary of the content of a table
5967  * @cfg {Number} width Specifies the width of a table
5968  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5969  * 
5970  * @cfg {boolean} striped Should the rows be alternative striped
5971  * @cfg {boolean} bordered Add borders to the table
5972  * @cfg {boolean} hover Add hover highlighting
5973  * @cfg {boolean} condensed Format condensed
5974  * @cfg {boolean} responsive Format condensed
5975  * @cfg {Boolean} loadMask (true|false) default false
5976  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5977  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5978  * @cfg {Boolean} rowSelection (true|false) default false
5979  * @cfg {Boolean} cellSelection (true|false) default false
5980  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5981  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5982  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5983  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5984  
5985  * 
5986  * @constructor
5987  * Create a new Table
5988  * @param {Object} config The config object
5989  */
5990
5991 Roo.bootstrap.Table = function(config){
5992     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5993     
5994   
5995     
5996     // BC...
5997     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5998     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5999     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6000     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6001     
6002     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6003     if (this.sm) {
6004         this.sm.grid = this;
6005         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6006         this.sm = this.selModel;
6007         this.sm.xmodule = this.xmodule || false;
6008     }
6009     
6010     if (this.cm && typeof(this.cm.config) == 'undefined') {
6011         this.colModel = new Roo.grid.ColumnModel(this.cm);
6012         this.cm = this.colModel;
6013         this.cm.xmodule = this.xmodule || false;
6014     }
6015     if (this.store) {
6016         this.store= Roo.factory(this.store, Roo.data);
6017         this.ds = this.store;
6018         this.ds.xmodule = this.xmodule || false;
6019          
6020     }
6021     if (this.footer && this.store) {
6022         this.footer.dataSource = this.ds;
6023         this.footer = Roo.factory(this.footer);
6024     }
6025     
6026     /** @private */
6027     this.addEvents({
6028         /**
6029          * @event cellclick
6030          * Fires when a cell is clicked
6031          * @param {Roo.bootstrap.Table} this
6032          * @param {Roo.Element} el
6033          * @param {Number} rowIndex
6034          * @param {Number} columnIndex
6035          * @param {Roo.EventObject} e
6036          */
6037         "cellclick" : true,
6038         /**
6039          * @event celldblclick
6040          * Fires when a cell is double clicked
6041          * @param {Roo.bootstrap.Table} this
6042          * @param {Roo.Element} el
6043          * @param {Number} rowIndex
6044          * @param {Number} columnIndex
6045          * @param {Roo.EventObject} e
6046          */
6047         "celldblclick" : true,
6048         /**
6049          * @event rowclick
6050          * Fires when a row is clicked
6051          * @param {Roo.bootstrap.Table} this
6052          * @param {Roo.Element} el
6053          * @param {Number} rowIndex
6054          * @param {Roo.EventObject} e
6055          */
6056         "rowclick" : true,
6057         /**
6058          * @event rowdblclick
6059          * Fires when a row is double clicked
6060          * @param {Roo.bootstrap.Table} this
6061          * @param {Roo.Element} el
6062          * @param {Number} rowIndex
6063          * @param {Roo.EventObject} e
6064          */
6065         "rowdblclick" : true,
6066         /**
6067          * @event mouseover
6068          * Fires when a mouseover occur
6069          * @param {Roo.bootstrap.Table} this
6070          * @param {Roo.Element} el
6071          * @param {Number} rowIndex
6072          * @param {Number} columnIndex
6073          * @param {Roo.EventObject} e
6074          */
6075         "mouseover" : true,
6076         /**
6077          * @event mouseout
6078          * Fires when a mouseout occur
6079          * @param {Roo.bootstrap.Table} this
6080          * @param {Roo.Element} el
6081          * @param {Number} rowIndex
6082          * @param {Number} columnIndex
6083          * @param {Roo.EventObject} e
6084          */
6085         "mouseout" : true,
6086         /**
6087          * @event rowclass
6088          * Fires when a row is rendered, so you can change add a style to it.
6089          * @param {Roo.bootstrap.Table} this
6090          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6091          */
6092         'rowclass' : true,
6093           /**
6094          * @event rowsrendered
6095          * Fires when all the  rows have been rendered
6096          * @param {Roo.bootstrap.Table} this
6097          */
6098         'rowsrendered' : true,
6099         /**
6100          * @event contextmenu
6101          * The raw contextmenu event for the entire grid.
6102          * @param {Roo.EventObject} e
6103          */
6104         "contextmenu" : true,
6105         /**
6106          * @event rowcontextmenu
6107          * Fires when a row is right clicked
6108          * @param {Roo.bootstrap.Table} this
6109          * @param {Number} rowIndex
6110          * @param {Roo.EventObject} e
6111          */
6112         "rowcontextmenu" : true,
6113         /**
6114          * @event cellcontextmenu
6115          * Fires when a cell is right clicked
6116          * @param {Roo.bootstrap.Table} this
6117          * @param {Number} rowIndex
6118          * @param {Number} cellIndex
6119          * @param {Roo.EventObject} e
6120          */
6121          "cellcontextmenu" : true,
6122          /**
6123          * @event headercontextmenu
6124          * Fires when a header is right clicked
6125          * @param {Roo.bootstrap.Table} this
6126          * @param {Number} columnIndex
6127          * @param {Roo.EventObject} e
6128          */
6129         "headercontextmenu" : true
6130     });
6131 };
6132
6133 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6134     
6135     cls: false,
6136     align: false,
6137     bgcolor: false,
6138     border: false,
6139     cellpadding: false,
6140     cellspacing: false,
6141     frame: false,
6142     rules: false,
6143     sortable: false,
6144     summary: false,
6145     width: false,
6146     striped : false,
6147     scrollBody : false,
6148     bordered: false,
6149     hover:  false,
6150     condensed : false,
6151     responsive : false,
6152     sm : false,
6153     cm : false,
6154     store : false,
6155     loadMask : false,
6156     footerShow : true,
6157     headerShow : true,
6158   
6159     rowSelection : false,
6160     cellSelection : false,
6161     layout : false,
6162     
6163     // Roo.Element - the tbody
6164     mainBody: false,
6165     // Roo.Element - thead element
6166     mainHead: false,
6167     
6168     container: false, // used by gridpanel...
6169     
6170     lazyLoad : false,
6171     
6172     CSS : Roo.util.CSS,
6173     
6174     auto_hide_footer : false,
6175     
6176     getAutoCreate : function()
6177     {
6178         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6179         
6180         cfg = {
6181             tag: 'table',
6182             cls : 'table',
6183             cn : []
6184         };
6185         if (this.scrollBody) {
6186             cfg.cls += ' table-body-fixed';
6187         }    
6188         if (this.striped) {
6189             cfg.cls += ' table-striped';
6190         }
6191         
6192         if (this.hover) {
6193             cfg.cls += ' table-hover';
6194         }
6195         if (this.bordered) {
6196             cfg.cls += ' table-bordered';
6197         }
6198         if (this.condensed) {
6199             cfg.cls += ' table-condensed';
6200         }
6201         if (this.responsive) {
6202             cfg.cls += ' table-responsive';
6203         }
6204         
6205         if (this.cls) {
6206             cfg.cls+=  ' ' +this.cls;
6207         }
6208         
6209         // this lot should be simplifed...
6210         var _t = this;
6211         var cp = [
6212             'align',
6213             'bgcolor',
6214             'border',
6215             'cellpadding',
6216             'cellspacing',
6217             'frame',
6218             'rules',
6219             'sortable',
6220             'summary',
6221             'width'
6222         ].forEach(function(k) {
6223             if (_t[k]) {
6224                 cfg[k] = _t[k];
6225             }
6226         });
6227         
6228         
6229         if (this.layout) {
6230             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6231         }
6232         
6233         if(this.store || this.cm){
6234             if(this.headerShow){
6235                 cfg.cn.push(this.renderHeader());
6236             }
6237             
6238             cfg.cn.push(this.renderBody());
6239             
6240             if(this.footerShow){
6241                 cfg.cn.push(this.renderFooter());
6242             }
6243             // where does this come from?
6244             //cfg.cls+=  ' TableGrid';
6245         }
6246         
6247         return { cn : [ cfg ] };
6248     },
6249     
6250     initEvents : function()
6251     {   
6252         if(!this.store || !this.cm){
6253             return;
6254         }
6255         if (this.selModel) {
6256             this.selModel.initEvents();
6257         }
6258         
6259         
6260         //Roo.log('initEvents with ds!!!!');
6261         
6262         this.mainBody = this.el.select('tbody', true).first();
6263         this.mainHead = this.el.select('thead', true).first();
6264         this.mainFoot = this.el.select('tfoot', true).first();
6265         
6266         
6267         
6268         var _this = this;
6269         
6270         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6271             e.on('click', _this.sort, _this);
6272         });
6273         
6274         this.mainBody.on("click", this.onClick, this);
6275         this.mainBody.on("dblclick", this.onDblClick, this);
6276         
6277         // why is this done????? = it breaks dialogs??
6278         //this.parent().el.setStyle('position', 'relative');
6279         
6280         
6281         if (this.footer) {
6282             this.footer.parentId = this.id;
6283             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6284             
6285             if(this.lazyLoad){
6286                 this.el.select('tfoot tr td').first().addClass('hide');
6287             }
6288         } 
6289         
6290         if(this.loadMask) {
6291             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6292         }
6293         
6294         this.store.on('load', this.onLoad, this);
6295         this.store.on('beforeload', this.onBeforeLoad, this);
6296         this.store.on('update', this.onUpdate, this);
6297         this.store.on('add', this.onAdd, this);
6298         this.store.on("clear", this.clear, this);
6299         
6300         this.el.on("contextmenu", this.onContextMenu, this);
6301         
6302         this.mainBody.on('scroll', this.onBodyScroll, this);
6303         
6304         this.cm.on("headerchange", this.onHeaderChange, this);
6305         
6306         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6307         
6308     },
6309     
6310     onContextMenu : function(e, t)
6311     {
6312         this.processEvent("contextmenu", e);
6313     },
6314     
6315     processEvent : function(name, e)
6316     {
6317         if (name != 'touchstart' ) {
6318             this.fireEvent(name, e);    
6319         }
6320         
6321         var t = e.getTarget();
6322         
6323         var cell = Roo.get(t);
6324         
6325         if(!cell){
6326             return;
6327         }
6328         
6329         if(cell.findParent('tfoot', false, true)){
6330             return;
6331         }
6332         
6333         if(cell.findParent('thead', false, true)){
6334             
6335             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6336                 cell = Roo.get(t).findParent('th', false, true);
6337                 if (!cell) {
6338                     Roo.log("failed to find th in thead?");
6339                     Roo.log(e.getTarget());
6340                     return;
6341                 }
6342             }
6343             
6344             var cellIndex = cell.dom.cellIndex;
6345             
6346             var ename = name == 'touchstart' ? 'click' : name;
6347             this.fireEvent("header" + ename, this, cellIndex, e);
6348             
6349             return;
6350         }
6351         
6352         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6353             cell = Roo.get(t).findParent('td', false, true);
6354             if (!cell) {
6355                 Roo.log("failed to find th in tbody?");
6356                 Roo.log(e.getTarget());
6357                 return;
6358             }
6359         }
6360         
6361         var row = cell.findParent('tr', false, true);
6362         var cellIndex = cell.dom.cellIndex;
6363         var rowIndex = row.dom.rowIndex - 1;
6364         
6365         if(row !== false){
6366             
6367             this.fireEvent("row" + name, this, rowIndex, e);
6368             
6369             if(cell !== false){
6370             
6371                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6372             }
6373         }
6374         
6375     },
6376     
6377     onMouseover : function(e, el)
6378     {
6379         var cell = Roo.get(el);
6380         
6381         if(!cell){
6382             return;
6383         }
6384         
6385         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6386             cell = cell.findParent('td', false, true);
6387         }
6388         
6389         var row = cell.findParent('tr', false, true);
6390         var cellIndex = cell.dom.cellIndex;
6391         var rowIndex = row.dom.rowIndex - 1; // start from 0
6392         
6393         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6394         
6395     },
6396     
6397     onMouseout : function(e, el)
6398     {
6399         var cell = Roo.get(el);
6400         
6401         if(!cell){
6402             return;
6403         }
6404         
6405         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6406             cell = cell.findParent('td', false, true);
6407         }
6408         
6409         var row = cell.findParent('tr', false, true);
6410         var cellIndex = cell.dom.cellIndex;
6411         var rowIndex = row.dom.rowIndex - 1; // start from 0
6412         
6413         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6414         
6415     },
6416     
6417     onClick : function(e, el)
6418     {
6419         var cell = Roo.get(el);
6420         
6421         if(!cell || (!this.cellSelection && !this.rowSelection)){
6422             return;
6423         }
6424         
6425         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6426             cell = cell.findParent('td', false, true);
6427         }
6428         
6429         if(!cell || typeof(cell) == 'undefined'){
6430             return;
6431         }
6432         
6433         var row = cell.findParent('tr', false, true);
6434         
6435         if(!row || typeof(row) == 'undefined'){
6436             return;
6437         }
6438         
6439         var cellIndex = cell.dom.cellIndex;
6440         var rowIndex = this.getRowIndex(row);
6441         
6442         // why??? - should these not be based on SelectionModel?
6443         if(this.cellSelection){
6444             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6445         }
6446         
6447         if(this.rowSelection){
6448             this.fireEvent('rowclick', this, row, rowIndex, e);
6449         }
6450         
6451         
6452     },
6453         
6454     onDblClick : function(e,el)
6455     {
6456         var cell = Roo.get(el);
6457         
6458         if(!cell || (!this.cellSelection && !this.rowSelection)){
6459             return;
6460         }
6461         
6462         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6463             cell = cell.findParent('td', false, true);
6464         }
6465         
6466         if(!cell || typeof(cell) == 'undefined'){
6467             return;
6468         }
6469         
6470         var row = cell.findParent('tr', false, true);
6471         
6472         if(!row || typeof(row) == 'undefined'){
6473             return;
6474         }
6475         
6476         var cellIndex = cell.dom.cellIndex;
6477         var rowIndex = this.getRowIndex(row);
6478         
6479         if(this.cellSelection){
6480             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6481         }
6482         
6483         if(this.rowSelection){
6484             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6485         }
6486     },
6487     
6488     sort : function(e,el)
6489     {
6490         var col = Roo.get(el);
6491         
6492         if(!col.hasClass('sortable')){
6493             return;
6494         }
6495         
6496         var sort = col.attr('sort');
6497         var dir = 'ASC';
6498         
6499         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6500             dir = 'DESC';
6501         }
6502         
6503         this.store.sortInfo = {field : sort, direction : dir};
6504         
6505         if (this.footer) {
6506             Roo.log("calling footer first");
6507             this.footer.onClick('first');
6508         } else {
6509         
6510             this.store.load({ params : { start : 0 } });
6511         }
6512     },
6513     
6514     renderHeader : function()
6515     {
6516         var header = {
6517             tag: 'thead',
6518             cn : []
6519         };
6520         
6521         var cm = this.cm;
6522         this.totalWidth = 0;
6523         
6524         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6525             
6526             var config = cm.config[i];
6527             
6528             var c = {
6529                 tag: 'th',
6530                 cls : 'x-hcol-' + i,
6531                 style : '',
6532                 html: cm.getColumnHeader(i)
6533             };
6534             
6535             var hh = '';
6536             
6537             if(typeof(config.sortable) != 'undefined' && config.sortable){
6538                 c.cls = 'sortable';
6539                 c.html = '<i class="glyphicon"></i>' + c.html;
6540             }
6541             
6542             if(typeof(config.lgHeader) != 'undefined'){
6543                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6544             }
6545             
6546             if(typeof(config.mdHeader) != 'undefined'){
6547                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6548             }
6549             
6550             if(typeof(config.smHeader) != 'undefined'){
6551                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6552             }
6553             
6554             if(typeof(config.xsHeader) != 'undefined'){
6555                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6556             }
6557             
6558             if(hh.length){
6559                 c.html = hh;
6560             }
6561             
6562             if(typeof(config.tooltip) != 'undefined'){
6563                 c.tooltip = config.tooltip;
6564             }
6565             
6566             if(typeof(config.colspan) != 'undefined'){
6567                 c.colspan = config.colspan;
6568             }
6569             
6570             if(typeof(config.hidden) != 'undefined' && config.hidden){
6571                 c.style += ' display:none;';
6572             }
6573             
6574             if(typeof(config.dataIndex) != 'undefined'){
6575                 c.sort = config.dataIndex;
6576             }
6577             
6578            
6579             
6580             if(typeof(config.align) != 'undefined' && config.align.length){
6581                 c.style += ' text-align:' + config.align + ';';
6582             }
6583             
6584             if(typeof(config.width) != 'undefined'){
6585                 c.style += ' width:' + config.width + 'px;';
6586                 this.totalWidth += config.width;
6587             } else {
6588                 this.totalWidth += 100; // assume minimum of 100 per column?
6589             }
6590             
6591             if(typeof(config.cls) != 'undefined'){
6592                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6593             }
6594             
6595             ['xs','sm','md','lg'].map(function(size){
6596                 
6597                 if(typeof(config[size]) == 'undefined'){
6598                     return;
6599                 }
6600                 
6601                 if (!config[size]) { // 0 = hidden
6602                     c.cls += ' hidden-' + size;
6603                     return;
6604                 }
6605                 
6606                 c.cls += ' col-' + size + '-' + config[size];
6607
6608             });
6609             
6610             header.cn.push(c)
6611         }
6612         
6613         return header;
6614     },
6615     
6616     renderBody : function()
6617     {
6618         var body = {
6619             tag: 'tbody',
6620             cn : [
6621                 {
6622                     tag: 'tr',
6623                     cn : [
6624                         {
6625                             tag : 'td',
6626                             colspan :  this.cm.getColumnCount()
6627                         }
6628                     ]
6629                 }
6630             ]
6631         };
6632         
6633         return body;
6634     },
6635     
6636     renderFooter : function()
6637     {
6638         var footer = {
6639             tag: 'tfoot',
6640             cn : [
6641                 {
6642                     tag: 'tr',
6643                     cn : [
6644                         {
6645                             tag : 'td',
6646                             colspan :  this.cm.getColumnCount()
6647                         }
6648                     ]
6649                 }
6650             ]
6651         };
6652         
6653         return footer;
6654     },
6655     
6656     
6657     
6658     onLoad : function()
6659     {
6660 //        Roo.log('ds onload');
6661         this.clear();
6662         
6663         var _this = this;
6664         var cm = this.cm;
6665         var ds = this.store;
6666         
6667         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6668             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6669             if (_this.store.sortInfo) {
6670                     
6671                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6672                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6673                 }
6674                 
6675                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6676                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6677                 }
6678             }
6679         });
6680         
6681         var tbody =  this.mainBody;
6682               
6683         if(ds.getCount() > 0){
6684             ds.data.each(function(d,rowIndex){
6685                 var row =  this.renderRow(cm, ds, rowIndex);
6686                 
6687                 tbody.createChild(row);
6688                 
6689                 var _this = this;
6690                 
6691                 if(row.cellObjects.length){
6692                     Roo.each(row.cellObjects, function(r){
6693                         _this.renderCellObject(r);
6694                     })
6695                 }
6696                 
6697             }, this);
6698         }
6699         
6700         var tfoot = this.el.select('tfoot', true).first();
6701         
6702         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6703             
6704             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6705             
6706             var total = this.ds.getTotalCount();
6707             
6708             if(this.footer.pageSize < total){
6709                 this.mainFoot.show();
6710             }
6711         }
6712         
6713         Roo.each(this.el.select('tbody td', true).elements, function(e){
6714             e.on('mouseover', _this.onMouseover, _this);
6715         });
6716         
6717         Roo.each(this.el.select('tbody td', true).elements, function(e){
6718             e.on('mouseout', _this.onMouseout, _this);
6719         });
6720         this.fireEvent('rowsrendered', this);
6721         
6722         this.autoSize();
6723     },
6724     
6725     
6726     onUpdate : function(ds,record)
6727     {
6728         this.refreshRow(record);
6729         this.autoSize();
6730     },
6731     
6732     onRemove : function(ds, record, index, isUpdate){
6733         if(isUpdate !== true){
6734             this.fireEvent("beforerowremoved", this, index, record);
6735         }
6736         var bt = this.mainBody.dom;
6737         
6738         var rows = this.el.select('tbody > tr', true).elements;
6739         
6740         if(typeof(rows[index]) != 'undefined'){
6741             bt.removeChild(rows[index].dom);
6742         }
6743         
6744 //        if(bt.rows[index]){
6745 //            bt.removeChild(bt.rows[index]);
6746 //        }
6747         
6748         if(isUpdate !== true){
6749             //this.stripeRows(index);
6750             //this.syncRowHeights(index, index);
6751             //this.layout();
6752             this.fireEvent("rowremoved", this, index, record);
6753         }
6754     },
6755     
6756     onAdd : function(ds, records, rowIndex)
6757     {
6758         //Roo.log('on Add called');
6759         // - note this does not handle multiple adding very well..
6760         var bt = this.mainBody.dom;
6761         for (var i =0 ; i < records.length;i++) {
6762             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6763             //Roo.log(records[i]);
6764             //Roo.log(this.store.getAt(rowIndex+i));
6765             this.insertRow(this.store, rowIndex + i, false);
6766             return;
6767         }
6768         
6769     },
6770     
6771     
6772     refreshRow : function(record){
6773         var ds = this.store, index;
6774         if(typeof record == 'number'){
6775             index = record;
6776             record = ds.getAt(index);
6777         }else{
6778             index = ds.indexOf(record);
6779         }
6780         this.insertRow(ds, index, true);
6781         this.autoSize();
6782         this.onRemove(ds, record, index+1, true);
6783         this.autoSize();
6784         //this.syncRowHeights(index, index);
6785         //this.layout();
6786         this.fireEvent("rowupdated", this, index, record);
6787     },
6788     
6789     insertRow : function(dm, rowIndex, isUpdate){
6790         
6791         if(!isUpdate){
6792             this.fireEvent("beforerowsinserted", this, rowIndex);
6793         }
6794             //var s = this.getScrollState();
6795         var row = this.renderRow(this.cm, this.store, rowIndex);
6796         // insert before rowIndex..
6797         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6798         
6799         var _this = this;
6800                 
6801         if(row.cellObjects.length){
6802             Roo.each(row.cellObjects, function(r){
6803                 _this.renderCellObject(r);
6804             })
6805         }
6806             
6807         if(!isUpdate){
6808             this.fireEvent("rowsinserted", this, rowIndex);
6809             //this.syncRowHeights(firstRow, lastRow);
6810             //this.stripeRows(firstRow);
6811             //this.layout();
6812         }
6813         
6814     },
6815     
6816     
6817     getRowDom : function(rowIndex)
6818     {
6819         var rows = this.el.select('tbody > tr', true).elements;
6820         
6821         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6822         
6823     },
6824     // returns the object tree for a tr..
6825   
6826     
6827     renderRow : function(cm, ds, rowIndex) 
6828     {
6829         var d = ds.getAt(rowIndex);
6830         
6831         var row = {
6832             tag : 'tr',
6833             cls : 'x-row-' + rowIndex,
6834             cn : []
6835         };
6836             
6837         var cellObjects = [];
6838         
6839         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6840             var config = cm.config[i];
6841             
6842             var renderer = cm.getRenderer(i);
6843             var value = '';
6844             var id = false;
6845             
6846             if(typeof(renderer) !== 'undefined'){
6847                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6848             }
6849             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6850             // and are rendered into the cells after the row is rendered - using the id for the element.
6851             
6852             if(typeof(value) === 'object'){
6853                 id = Roo.id();
6854                 cellObjects.push({
6855                     container : id,
6856                     cfg : value 
6857                 })
6858             }
6859             
6860             var rowcfg = {
6861                 record: d,
6862                 rowIndex : rowIndex,
6863                 colIndex : i,
6864                 rowClass : ''
6865             };
6866
6867             this.fireEvent('rowclass', this, rowcfg);
6868             
6869             var td = {
6870                 tag: 'td',
6871                 cls : rowcfg.rowClass + ' x-col-' + i,
6872                 style: '',
6873                 html: (typeof(value) === 'object') ? '' : value
6874             };
6875             
6876             if (id) {
6877                 td.id = id;
6878             }
6879             
6880             if(typeof(config.colspan) != 'undefined'){
6881                 td.colspan = config.colspan;
6882             }
6883             
6884             if(typeof(config.hidden) != 'undefined' && config.hidden){
6885                 td.style += ' display:none;';
6886             }
6887             
6888             if(typeof(config.align) != 'undefined' && config.align.length){
6889                 td.style += ' text-align:' + config.align + ';';
6890             }
6891             if(typeof(config.valign) != 'undefined' && config.valign.length){
6892                 td.style += ' vertical-align:' + config.valign + ';';
6893             }
6894             
6895             if(typeof(config.width) != 'undefined'){
6896                 td.style += ' width:' +  config.width + 'px;';
6897             }
6898             
6899             if(typeof(config.cursor) != 'undefined'){
6900                 td.style += ' cursor:' +  config.cursor + ';';
6901             }
6902             
6903             if(typeof(config.cls) != 'undefined'){
6904                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6905             }
6906             
6907             ['xs','sm','md','lg'].map(function(size){
6908                 
6909                 if(typeof(config[size]) == 'undefined'){
6910                     return;
6911                 }
6912                 
6913                 if (!config[size]) { // 0 = hidden
6914                     td.cls += ' hidden-' + size;
6915                     return;
6916                 }
6917                 
6918                 td.cls += ' col-' + size + '-' + config[size];
6919
6920             });
6921             
6922             row.cn.push(td);
6923            
6924         }
6925         
6926         row.cellObjects = cellObjects;
6927         
6928         return row;
6929           
6930     },
6931     
6932     
6933     
6934     onBeforeLoad : function()
6935     {
6936         
6937     },
6938      /**
6939      * Remove all rows
6940      */
6941     clear : function()
6942     {
6943         this.el.select('tbody', true).first().dom.innerHTML = '';
6944     },
6945     /**
6946      * Show or hide a row.
6947      * @param {Number} rowIndex to show or hide
6948      * @param {Boolean} state hide
6949      */
6950     setRowVisibility : function(rowIndex, state)
6951     {
6952         var bt = this.mainBody.dom;
6953         
6954         var rows = this.el.select('tbody > tr', true).elements;
6955         
6956         if(typeof(rows[rowIndex]) == 'undefined'){
6957             return;
6958         }
6959         rows[rowIndex].dom.style.display = state ? '' : 'none';
6960     },
6961     
6962     
6963     getSelectionModel : function(){
6964         if(!this.selModel){
6965             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6966         }
6967         return this.selModel;
6968     },
6969     /*
6970      * Render the Roo.bootstrap object from renderder
6971      */
6972     renderCellObject : function(r)
6973     {
6974         var _this = this;
6975         
6976         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6977         
6978         var t = r.cfg.render(r.container);
6979         
6980         if(r.cfg.cn){
6981             Roo.each(r.cfg.cn, function(c){
6982                 var child = {
6983                     container: t.getChildContainer(),
6984                     cfg: c
6985                 };
6986                 _this.renderCellObject(child);
6987             })
6988         }
6989     },
6990     
6991     getRowIndex : function(row)
6992     {
6993         var rowIndex = -1;
6994         
6995         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6996             if(el != row){
6997                 return;
6998             }
6999             
7000             rowIndex = index;
7001         });
7002         
7003         return rowIndex;
7004     },
7005      /**
7006      * Returns the grid's underlying element = used by panel.Grid
7007      * @return {Element} The element
7008      */
7009     getGridEl : function(){
7010         return this.el;
7011     },
7012      /**
7013      * Forces a resize - used by panel.Grid
7014      * @return {Element} The element
7015      */
7016     autoSize : function()
7017     {
7018         //var ctr = Roo.get(this.container.dom.parentElement);
7019         var ctr = Roo.get(this.el.dom);
7020         
7021         var thd = this.getGridEl().select('thead',true).first();
7022         var tbd = this.getGridEl().select('tbody', true).first();
7023         var tfd = this.getGridEl().select('tfoot', true).first();
7024         
7025         var cw = ctr.getWidth();
7026         
7027         if (tbd) {
7028             
7029             tbd.setSize(ctr.getWidth(),
7030                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7031             );
7032             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7033             cw -= barsize;
7034         }
7035         cw = Math.max(cw, this.totalWidth);
7036         this.getGridEl().select('tr',true).setWidth(cw);
7037         // resize 'expandable coloumn?
7038         
7039         return; // we doe not have a view in this design..
7040         
7041     },
7042     onBodyScroll: function()
7043     {
7044         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7045         if(this.mainHead){
7046             this.mainHead.setStyle({
7047                 'position' : 'relative',
7048                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7049             });
7050         }
7051         
7052         if(this.lazyLoad){
7053             
7054             var scrollHeight = this.mainBody.dom.scrollHeight;
7055             
7056             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7057             
7058             var height = this.mainBody.getHeight();
7059             
7060             if(scrollHeight - height == scrollTop) {
7061                 
7062                 var total = this.ds.getTotalCount();
7063                 
7064                 if(this.footer.cursor + this.footer.pageSize < total){
7065                     
7066                     this.footer.ds.load({
7067                         params : {
7068                             start : this.footer.cursor + this.footer.pageSize,
7069                             limit : this.footer.pageSize
7070                         },
7071                         add : true
7072                     });
7073                 }
7074             }
7075             
7076         }
7077     },
7078     
7079     onHeaderChange : function()
7080     {
7081         var header = this.renderHeader();
7082         var table = this.el.select('table', true).first();
7083         
7084         this.mainHead.remove();
7085         this.mainHead = table.createChild(header, this.mainBody, false);
7086     },
7087     
7088     onHiddenChange : function(colModel, colIndex, hidden)
7089     {
7090         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7091         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7092         
7093         this.CSS.updateRule(thSelector, "display", "");
7094         this.CSS.updateRule(tdSelector, "display", "");
7095         
7096         if(hidden){
7097             this.CSS.updateRule(thSelector, "display", "none");
7098             this.CSS.updateRule(tdSelector, "display", "none");
7099         }
7100         
7101         this.onHeaderChange();
7102         this.onLoad();
7103         
7104     }
7105     
7106 });
7107
7108  
7109
7110  /*
7111  * - LGPL
7112  *
7113  * table cell
7114  * 
7115  */
7116
7117 /**
7118  * @class Roo.bootstrap.TableCell
7119  * @extends Roo.bootstrap.Component
7120  * Bootstrap TableCell class
7121  * @cfg {String} html cell contain text
7122  * @cfg {String} cls cell class
7123  * @cfg {String} tag cell tag (td|th) default td
7124  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7125  * @cfg {String} align Aligns the content in a cell
7126  * @cfg {String} axis Categorizes cells
7127  * @cfg {String} bgcolor Specifies the background color of a cell
7128  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7129  * @cfg {Number} colspan Specifies the number of columns a cell should span
7130  * @cfg {String} headers Specifies one or more header cells a cell is related to
7131  * @cfg {Number} height Sets the height of a cell
7132  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7133  * @cfg {Number} rowspan Sets the number of rows a cell should span
7134  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7135  * @cfg {String} valign Vertical aligns the content in a cell
7136  * @cfg {Number} width Specifies the width of a cell
7137  * 
7138  * @constructor
7139  * Create a new TableCell
7140  * @param {Object} config The config object
7141  */
7142
7143 Roo.bootstrap.TableCell = function(config){
7144     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7145 };
7146
7147 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7148     
7149     html: false,
7150     cls: false,
7151     tag: false,
7152     abbr: false,
7153     align: false,
7154     axis: false,
7155     bgcolor: false,
7156     charoff: false,
7157     colspan: false,
7158     headers: false,
7159     height: false,
7160     nowrap: false,
7161     rowspan: false,
7162     scope: false,
7163     valign: false,
7164     width: false,
7165     
7166     
7167     getAutoCreate : function(){
7168         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7169         
7170         cfg = {
7171             tag: 'td'
7172         };
7173         
7174         if(this.tag){
7175             cfg.tag = this.tag;
7176         }
7177         
7178         if (this.html) {
7179             cfg.html=this.html
7180         }
7181         if (this.cls) {
7182             cfg.cls=this.cls
7183         }
7184         if (this.abbr) {
7185             cfg.abbr=this.abbr
7186         }
7187         if (this.align) {
7188             cfg.align=this.align
7189         }
7190         if (this.axis) {
7191             cfg.axis=this.axis
7192         }
7193         if (this.bgcolor) {
7194             cfg.bgcolor=this.bgcolor
7195         }
7196         if (this.charoff) {
7197             cfg.charoff=this.charoff
7198         }
7199         if (this.colspan) {
7200             cfg.colspan=this.colspan
7201         }
7202         if (this.headers) {
7203             cfg.headers=this.headers
7204         }
7205         if (this.height) {
7206             cfg.height=this.height
7207         }
7208         if (this.nowrap) {
7209             cfg.nowrap=this.nowrap
7210         }
7211         if (this.rowspan) {
7212             cfg.rowspan=this.rowspan
7213         }
7214         if (this.scope) {
7215             cfg.scope=this.scope
7216         }
7217         if (this.valign) {
7218             cfg.valign=this.valign
7219         }
7220         if (this.width) {
7221             cfg.width=this.width
7222         }
7223         
7224         
7225         return cfg;
7226     }
7227    
7228 });
7229
7230  
7231
7232  /*
7233  * - LGPL
7234  *
7235  * table row
7236  * 
7237  */
7238
7239 /**
7240  * @class Roo.bootstrap.TableRow
7241  * @extends Roo.bootstrap.Component
7242  * Bootstrap TableRow class
7243  * @cfg {String} cls row class
7244  * @cfg {String} align Aligns the content in a table row
7245  * @cfg {String} bgcolor Specifies a background color for a table row
7246  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7247  * @cfg {String} valign Vertical aligns the content in a table row
7248  * 
7249  * @constructor
7250  * Create a new TableRow
7251  * @param {Object} config The config object
7252  */
7253
7254 Roo.bootstrap.TableRow = function(config){
7255     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7256 };
7257
7258 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7259     
7260     cls: false,
7261     align: false,
7262     bgcolor: false,
7263     charoff: false,
7264     valign: false,
7265     
7266     getAutoCreate : function(){
7267         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7268         
7269         cfg = {
7270             tag: 'tr'
7271         };
7272             
7273         if(this.cls){
7274             cfg.cls = this.cls;
7275         }
7276         if(this.align){
7277             cfg.align = this.align;
7278         }
7279         if(this.bgcolor){
7280             cfg.bgcolor = this.bgcolor;
7281         }
7282         if(this.charoff){
7283             cfg.charoff = this.charoff;
7284         }
7285         if(this.valign){
7286             cfg.valign = this.valign;
7287         }
7288         
7289         return cfg;
7290     }
7291    
7292 });
7293
7294  
7295
7296  /*
7297  * - LGPL
7298  *
7299  * table body
7300  * 
7301  */
7302
7303 /**
7304  * @class Roo.bootstrap.TableBody
7305  * @extends Roo.bootstrap.Component
7306  * Bootstrap TableBody class
7307  * @cfg {String} cls element class
7308  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7309  * @cfg {String} align Aligns the content inside the element
7310  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7311  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7312  * 
7313  * @constructor
7314  * Create a new TableBody
7315  * @param {Object} config The config object
7316  */
7317
7318 Roo.bootstrap.TableBody = function(config){
7319     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7320 };
7321
7322 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7323     
7324     cls: false,
7325     tag: false,
7326     align: false,
7327     charoff: false,
7328     valign: false,
7329     
7330     getAutoCreate : function(){
7331         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7332         
7333         cfg = {
7334             tag: 'tbody'
7335         };
7336             
7337         if (this.cls) {
7338             cfg.cls=this.cls
7339         }
7340         if(this.tag){
7341             cfg.tag = this.tag;
7342         }
7343         
7344         if(this.align){
7345             cfg.align = this.align;
7346         }
7347         if(this.charoff){
7348             cfg.charoff = this.charoff;
7349         }
7350         if(this.valign){
7351             cfg.valign = this.valign;
7352         }
7353         
7354         return cfg;
7355     }
7356     
7357     
7358 //    initEvents : function()
7359 //    {
7360 //        
7361 //        if(!this.store){
7362 //            return;
7363 //        }
7364 //        
7365 //        this.store = Roo.factory(this.store, Roo.data);
7366 //        this.store.on('load', this.onLoad, this);
7367 //        
7368 //        this.store.load();
7369 //        
7370 //    },
7371 //    
7372 //    onLoad: function () 
7373 //    {   
7374 //        this.fireEvent('load', this);
7375 //    }
7376 //    
7377 //   
7378 });
7379
7380  
7381
7382  /*
7383  * Based on:
7384  * Ext JS Library 1.1.1
7385  * Copyright(c) 2006-2007, Ext JS, LLC.
7386  *
7387  * Originally Released Under LGPL - original licence link has changed is not relivant.
7388  *
7389  * Fork - LGPL
7390  * <script type="text/javascript">
7391  */
7392
7393 // as we use this in bootstrap.
7394 Roo.namespace('Roo.form');
7395  /**
7396  * @class Roo.form.Action
7397  * Internal Class used to handle form actions
7398  * @constructor
7399  * @param {Roo.form.BasicForm} el The form element or its id
7400  * @param {Object} config Configuration options
7401  */
7402
7403  
7404  
7405 // define the action interface
7406 Roo.form.Action = function(form, options){
7407     this.form = form;
7408     this.options = options || {};
7409 };
7410 /**
7411  * Client Validation Failed
7412  * @const 
7413  */
7414 Roo.form.Action.CLIENT_INVALID = 'client';
7415 /**
7416  * Server Validation Failed
7417  * @const 
7418  */
7419 Roo.form.Action.SERVER_INVALID = 'server';
7420  /**
7421  * Connect to Server Failed
7422  * @const 
7423  */
7424 Roo.form.Action.CONNECT_FAILURE = 'connect';
7425 /**
7426  * Reading Data from Server Failed
7427  * @const 
7428  */
7429 Roo.form.Action.LOAD_FAILURE = 'load';
7430
7431 Roo.form.Action.prototype = {
7432     type : 'default',
7433     failureType : undefined,
7434     response : undefined,
7435     result : undefined,
7436
7437     // interface method
7438     run : function(options){
7439
7440     },
7441
7442     // interface method
7443     success : function(response){
7444
7445     },
7446
7447     // interface method
7448     handleResponse : function(response){
7449
7450     },
7451
7452     // default connection failure
7453     failure : function(response){
7454         
7455         this.response = response;
7456         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7457         this.form.afterAction(this, false);
7458     },
7459
7460     processResponse : function(response){
7461         this.response = response;
7462         if(!response.responseText){
7463             return true;
7464         }
7465         this.result = this.handleResponse(response);
7466         return this.result;
7467     },
7468
7469     // utility functions used internally
7470     getUrl : function(appendParams){
7471         var url = this.options.url || this.form.url || this.form.el.dom.action;
7472         if(appendParams){
7473             var p = this.getParams();
7474             if(p){
7475                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7476             }
7477         }
7478         return url;
7479     },
7480
7481     getMethod : function(){
7482         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7483     },
7484
7485     getParams : function(){
7486         var bp = this.form.baseParams;
7487         var p = this.options.params;
7488         if(p){
7489             if(typeof p == "object"){
7490                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7491             }else if(typeof p == 'string' && bp){
7492                 p += '&' + Roo.urlEncode(bp);
7493             }
7494         }else if(bp){
7495             p = Roo.urlEncode(bp);
7496         }
7497         return p;
7498     },
7499
7500     createCallback : function(){
7501         return {
7502             success: this.success,
7503             failure: this.failure,
7504             scope: this,
7505             timeout: (this.form.timeout*1000),
7506             upload: this.form.fileUpload ? this.success : undefined
7507         };
7508     }
7509 };
7510
7511 Roo.form.Action.Submit = function(form, options){
7512     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7513 };
7514
7515 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7516     type : 'submit',
7517
7518     haveProgress : false,
7519     uploadComplete : false,
7520     
7521     // uploadProgress indicator.
7522     uploadProgress : function()
7523     {
7524         if (!this.form.progressUrl) {
7525             return;
7526         }
7527         
7528         if (!this.haveProgress) {
7529             Roo.MessageBox.progress("Uploading", "Uploading");
7530         }
7531         if (this.uploadComplete) {
7532            Roo.MessageBox.hide();
7533            return;
7534         }
7535         
7536         this.haveProgress = true;
7537    
7538         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7539         
7540         var c = new Roo.data.Connection();
7541         c.request({
7542             url : this.form.progressUrl,
7543             params: {
7544                 id : uid
7545             },
7546             method: 'GET',
7547             success : function(req){
7548                //console.log(data);
7549                 var rdata = false;
7550                 var edata;
7551                 try  {
7552                    rdata = Roo.decode(req.responseText)
7553                 } catch (e) {
7554                     Roo.log("Invalid data from server..");
7555                     Roo.log(edata);
7556                     return;
7557                 }
7558                 if (!rdata || !rdata.success) {
7559                     Roo.log(rdata);
7560                     Roo.MessageBox.alert(Roo.encode(rdata));
7561                     return;
7562                 }
7563                 var data = rdata.data;
7564                 
7565                 if (this.uploadComplete) {
7566                    Roo.MessageBox.hide();
7567                    return;
7568                 }
7569                    
7570                 if (data){
7571                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7572                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7573                     );
7574                 }
7575                 this.uploadProgress.defer(2000,this);
7576             },
7577        
7578             failure: function(data) {
7579                 Roo.log('progress url failed ');
7580                 Roo.log(data);
7581             },
7582             scope : this
7583         });
7584            
7585     },
7586     
7587     
7588     run : function()
7589     {
7590         // run get Values on the form, so it syncs any secondary forms.
7591         this.form.getValues();
7592         
7593         var o = this.options;
7594         var method = this.getMethod();
7595         var isPost = method == 'POST';
7596         if(o.clientValidation === false || this.form.isValid()){
7597             
7598             if (this.form.progressUrl) {
7599                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7600                     (new Date() * 1) + '' + Math.random());
7601                     
7602             } 
7603             
7604             
7605             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7606                 form:this.form.el.dom,
7607                 url:this.getUrl(!isPost),
7608                 method: method,
7609                 params:isPost ? this.getParams() : null,
7610                 isUpload: this.form.fileUpload
7611             }));
7612             
7613             this.uploadProgress();
7614
7615         }else if (o.clientValidation !== false){ // client validation failed
7616             this.failureType = Roo.form.Action.CLIENT_INVALID;
7617             this.form.afterAction(this, false);
7618         }
7619     },
7620
7621     success : function(response)
7622     {
7623         this.uploadComplete= true;
7624         if (this.haveProgress) {
7625             Roo.MessageBox.hide();
7626         }
7627         
7628         
7629         var result = this.processResponse(response);
7630         if(result === true || result.success){
7631             this.form.afterAction(this, true);
7632             return;
7633         }
7634         if(result.errors){
7635             this.form.markInvalid(result.errors);
7636             this.failureType = Roo.form.Action.SERVER_INVALID;
7637         }
7638         this.form.afterAction(this, false);
7639     },
7640     failure : function(response)
7641     {
7642         this.uploadComplete= true;
7643         if (this.haveProgress) {
7644             Roo.MessageBox.hide();
7645         }
7646         
7647         this.response = response;
7648         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7649         this.form.afterAction(this, false);
7650     },
7651     
7652     handleResponse : function(response){
7653         if(this.form.errorReader){
7654             var rs = this.form.errorReader.read(response);
7655             var errors = [];
7656             if(rs.records){
7657                 for(var i = 0, len = rs.records.length; i < len; i++) {
7658                     var r = rs.records[i];
7659                     errors[i] = r.data;
7660                 }
7661             }
7662             if(errors.length < 1){
7663                 errors = null;
7664             }
7665             return {
7666                 success : rs.success,
7667                 errors : errors
7668             };
7669         }
7670         var ret = false;
7671         try {
7672             ret = Roo.decode(response.responseText);
7673         } catch (e) {
7674             ret = {
7675                 success: false,
7676                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7677                 errors : []
7678             };
7679         }
7680         return ret;
7681         
7682     }
7683 });
7684
7685
7686 Roo.form.Action.Load = function(form, options){
7687     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7688     this.reader = this.form.reader;
7689 };
7690
7691 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7692     type : 'load',
7693
7694     run : function(){
7695         
7696         Roo.Ajax.request(Roo.apply(
7697                 this.createCallback(), {
7698                     method:this.getMethod(),
7699                     url:this.getUrl(false),
7700                     params:this.getParams()
7701         }));
7702     },
7703
7704     success : function(response){
7705         
7706         var result = this.processResponse(response);
7707         if(result === true || !result.success || !result.data){
7708             this.failureType = Roo.form.Action.LOAD_FAILURE;
7709             this.form.afterAction(this, false);
7710             return;
7711         }
7712         this.form.clearInvalid();
7713         this.form.setValues(result.data);
7714         this.form.afterAction(this, true);
7715     },
7716
7717     handleResponse : function(response){
7718         if(this.form.reader){
7719             var rs = this.form.reader.read(response);
7720             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7721             return {
7722                 success : rs.success,
7723                 data : data
7724             };
7725         }
7726         return Roo.decode(response.responseText);
7727     }
7728 });
7729
7730 Roo.form.Action.ACTION_TYPES = {
7731     'load' : Roo.form.Action.Load,
7732     'submit' : Roo.form.Action.Submit
7733 };/*
7734  * - LGPL
7735  *
7736  * form
7737  *
7738  */
7739
7740 /**
7741  * @class Roo.bootstrap.Form
7742  * @extends Roo.bootstrap.Component
7743  * Bootstrap Form class
7744  * @cfg {String} method  GET | POST (default POST)
7745  * @cfg {String} labelAlign top | left (default top)
7746  * @cfg {String} align left  | right - for navbars
7747  * @cfg {Boolean} loadMask load mask when submit (default true)
7748
7749  *
7750  * @constructor
7751  * Create a new Form
7752  * @param {Object} config The config object
7753  */
7754
7755
7756 Roo.bootstrap.Form = function(config){
7757     
7758     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7759     
7760     Roo.bootstrap.Form.popover.apply();
7761     
7762     this.addEvents({
7763         /**
7764          * @event clientvalidation
7765          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7766          * @param {Form} this
7767          * @param {Boolean} valid true if the form has passed client-side validation
7768          */
7769         clientvalidation: true,
7770         /**
7771          * @event beforeaction
7772          * Fires before any action is performed. Return false to cancel the action.
7773          * @param {Form} this
7774          * @param {Action} action The action to be performed
7775          */
7776         beforeaction: true,
7777         /**
7778          * @event actionfailed
7779          * Fires when an action fails.
7780          * @param {Form} this
7781          * @param {Action} action The action that failed
7782          */
7783         actionfailed : true,
7784         /**
7785          * @event actioncomplete
7786          * Fires when an action is completed.
7787          * @param {Form} this
7788          * @param {Action} action The action that completed
7789          */
7790         actioncomplete : true
7791     });
7792 };
7793
7794 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7795
7796      /**
7797      * @cfg {String} method
7798      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7799      */
7800     method : 'POST',
7801     /**
7802      * @cfg {String} url
7803      * The URL to use for form actions if one isn't supplied in the action options.
7804      */
7805     /**
7806      * @cfg {Boolean} fileUpload
7807      * Set to true if this form is a file upload.
7808      */
7809
7810     /**
7811      * @cfg {Object} baseParams
7812      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7813      */
7814
7815     /**
7816      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7817      */
7818     timeout: 30,
7819     /**
7820      * @cfg {Sting} align (left|right) for navbar forms
7821      */
7822     align : 'left',
7823
7824     // private
7825     activeAction : null,
7826
7827     /**
7828      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7829      * element by passing it or its id or mask the form itself by passing in true.
7830      * @type Mixed
7831      */
7832     waitMsgTarget : false,
7833
7834     loadMask : true,
7835     
7836     /**
7837      * @cfg {Boolean} errorMask (true|false) default false
7838      */
7839     errorMask : false,
7840     
7841     /**
7842      * @cfg {Number} maskOffset Default 100
7843      */
7844     maskOffset : 100,
7845     
7846     /**
7847      * @cfg {Boolean} maskBody
7848      */
7849     maskBody : false,
7850
7851     getAutoCreate : function(){
7852
7853         var cfg = {
7854             tag: 'form',
7855             method : this.method || 'POST',
7856             id : this.id || Roo.id(),
7857             cls : ''
7858         };
7859         if (this.parent().xtype.match(/^Nav/)) {
7860             cfg.cls = 'navbar-form navbar-' + this.align;
7861
7862         }
7863
7864         if (this.labelAlign == 'left' ) {
7865             cfg.cls += ' form-horizontal';
7866         }
7867
7868
7869         return cfg;
7870     },
7871     initEvents : function()
7872     {
7873         this.el.on('submit', this.onSubmit, this);
7874         // this was added as random key presses on the form where triggering form submit.
7875         this.el.on('keypress', function(e) {
7876             if (e.getCharCode() != 13) {
7877                 return true;
7878             }
7879             // we might need to allow it for textareas.. and some other items.
7880             // check e.getTarget().
7881
7882             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7883                 return true;
7884             }
7885
7886             Roo.log("keypress blocked");
7887
7888             e.preventDefault();
7889             return false;
7890         });
7891         
7892     },
7893     // private
7894     onSubmit : function(e){
7895         e.stopEvent();
7896     },
7897
7898      /**
7899      * Returns true if client-side validation on the form is successful.
7900      * @return Boolean
7901      */
7902     isValid : function(){
7903         var items = this.getItems();
7904         var valid = true;
7905         var target = false;
7906         
7907         items.each(function(f){
7908             
7909             if(f.validate()){
7910                 return;
7911             }
7912             
7913             Roo.log('invalid field: ' + f.name);
7914             
7915             valid = false;
7916
7917             if(!target && f.el.isVisible(true)){
7918                 target = f;
7919             }
7920            
7921         });
7922         
7923         if(this.errorMask && !valid){
7924             Roo.bootstrap.Form.popover.mask(this, target);
7925         }
7926         
7927         return valid;
7928     },
7929     
7930     /**
7931      * Returns true if any fields in this form have changed since their original load.
7932      * @return Boolean
7933      */
7934     isDirty : function(){
7935         var dirty = false;
7936         var items = this.getItems();
7937         items.each(function(f){
7938            if(f.isDirty()){
7939                dirty = true;
7940                return false;
7941            }
7942            return true;
7943         });
7944         return dirty;
7945     },
7946      /**
7947      * Performs a predefined action (submit or load) or custom actions you define on this form.
7948      * @param {String} actionName The name of the action type
7949      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7950      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7951      * accept other config options):
7952      * <pre>
7953 Property          Type             Description
7954 ----------------  ---------------  ----------------------------------------------------------------------------------
7955 url               String           The url for the action (defaults to the form's url)
7956 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7957 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7958 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7959                                    validate the form on the client (defaults to false)
7960      * </pre>
7961      * @return {BasicForm} this
7962      */
7963     doAction : function(action, options){
7964         if(typeof action == 'string'){
7965             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7966         }
7967         if(this.fireEvent('beforeaction', this, action) !== false){
7968             this.beforeAction(action);
7969             action.run.defer(100, action);
7970         }
7971         return this;
7972     },
7973
7974     // private
7975     beforeAction : function(action){
7976         var o = action.options;
7977         
7978         if(this.loadMask){
7979             
7980             if(this.maskBody){
7981                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7982             } else {
7983                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7984             }
7985         }
7986         // not really supported yet.. ??
7987
7988         //if(this.waitMsgTarget === true){
7989         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7990         //}else if(this.waitMsgTarget){
7991         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7992         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7993         //}else {
7994         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7995        // }
7996
7997     },
7998
7999     // private
8000     afterAction : function(action, success){
8001         this.activeAction = null;
8002         var o = action.options;
8003
8004         if(this.loadMask){
8005             
8006             if(this.maskBody){
8007                 Roo.get(document.body).unmask();
8008             } else {
8009                 this.el.unmask();
8010             }
8011         }
8012         
8013         //if(this.waitMsgTarget === true){
8014 //            this.el.unmask();
8015         //}else if(this.waitMsgTarget){
8016         //    this.waitMsgTarget.unmask();
8017         //}else{
8018         //    Roo.MessageBox.updateProgress(1);
8019         //    Roo.MessageBox.hide();
8020        // }
8021         //
8022         if(success){
8023             if(o.reset){
8024                 this.reset();
8025             }
8026             Roo.callback(o.success, o.scope, [this, action]);
8027             this.fireEvent('actioncomplete', this, action);
8028
8029         }else{
8030
8031             // failure condition..
8032             // we have a scenario where updates need confirming.
8033             // eg. if a locking scenario exists..
8034             // we look for { errors : { needs_confirm : true }} in the response.
8035             if (
8036                 (typeof(action.result) != 'undefined')  &&
8037                 (typeof(action.result.errors) != 'undefined')  &&
8038                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8039            ){
8040                 var _t = this;
8041                 Roo.log("not supported yet");
8042                  /*
8043
8044                 Roo.MessageBox.confirm(
8045                     "Change requires confirmation",
8046                     action.result.errorMsg,
8047                     function(r) {
8048                         if (r != 'yes') {
8049                             return;
8050                         }
8051                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8052                     }
8053
8054                 );
8055                 */
8056
8057
8058                 return;
8059             }
8060
8061             Roo.callback(o.failure, o.scope, [this, action]);
8062             // show an error message if no failed handler is set..
8063             if (!this.hasListener('actionfailed')) {
8064                 Roo.log("need to add dialog support");
8065                 /*
8066                 Roo.MessageBox.alert("Error",
8067                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8068                         action.result.errorMsg :
8069                         "Saving Failed, please check your entries or try again"
8070                 );
8071                 */
8072             }
8073
8074             this.fireEvent('actionfailed', this, action);
8075         }
8076
8077     },
8078     /**
8079      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8080      * @param {String} id The value to search for
8081      * @return Field
8082      */
8083     findField : function(id){
8084         var items = this.getItems();
8085         var field = items.get(id);
8086         if(!field){
8087              items.each(function(f){
8088                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8089                     field = f;
8090                     return false;
8091                 }
8092                 return true;
8093             });
8094         }
8095         return field || null;
8096     },
8097      /**
8098      * Mark fields in this form invalid in bulk.
8099      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8100      * @return {BasicForm} this
8101      */
8102     markInvalid : function(errors){
8103         if(errors instanceof Array){
8104             for(var i = 0, len = errors.length; i < len; i++){
8105                 var fieldError = errors[i];
8106                 var f = this.findField(fieldError.id);
8107                 if(f){
8108                     f.markInvalid(fieldError.msg);
8109                 }
8110             }
8111         }else{
8112             var field, id;
8113             for(id in errors){
8114                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8115                     field.markInvalid(errors[id]);
8116                 }
8117             }
8118         }
8119         //Roo.each(this.childForms || [], function (f) {
8120         //    f.markInvalid(errors);
8121         //});
8122
8123         return this;
8124     },
8125
8126     /**
8127      * Set values for fields in this form in bulk.
8128      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8129      * @return {BasicForm} this
8130      */
8131     setValues : function(values){
8132         if(values instanceof Array){ // array of objects
8133             for(var i = 0, len = values.length; i < len; i++){
8134                 var v = values[i];
8135                 var f = this.findField(v.id);
8136                 if(f){
8137                     f.setValue(v.value);
8138                     if(this.trackResetOnLoad){
8139                         f.originalValue = f.getValue();
8140                     }
8141                 }
8142             }
8143         }else{ // object hash
8144             var field, id;
8145             for(id in values){
8146                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8147
8148                     if (field.setFromData &&
8149                         field.valueField &&
8150                         field.displayField &&
8151                         // combos' with local stores can
8152                         // be queried via setValue()
8153                         // to set their value..
8154                         (field.store && !field.store.isLocal)
8155                         ) {
8156                         // it's a combo
8157                         var sd = { };
8158                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8159                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8160                         field.setFromData(sd);
8161
8162                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8163                         
8164                         field.setFromData(values);
8165                         
8166                     } else {
8167                         field.setValue(values[id]);
8168                     }
8169
8170
8171                     if(this.trackResetOnLoad){
8172                         field.originalValue = field.getValue();
8173                     }
8174                 }
8175             }
8176         }
8177
8178         //Roo.each(this.childForms || [], function (f) {
8179         //    f.setValues(values);
8180         //});
8181
8182         return this;
8183     },
8184
8185     /**
8186      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8187      * they are returned as an array.
8188      * @param {Boolean} asString
8189      * @return {Object}
8190      */
8191     getValues : function(asString){
8192         //if (this.childForms) {
8193             // copy values from the child forms
8194         //    Roo.each(this.childForms, function (f) {
8195         //        this.setValues(f.getValues());
8196         //    }, this);
8197         //}
8198
8199
8200
8201         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8202         if(asString === true){
8203             return fs;
8204         }
8205         return Roo.urlDecode(fs);
8206     },
8207
8208     /**
8209      * Returns the fields in this form as an object with key/value pairs.
8210      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8211      * @return {Object}
8212      */
8213     getFieldValues : function(with_hidden)
8214     {
8215         var items = this.getItems();
8216         var ret = {};
8217         items.each(function(f){
8218             
8219             if (!f.getName()) {
8220                 return;
8221             }
8222             
8223             var v = f.getValue();
8224             
8225             if (f.inputType =='radio') {
8226                 if (typeof(ret[f.getName()]) == 'undefined') {
8227                     ret[f.getName()] = ''; // empty..
8228                 }
8229
8230                 if (!f.el.dom.checked) {
8231                     return;
8232
8233                 }
8234                 v = f.el.dom.value;
8235
8236             }
8237             
8238             if(f.xtype == 'MoneyField'){
8239                 ret[f.currencyName] = f.getCurrency();
8240             }
8241
8242             // not sure if this supported any more..
8243             if ((typeof(v) == 'object') && f.getRawValue) {
8244                 v = f.getRawValue() ; // dates..
8245             }
8246             // combo boxes where name != hiddenName...
8247             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8248                 ret[f.name] = f.getRawValue();
8249             }
8250             ret[f.getName()] = v;
8251         });
8252
8253         return ret;
8254     },
8255
8256     /**
8257      * Clears all invalid messages in this form.
8258      * @return {BasicForm} this
8259      */
8260     clearInvalid : function(){
8261         var items = this.getItems();
8262
8263         items.each(function(f){
8264            f.clearInvalid();
8265         });
8266
8267         return this;
8268     },
8269
8270     /**
8271      * Resets this form.
8272      * @return {BasicForm} this
8273      */
8274     reset : function(){
8275         var items = this.getItems();
8276         items.each(function(f){
8277             f.reset();
8278         });
8279
8280         Roo.each(this.childForms || [], function (f) {
8281             f.reset();
8282         });
8283
8284
8285         return this;
8286     },
8287     
8288     getItems : function()
8289     {
8290         var r=new Roo.util.MixedCollection(false, function(o){
8291             return o.id || (o.id = Roo.id());
8292         });
8293         var iter = function(el) {
8294             if (el.inputEl) {
8295                 r.add(el);
8296             }
8297             if (!el.items) {
8298                 return;
8299             }
8300             Roo.each(el.items,function(e) {
8301                 iter(e);
8302             });
8303         };
8304
8305         iter(this);
8306         return r;
8307     },
8308     
8309     hideFields : function(items)
8310     {
8311         Roo.each(items, function(i){
8312             
8313             var f = this.findField(i);
8314             
8315             if(!f){
8316                 return;
8317             }
8318             
8319             if(f.xtype == 'DateField'){
8320                 f.setVisible(false);
8321                 return;
8322             }
8323             
8324             f.hide();
8325             
8326         }, this);
8327     },
8328     
8329     showFields : function(items)
8330     {
8331         Roo.each(items, function(i){
8332             
8333             var f = this.findField(i);
8334             
8335             if(!f){
8336                 return;
8337             }
8338             
8339             if(f.xtype == 'DateField'){
8340                 f.setVisible(true);
8341                 return;
8342             }
8343             
8344             f.show();
8345             
8346         }, this);
8347     }
8348
8349 });
8350
8351 Roo.apply(Roo.bootstrap.Form, {
8352     
8353     popover : {
8354         
8355         padding : 5,
8356         
8357         isApplied : false,
8358         
8359         isMasked : false,
8360         
8361         form : false,
8362         
8363         target : false,
8364         
8365         toolTip : false,
8366         
8367         intervalID : false,
8368         
8369         maskEl : false,
8370         
8371         apply : function()
8372         {
8373             if(this.isApplied){
8374                 return;
8375             }
8376             
8377             this.maskEl = {
8378                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8379                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8380                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8381                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8382             };
8383             
8384             this.maskEl.top.enableDisplayMode("block");
8385             this.maskEl.left.enableDisplayMode("block");
8386             this.maskEl.bottom.enableDisplayMode("block");
8387             this.maskEl.right.enableDisplayMode("block");
8388             
8389             this.toolTip = new Roo.bootstrap.Tooltip({
8390                 cls : 'roo-form-error-popover',
8391                 alignment : {
8392                     'left' : ['r-l', [-2,0], 'right'],
8393                     'right' : ['l-r', [2,0], 'left'],
8394                     'bottom' : ['tl-bl', [0,2], 'top'],
8395                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8396                 }
8397             });
8398             
8399             this.toolTip.render(Roo.get(document.body));
8400
8401             this.toolTip.el.enableDisplayMode("block");
8402             
8403             Roo.get(document.body).on('click', function(){
8404                 this.unmask();
8405             }, this);
8406             
8407             Roo.get(document.body).on('touchstart', function(){
8408                 this.unmask();
8409             }, this);
8410             
8411             this.isApplied = true
8412         },
8413         
8414         mask : function(form, target)
8415         {
8416             this.form = form;
8417             
8418             this.target = target;
8419             
8420             if(!this.form.errorMask || !target.el){
8421                 return;
8422             }
8423             
8424             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8425             
8426             Roo.log(scrollable);
8427             
8428             var ot = this.target.el.calcOffsetsTo(scrollable);
8429             
8430             var scrollTo = ot[1] - this.form.maskOffset;
8431             
8432             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8433             
8434             scrollable.scrollTo('top', scrollTo);
8435             
8436             var box = this.target.el.getBox();
8437             Roo.log(box);
8438             var zIndex = Roo.bootstrap.Modal.zIndex++;
8439
8440             
8441             this.maskEl.top.setStyle('position', 'absolute');
8442             this.maskEl.top.setStyle('z-index', zIndex);
8443             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8444             this.maskEl.top.setLeft(0);
8445             this.maskEl.top.setTop(0);
8446             this.maskEl.top.show();
8447             
8448             this.maskEl.left.setStyle('position', 'absolute');
8449             this.maskEl.left.setStyle('z-index', zIndex);
8450             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8451             this.maskEl.left.setLeft(0);
8452             this.maskEl.left.setTop(box.y - this.padding);
8453             this.maskEl.left.show();
8454
8455             this.maskEl.bottom.setStyle('position', 'absolute');
8456             this.maskEl.bottom.setStyle('z-index', zIndex);
8457             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8458             this.maskEl.bottom.setLeft(0);
8459             this.maskEl.bottom.setTop(box.bottom + this.padding);
8460             this.maskEl.bottom.show();
8461
8462             this.maskEl.right.setStyle('position', 'absolute');
8463             this.maskEl.right.setStyle('z-index', zIndex);
8464             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8465             this.maskEl.right.setLeft(box.right + this.padding);
8466             this.maskEl.right.setTop(box.y - this.padding);
8467             this.maskEl.right.show();
8468
8469             this.toolTip.bindEl = this.target.el;
8470
8471             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8472
8473             var tip = this.target.blankText;
8474
8475             if(this.target.getValue() !== '' ) {
8476                 
8477                 if (this.target.invalidText.length) {
8478                     tip = this.target.invalidText;
8479                 } else if (this.target.regexText.length){
8480                     tip = this.target.regexText;
8481                 }
8482             }
8483
8484             this.toolTip.show(tip);
8485
8486             this.intervalID = window.setInterval(function() {
8487                 Roo.bootstrap.Form.popover.unmask();
8488             }, 10000);
8489
8490             window.onwheel = function(){ return false;};
8491             
8492             (function(){ this.isMasked = true; }).defer(500, this);
8493             
8494         },
8495         
8496         unmask : function()
8497         {
8498             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8499                 return;
8500             }
8501             
8502             this.maskEl.top.setStyle('position', 'absolute');
8503             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8504             this.maskEl.top.hide();
8505
8506             this.maskEl.left.setStyle('position', 'absolute');
8507             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8508             this.maskEl.left.hide();
8509
8510             this.maskEl.bottom.setStyle('position', 'absolute');
8511             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8512             this.maskEl.bottom.hide();
8513
8514             this.maskEl.right.setStyle('position', 'absolute');
8515             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8516             this.maskEl.right.hide();
8517             
8518             this.toolTip.hide();
8519             
8520             this.toolTip.el.hide();
8521             
8522             window.onwheel = function(){ return true;};
8523             
8524             if(this.intervalID){
8525                 window.clearInterval(this.intervalID);
8526                 this.intervalID = false;
8527             }
8528             
8529             this.isMasked = false;
8530             
8531         }
8532         
8533     }
8534     
8535 });
8536
8537 /*
8538  * Based on:
8539  * Ext JS Library 1.1.1
8540  * Copyright(c) 2006-2007, Ext JS, LLC.
8541  *
8542  * Originally Released Under LGPL - original licence link has changed is not relivant.
8543  *
8544  * Fork - LGPL
8545  * <script type="text/javascript">
8546  */
8547 /**
8548  * @class Roo.form.VTypes
8549  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8550  * @singleton
8551  */
8552 Roo.form.VTypes = function(){
8553     // closure these in so they are only created once.
8554     var alpha = /^[a-zA-Z_]+$/;
8555     var alphanum = /^[a-zA-Z0-9_]+$/;
8556     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8557     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8558
8559     // All these messages and functions are configurable
8560     return {
8561         /**
8562          * The function used to validate email addresses
8563          * @param {String} value The email address
8564          */
8565         'email' : function(v){
8566             return email.test(v);
8567         },
8568         /**
8569          * The error text to display when the email validation function returns false
8570          * @type String
8571          */
8572         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8573         /**
8574          * The keystroke filter mask to be applied on email input
8575          * @type RegExp
8576          */
8577         'emailMask' : /[a-z0-9_\.\-@]/i,
8578
8579         /**
8580          * The function used to validate URLs
8581          * @param {String} value The URL
8582          */
8583         'url' : function(v){
8584             return url.test(v);
8585         },
8586         /**
8587          * The error text to display when the url validation function returns false
8588          * @type String
8589          */
8590         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8591         
8592         /**
8593          * The function used to validate alpha values
8594          * @param {String} value The value
8595          */
8596         'alpha' : function(v){
8597             return alpha.test(v);
8598         },
8599         /**
8600          * The error text to display when the alpha validation function returns false
8601          * @type String
8602          */
8603         'alphaText' : 'This field should only contain letters and _',
8604         /**
8605          * The keystroke filter mask to be applied on alpha input
8606          * @type RegExp
8607          */
8608         'alphaMask' : /[a-z_]/i,
8609
8610         /**
8611          * The function used to validate alphanumeric values
8612          * @param {String} value The value
8613          */
8614         'alphanum' : function(v){
8615             return alphanum.test(v);
8616         },
8617         /**
8618          * The error text to display when the alphanumeric validation function returns false
8619          * @type String
8620          */
8621         'alphanumText' : 'This field should only contain letters, numbers and _',
8622         /**
8623          * The keystroke filter mask to be applied on alphanumeric input
8624          * @type RegExp
8625          */
8626         'alphanumMask' : /[a-z0-9_]/i
8627     };
8628 }();/*
8629  * - LGPL
8630  *
8631  * Input
8632  * 
8633  */
8634
8635 /**
8636  * @class Roo.bootstrap.Input
8637  * @extends Roo.bootstrap.Component
8638  * Bootstrap Input class
8639  * @cfg {Boolean} disabled is it disabled
8640  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8641  * @cfg {String} name name of the input
8642  * @cfg {string} fieldLabel - the label associated
8643  * @cfg {string} placeholder - placeholder to put in text.
8644  * @cfg {string}  before - input group add on before
8645  * @cfg {string} after - input group add on after
8646  * @cfg {string} size - (lg|sm) or leave empty..
8647  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8648  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8649  * @cfg {Number} md colspan out of 12 for computer-sized screens
8650  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8651  * @cfg {string} value default value of the input
8652  * @cfg {Number} labelWidth set the width of label 
8653  * @cfg {Number} labellg set the width of label (1-12)
8654  * @cfg {Number} labelmd set the width of label (1-12)
8655  * @cfg {Number} labelsm set the width of label (1-12)
8656  * @cfg {Number} labelxs set the width of label (1-12)
8657  * @cfg {String} labelAlign (top|left)
8658  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8659  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8660  * @cfg {String} indicatorpos (left|right) default left
8661  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8662  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8663
8664  * @cfg {String} align (left|center|right) Default left
8665  * @cfg {Boolean} forceFeedback (true|false) Default false
8666  * 
8667  * @constructor
8668  * Create a new Input
8669  * @param {Object} config The config object
8670  */
8671
8672 Roo.bootstrap.Input = function(config){
8673     
8674     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8675     
8676     this.addEvents({
8677         /**
8678          * @event focus
8679          * Fires when this field receives input focus.
8680          * @param {Roo.form.Field} this
8681          */
8682         focus : true,
8683         /**
8684          * @event blur
8685          * Fires when this field loses input focus.
8686          * @param {Roo.form.Field} this
8687          */
8688         blur : true,
8689         /**
8690          * @event specialkey
8691          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8692          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8693          * @param {Roo.form.Field} this
8694          * @param {Roo.EventObject} e The event object
8695          */
8696         specialkey : true,
8697         /**
8698          * @event change
8699          * Fires just before the field blurs if the field value has changed.
8700          * @param {Roo.form.Field} this
8701          * @param {Mixed} newValue The new value
8702          * @param {Mixed} oldValue The original value
8703          */
8704         change : true,
8705         /**
8706          * @event invalid
8707          * Fires after the field has been marked as invalid.
8708          * @param {Roo.form.Field} this
8709          * @param {String} msg The validation message
8710          */
8711         invalid : true,
8712         /**
8713          * @event valid
8714          * Fires after the field has been validated with no errors.
8715          * @param {Roo.form.Field} this
8716          */
8717         valid : true,
8718          /**
8719          * @event keyup
8720          * Fires after the key up
8721          * @param {Roo.form.Field} this
8722          * @param {Roo.EventObject}  e The event Object
8723          */
8724         keyup : true
8725     });
8726 };
8727
8728 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8729      /**
8730      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8731       automatic validation (defaults to "keyup").
8732      */
8733     validationEvent : "keyup",
8734      /**
8735      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8736      */
8737     validateOnBlur : true,
8738     /**
8739      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8740      */
8741     validationDelay : 250,
8742      /**
8743      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8744      */
8745     focusClass : "x-form-focus",  // not needed???
8746     
8747        
8748     /**
8749      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8750      */
8751     invalidClass : "has-warning",
8752     
8753     /**
8754      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8755      */
8756     validClass : "has-success",
8757     
8758     /**
8759      * @cfg {Boolean} hasFeedback (true|false) default true
8760      */
8761     hasFeedback : true,
8762     
8763     /**
8764      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8765      */
8766     invalidFeedbackClass : "glyphicon-warning-sign",
8767     
8768     /**
8769      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8770      */
8771     validFeedbackClass : "glyphicon-ok",
8772     
8773     /**
8774      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8775      */
8776     selectOnFocus : false,
8777     
8778      /**
8779      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8780      */
8781     maskRe : null,
8782        /**
8783      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8784      */
8785     vtype : null,
8786     
8787       /**
8788      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8789      */
8790     disableKeyFilter : false,
8791     
8792        /**
8793      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8794      */
8795     disabled : false,
8796      /**
8797      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8798      */
8799     allowBlank : true,
8800     /**
8801      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8802      */
8803     blankText : "Please complete this mandatory field",
8804     
8805      /**
8806      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8807      */
8808     minLength : 0,
8809     /**
8810      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8811      */
8812     maxLength : Number.MAX_VALUE,
8813     /**
8814      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8815      */
8816     minLengthText : "The minimum length for this field is {0}",
8817     /**
8818      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8819      */
8820     maxLengthText : "The maximum length for this field is {0}",
8821   
8822     
8823     /**
8824      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8825      * If available, this function will be called only after the basic validators all return true, and will be passed the
8826      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8827      */
8828     validator : null,
8829     /**
8830      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8831      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8832      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8833      */
8834     regex : null,
8835     /**
8836      * @cfg {String} regexText -- Depricated - use Invalid Text
8837      */
8838     regexText : "",
8839     
8840     /**
8841      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8842      */
8843     invalidText : "",
8844     
8845     
8846     
8847     autocomplete: false,
8848     
8849     
8850     fieldLabel : '',
8851     inputType : 'text',
8852     
8853     name : false,
8854     placeholder: false,
8855     before : false,
8856     after : false,
8857     size : false,
8858     hasFocus : false,
8859     preventMark: false,
8860     isFormField : true,
8861     value : '',
8862     labelWidth : 2,
8863     labelAlign : false,
8864     readOnly : false,
8865     align : false,
8866     formatedValue : false,
8867     forceFeedback : false,
8868     
8869     indicatorpos : 'left',
8870     
8871     labellg : 0,
8872     labelmd : 0,
8873     labelsm : 0,
8874     labelxs : 0,
8875     
8876     capture : '',
8877     accept : '',
8878     
8879     parentLabelAlign : function()
8880     {
8881         var parent = this;
8882         while (parent.parent()) {
8883             parent = parent.parent();
8884             if (typeof(parent.labelAlign) !='undefined') {
8885                 return parent.labelAlign;
8886             }
8887         }
8888         return 'left';
8889         
8890     },
8891     
8892     getAutoCreate : function()
8893     {
8894         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8895         
8896         var id = Roo.id();
8897         
8898         var cfg = {};
8899         
8900         if(this.inputType != 'hidden'){
8901             cfg.cls = 'form-group' //input-group
8902         }
8903         
8904         var input =  {
8905             tag: 'input',
8906             id : id,
8907             type : this.inputType,
8908             value : this.value,
8909             cls : 'form-control',
8910             placeholder : this.placeholder || '',
8911             autocomplete : this.autocomplete || 'new-password'
8912         };
8913         
8914         if(this.capture.length){
8915             input.capture = this.capture;
8916         }
8917         
8918         if(this.accept.length){
8919             input.accept = this.accept + "/*";
8920         }
8921         
8922         if(this.align){
8923             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8924         }
8925         
8926         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8927             input.maxLength = this.maxLength;
8928         }
8929         
8930         if (this.disabled) {
8931             input.disabled=true;
8932         }
8933         
8934         if (this.readOnly) {
8935             input.readonly=true;
8936         }
8937         
8938         if (this.name) {
8939             input.name = this.name;
8940         }
8941         
8942         if (this.size) {
8943             input.cls += ' input-' + this.size;
8944         }
8945         
8946         var settings=this;
8947         ['xs','sm','md','lg'].map(function(size){
8948             if (settings[size]) {
8949                 cfg.cls += ' col-' + size + '-' + settings[size];
8950             }
8951         });
8952         
8953         var inputblock = input;
8954         
8955         var feedback = {
8956             tag: 'span',
8957             cls: 'glyphicon form-control-feedback'
8958         };
8959             
8960         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8961             
8962             inputblock = {
8963                 cls : 'has-feedback',
8964                 cn :  [
8965                     input,
8966                     feedback
8967                 ] 
8968             };  
8969         }
8970         
8971         if (this.before || this.after) {
8972             
8973             inputblock = {
8974                 cls : 'input-group',
8975                 cn :  [] 
8976             };
8977             
8978             if (this.before && typeof(this.before) == 'string') {
8979                 
8980                 inputblock.cn.push({
8981                     tag :'span',
8982                     cls : 'roo-input-before input-group-addon',
8983                     html : this.before
8984                 });
8985             }
8986             if (this.before && typeof(this.before) == 'object') {
8987                 this.before = Roo.factory(this.before);
8988                 
8989                 inputblock.cn.push({
8990                     tag :'span',
8991                     cls : 'roo-input-before input-group-' +
8992                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8993                 });
8994             }
8995             
8996             inputblock.cn.push(input);
8997             
8998             if (this.after && typeof(this.after) == 'string') {
8999                 inputblock.cn.push({
9000                     tag :'span',
9001                     cls : 'roo-input-after input-group-addon',
9002                     html : this.after
9003                 });
9004             }
9005             if (this.after && typeof(this.after) == 'object') {
9006                 this.after = Roo.factory(this.after);
9007                 
9008                 inputblock.cn.push({
9009                     tag :'span',
9010                     cls : 'roo-input-after input-group-' +
9011                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9012                 });
9013             }
9014             
9015             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9016                 inputblock.cls += ' has-feedback';
9017                 inputblock.cn.push(feedback);
9018             }
9019         };
9020         
9021         if (align ==='left' && this.fieldLabel.length) {
9022             
9023             cfg.cls += ' roo-form-group-label-left';
9024             
9025             cfg.cn = [
9026                 {
9027                     tag : 'i',
9028                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9029                     tooltip : 'This field is required'
9030                 },
9031                 {
9032                     tag: 'label',
9033                     'for' :  id,
9034                     cls : 'control-label',
9035                     html : this.fieldLabel
9036
9037                 },
9038                 {
9039                     cls : "", 
9040                     cn: [
9041                         inputblock
9042                     ]
9043                 }
9044             ];
9045             
9046             var labelCfg = cfg.cn[1];
9047             var contentCfg = cfg.cn[2];
9048             
9049             if(this.indicatorpos == 'right'){
9050                 cfg.cn = [
9051                     {
9052                         tag: 'label',
9053                         'for' :  id,
9054                         cls : 'control-label',
9055                         cn : [
9056                             {
9057                                 tag : 'span',
9058                                 html : this.fieldLabel
9059                             },
9060                             {
9061                                 tag : 'i',
9062                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9063                                 tooltip : 'This field is required'
9064                             }
9065                         ]
9066                     },
9067                     {
9068                         cls : "",
9069                         cn: [
9070                             inputblock
9071                         ]
9072                     }
9073
9074                 ];
9075                 
9076                 labelCfg = cfg.cn[0];
9077                 contentCfg = cfg.cn[1];
9078             
9079             }
9080             
9081             if(this.labelWidth > 12){
9082                 labelCfg.style = "width: " + this.labelWidth + 'px';
9083             }
9084             
9085             if(this.labelWidth < 13 && this.labelmd == 0){
9086                 this.labelmd = this.labelWidth;
9087             }
9088             
9089             if(this.labellg > 0){
9090                 labelCfg.cls += ' col-lg-' + this.labellg;
9091                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9092             }
9093             
9094             if(this.labelmd > 0){
9095                 labelCfg.cls += ' col-md-' + this.labelmd;
9096                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9097             }
9098             
9099             if(this.labelsm > 0){
9100                 labelCfg.cls += ' col-sm-' + this.labelsm;
9101                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9102             }
9103             
9104             if(this.labelxs > 0){
9105                 labelCfg.cls += ' col-xs-' + this.labelxs;
9106                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9107             }
9108             
9109             
9110         } else if ( this.fieldLabel.length) {
9111                 
9112             cfg.cn = [
9113                 {
9114                     tag : 'i',
9115                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9116                     tooltip : 'This field is required'
9117                 },
9118                 {
9119                     tag: 'label',
9120                    //cls : 'input-group-addon',
9121                     html : this.fieldLabel
9122
9123                 },
9124
9125                inputblock
9126
9127            ];
9128            
9129            if(this.indicatorpos == 'right'){
9130                 
9131                 cfg.cn = [
9132                     {
9133                         tag: 'label',
9134                        //cls : 'input-group-addon',
9135                         html : this.fieldLabel
9136
9137                     },
9138                     {
9139                         tag : 'i',
9140                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9141                         tooltip : 'This field is required'
9142                     },
9143
9144                    inputblock
9145
9146                ];
9147
9148             }
9149
9150         } else {
9151             
9152             cfg.cn = [
9153
9154                     inputblock
9155
9156             ];
9157                 
9158                 
9159         };
9160         
9161         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9162            cfg.cls += ' navbar-form';
9163         }
9164         
9165         if (this.parentType === 'NavGroup') {
9166            cfg.cls += ' navbar-form';
9167            cfg.tag = 'li';
9168         }
9169         
9170         return cfg;
9171         
9172     },
9173     /**
9174      * return the real input element.
9175      */
9176     inputEl: function ()
9177     {
9178         return this.el.select('input.form-control',true).first();
9179     },
9180     
9181     tooltipEl : function()
9182     {
9183         return this.inputEl();
9184     },
9185     
9186     indicatorEl : function()
9187     {
9188         var indicator = this.el.select('i.roo-required-indicator',true).first();
9189         
9190         if(!indicator){
9191             return false;
9192         }
9193         
9194         return indicator;
9195         
9196     },
9197     
9198     setDisabled : function(v)
9199     {
9200         var i  = this.inputEl().dom;
9201         if (!v) {
9202             i.removeAttribute('disabled');
9203             return;
9204             
9205         }
9206         i.setAttribute('disabled','true');
9207     },
9208     initEvents : function()
9209     {
9210           
9211         this.inputEl().on("keydown" , this.fireKey,  this);
9212         this.inputEl().on("focus", this.onFocus,  this);
9213         this.inputEl().on("blur", this.onBlur,  this);
9214         
9215         this.inputEl().relayEvent('keyup', this);
9216         
9217         this.indicator = this.indicatorEl();
9218         
9219         if(this.indicator){
9220             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9221         }
9222  
9223         // reference to original value for reset
9224         this.originalValue = this.getValue();
9225         //Roo.form.TextField.superclass.initEvents.call(this);
9226         if(this.validationEvent == 'keyup'){
9227             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9228             this.inputEl().on('keyup', this.filterValidation, this);
9229         }
9230         else if(this.validationEvent !== false){
9231             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9232         }
9233         
9234         if(this.selectOnFocus){
9235             this.on("focus", this.preFocus, this);
9236             
9237         }
9238         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9239             this.inputEl().on("keypress", this.filterKeys, this);
9240         } else {
9241             this.inputEl().relayEvent('keypress', this);
9242         }
9243        /* if(this.grow){
9244             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9245             this.el.on("click", this.autoSize,  this);
9246         }
9247         */
9248         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9249             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9250         }
9251         
9252         if (typeof(this.before) == 'object') {
9253             this.before.render(this.el.select('.roo-input-before',true).first());
9254         }
9255         if (typeof(this.after) == 'object') {
9256             this.after.render(this.el.select('.roo-input-after',true).first());
9257         }
9258         
9259         this.inputEl().on('change', this.onChange, this);
9260         
9261     },
9262     filterValidation : function(e){
9263         if(!e.isNavKeyPress()){
9264             this.validationTask.delay(this.validationDelay);
9265         }
9266     },
9267      /**
9268      * Validates the field value
9269      * @return {Boolean} True if the value is valid, else false
9270      */
9271     validate : function(){
9272         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9273         if(this.disabled || this.validateValue(this.getRawValue())){
9274             this.markValid();
9275             return true;
9276         }
9277         
9278         this.markInvalid();
9279         return false;
9280     },
9281     
9282     
9283     /**
9284      * Validates a value according to the field's validation rules and marks the field as invalid
9285      * if the validation fails
9286      * @param {Mixed} value The value to validate
9287      * @return {Boolean} True if the value is valid, else false
9288      */
9289     validateValue : function(value)
9290     {
9291         if(this.getVisibilityEl().hasClass('hidden')){
9292             return true;
9293         }
9294         
9295         if(value.length < 1)  { // if it's blank
9296             if(this.allowBlank){
9297                 return true;
9298             }
9299             return false;
9300         }
9301         
9302         if(value.length < this.minLength){
9303             return false;
9304         }
9305         if(value.length > this.maxLength){
9306             return false;
9307         }
9308         if(this.vtype){
9309             var vt = Roo.form.VTypes;
9310             if(!vt[this.vtype](value, this)){
9311                 return false;
9312             }
9313         }
9314         if(typeof this.validator == "function"){
9315             var msg = this.validator(value);
9316             if(msg !== true){
9317                 return false;
9318             }
9319             if (typeof(msg) == 'string') {
9320                 this.invalidText = msg;
9321             }
9322         }
9323         
9324         if(this.regex && !this.regex.test(value)){
9325             return false;
9326         }
9327         
9328         return true;
9329     },
9330     
9331      // private
9332     fireKey : function(e){
9333         //Roo.log('field ' + e.getKey());
9334         if(e.isNavKeyPress()){
9335             this.fireEvent("specialkey", this, e);
9336         }
9337     },
9338     focus : function (selectText){
9339         if(this.rendered){
9340             this.inputEl().focus();
9341             if(selectText === true){
9342                 this.inputEl().dom.select();
9343             }
9344         }
9345         return this;
9346     } ,
9347     
9348     onFocus : function(){
9349         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9350            // this.el.addClass(this.focusClass);
9351         }
9352         if(!this.hasFocus){
9353             this.hasFocus = true;
9354             this.startValue = this.getValue();
9355             this.fireEvent("focus", this);
9356         }
9357     },
9358     
9359     beforeBlur : Roo.emptyFn,
9360
9361     
9362     // private
9363     onBlur : function(){
9364         this.beforeBlur();
9365         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9366             //this.el.removeClass(this.focusClass);
9367         }
9368         this.hasFocus = false;
9369         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9370             this.validate();
9371         }
9372         var v = this.getValue();
9373         if(String(v) !== String(this.startValue)){
9374             this.fireEvent('change', this, v, this.startValue);
9375         }
9376         this.fireEvent("blur", this);
9377     },
9378     
9379     onChange : function(e)
9380     {
9381         var v = this.getValue();
9382         if(String(v) !== String(this.startValue)){
9383             this.fireEvent('change', this, v, this.startValue);
9384         }
9385         
9386     },
9387     
9388     /**
9389      * Resets the current field value to the originally loaded value and clears any validation messages
9390      */
9391     reset : function(){
9392         this.setValue(this.originalValue);
9393         this.validate();
9394     },
9395      /**
9396      * Returns the name of the field
9397      * @return {Mixed} name The name field
9398      */
9399     getName: function(){
9400         return this.name;
9401     },
9402      /**
9403      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9404      * @return {Mixed} value The field value
9405      */
9406     getValue : function(){
9407         
9408         var v = this.inputEl().getValue();
9409         
9410         return v;
9411     },
9412     /**
9413      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9414      * @return {Mixed} value The field value
9415      */
9416     getRawValue : function(){
9417         var v = this.inputEl().getValue();
9418         
9419         return v;
9420     },
9421     
9422     /**
9423      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9424      * @param {Mixed} value The value to set
9425      */
9426     setRawValue : function(v){
9427         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9428     },
9429     
9430     selectText : function(start, end){
9431         var v = this.getRawValue();
9432         if(v.length > 0){
9433             start = start === undefined ? 0 : start;
9434             end = end === undefined ? v.length : end;
9435             var d = this.inputEl().dom;
9436             if(d.setSelectionRange){
9437                 d.setSelectionRange(start, end);
9438             }else if(d.createTextRange){
9439                 var range = d.createTextRange();
9440                 range.moveStart("character", start);
9441                 range.moveEnd("character", v.length-end);
9442                 range.select();
9443             }
9444         }
9445     },
9446     
9447     /**
9448      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9449      * @param {Mixed} value The value to set
9450      */
9451     setValue : function(v){
9452         this.value = v;
9453         if(this.rendered){
9454             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9455             this.validate();
9456         }
9457     },
9458     
9459     /*
9460     processValue : function(value){
9461         if(this.stripCharsRe){
9462             var newValue = value.replace(this.stripCharsRe, '');
9463             if(newValue !== value){
9464                 this.setRawValue(newValue);
9465                 return newValue;
9466             }
9467         }
9468         return value;
9469     },
9470   */
9471     preFocus : function(){
9472         
9473         if(this.selectOnFocus){
9474             this.inputEl().dom.select();
9475         }
9476     },
9477     filterKeys : function(e){
9478         var k = e.getKey();
9479         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9480             return;
9481         }
9482         var c = e.getCharCode(), cc = String.fromCharCode(c);
9483         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9484             return;
9485         }
9486         if(!this.maskRe.test(cc)){
9487             e.stopEvent();
9488         }
9489     },
9490      /**
9491      * Clear any invalid styles/messages for this field
9492      */
9493     clearInvalid : function(){
9494         
9495         if(!this.el || this.preventMark){ // not rendered
9496             return;
9497         }
9498         
9499      
9500         this.el.removeClass(this.invalidClass);
9501         
9502         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9503             
9504             var feedback = this.el.select('.form-control-feedback', true).first();
9505             
9506             if(feedback){
9507                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9508             }
9509             
9510         }
9511         
9512         if(this.indicator){
9513             this.indicator.removeClass('visible');
9514             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9515         }
9516         
9517         this.fireEvent('valid', this);
9518     },
9519     
9520      /**
9521      * Mark this field as valid
9522      */
9523     markValid : function()
9524     {
9525         if(!this.el  || this.preventMark){ // not rendered...
9526             return;
9527         }
9528         
9529         this.el.removeClass([this.invalidClass, this.validClass]);
9530         
9531         var feedback = this.el.select('.form-control-feedback', true).first();
9532             
9533         if(feedback){
9534             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9535         }
9536         
9537         if(this.indicator){
9538             this.indicator.removeClass('visible');
9539             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9540         }
9541         
9542         if(this.disabled){
9543             return;
9544         }
9545         
9546         if(this.allowBlank && !this.getRawValue().length){
9547             return;
9548         }
9549         
9550         this.el.addClass(this.validClass);
9551         
9552         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9553             
9554             var feedback = this.el.select('.form-control-feedback', true).first();
9555             
9556             if(feedback){
9557                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9558                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9559             }
9560             
9561         }
9562         
9563         this.fireEvent('valid', this);
9564     },
9565     
9566      /**
9567      * Mark this field as invalid
9568      * @param {String} msg The validation message
9569      */
9570     markInvalid : function(msg)
9571     {
9572         if(!this.el  || this.preventMark){ // not rendered
9573             return;
9574         }
9575         
9576         this.el.removeClass([this.invalidClass, this.validClass]);
9577         
9578         var feedback = this.el.select('.form-control-feedback', true).first();
9579             
9580         if(feedback){
9581             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9582         }
9583
9584         if(this.disabled){
9585             return;
9586         }
9587         
9588         if(this.allowBlank && !this.getRawValue().length){
9589             return;
9590         }
9591         
9592         if(this.indicator){
9593             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9594             this.indicator.addClass('visible');
9595         }
9596         
9597         this.el.addClass(this.invalidClass);
9598         
9599         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9600             
9601             var feedback = this.el.select('.form-control-feedback', true).first();
9602             
9603             if(feedback){
9604                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9605                 
9606                 if(this.getValue().length || this.forceFeedback){
9607                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9608                 }
9609                 
9610             }
9611             
9612         }
9613         
9614         this.fireEvent('invalid', this, msg);
9615     },
9616     // private
9617     SafariOnKeyDown : function(event)
9618     {
9619         // this is a workaround for a password hang bug on chrome/ webkit.
9620         if (this.inputEl().dom.type != 'password') {
9621             return;
9622         }
9623         
9624         var isSelectAll = false;
9625         
9626         if(this.inputEl().dom.selectionEnd > 0){
9627             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9628         }
9629         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9630             event.preventDefault();
9631             this.setValue('');
9632             return;
9633         }
9634         
9635         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9636             
9637             event.preventDefault();
9638             // this is very hacky as keydown always get's upper case.
9639             //
9640             var cc = String.fromCharCode(event.getCharCode());
9641             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9642             
9643         }
9644     },
9645     adjustWidth : function(tag, w){
9646         tag = tag.toLowerCase();
9647         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9648             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9649                 if(tag == 'input'){
9650                     return w + 2;
9651                 }
9652                 if(tag == 'textarea'){
9653                     return w-2;
9654                 }
9655             }else if(Roo.isOpera){
9656                 if(tag == 'input'){
9657                     return w + 2;
9658                 }
9659                 if(tag == 'textarea'){
9660                     return w-2;
9661                 }
9662             }
9663         }
9664         return w;
9665     },
9666     
9667     setFieldLabel : function(v)
9668     {
9669         if(!this.rendered){
9670             return;
9671         }
9672         
9673         if(this.indicator){
9674             var ar = this.el.select('label > span',true);
9675             
9676             if (ar.elements.length) {
9677                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9678                 this.fieldLabel = v;
9679                 return;
9680             }
9681             
9682             var br = this.el.select('label',true);
9683             
9684             if(br.elements.length) {
9685                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9686                 this.fieldLabel = v;
9687                 return;
9688             }
9689             
9690             Roo.log('Cannot Found any of label > span || label in input');
9691             return;
9692         }
9693         
9694         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9695         this.fieldLabel = v;
9696         
9697         
9698     }
9699 });
9700
9701  
9702 /*
9703  * - LGPL
9704  *
9705  * Input
9706  * 
9707  */
9708
9709 /**
9710  * @class Roo.bootstrap.TextArea
9711  * @extends Roo.bootstrap.Input
9712  * Bootstrap TextArea class
9713  * @cfg {Number} cols Specifies the visible width of a text area
9714  * @cfg {Number} rows Specifies the visible number of lines in a text area
9715  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9716  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9717  * @cfg {string} html text
9718  * 
9719  * @constructor
9720  * Create a new TextArea
9721  * @param {Object} config The config object
9722  */
9723
9724 Roo.bootstrap.TextArea = function(config){
9725     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9726    
9727 };
9728
9729 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9730      
9731     cols : false,
9732     rows : 5,
9733     readOnly : false,
9734     warp : 'soft',
9735     resize : false,
9736     value: false,
9737     html: false,
9738     
9739     getAutoCreate : function(){
9740         
9741         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9742         
9743         var id = Roo.id();
9744         
9745         var cfg = {};
9746         
9747         if(this.inputType != 'hidden'){
9748             cfg.cls = 'form-group' //input-group
9749         }
9750         
9751         var input =  {
9752             tag: 'textarea',
9753             id : id,
9754             warp : this.warp,
9755             rows : this.rows,
9756             value : this.value || '',
9757             html: this.html || '',
9758             cls : 'form-control',
9759             placeholder : this.placeholder || '' 
9760             
9761         };
9762         
9763         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9764             input.maxLength = this.maxLength;
9765         }
9766         
9767         if(this.resize){
9768             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9769         }
9770         
9771         if(this.cols){
9772             input.cols = this.cols;
9773         }
9774         
9775         if (this.readOnly) {
9776             input.readonly = true;
9777         }
9778         
9779         if (this.name) {
9780             input.name = this.name;
9781         }
9782         
9783         if (this.size) {
9784             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9785         }
9786         
9787         var settings=this;
9788         ['xs','sm','md','lg'].map(function(size){
9789             if (settings[size]) {
9790                 cfg.cls += ' col-' + size + '-' + settings[size];
9791             }
9792         });
9793         
9794         var inputblock = input;
9795         
9796         if(this.hasFeedback && !this.allowBlank){
9797             
9798             var feedback = {
9799                 tag: 'span',
9800                 cls: 'glyphicon form-control-feedback'
9801             };
9802
9803             inputblock = {
9804                 cls : 'has-feedback',
9805                 cn :  [
9806                     input,
9807                     feedback
9808                 ] 
9809             };  
9810         }
9811         
9812         
9813         if (this.before || this.after) {
9814             
9815             inputblock = {
9816                 cls : 'input-group',
9817                 cn :  [] 
9818             };
9819             if (this.before) {
9820                 inputblock.cn.push({
9821                     tag :'span',
9822                     cls : 'input-group-addon',
9823                     html : this.before
9824                 });
9825             }
9826             
9827             inputblock.cn.push(input);
9828             
9829             if(this.hasFeedback && !this.allowBlank){
9830                 inputblock.cls += ' has-feedback';
9831                 inputblock.cn.push(feedback);
9832             }
9833             
9834             if (this.after) {
9835                 inputblock.cn.push({
9836                     tag :'span',
9837                     cls : 'input-group-addon',
9838                     html : this.after
9839                 });
9840             }
9841             
9842         }
9843         
9844         if (align ==='left' && this.fieldLabel.length) {
9845             cfg.cn = [
9846                 {
9847                     tag: 'label',
9848                     'for' :  id,
9849                     cls : 'control-label',
9850                     html : this.fieldLabel
9851                 },
9852                 {
9853                     cls : "",
9854                     cn: [
9855                         inputblock
9856                     ]
9857                 }
9858
9859             ];
9860             
9861             if(this.labelWidth > 12){
9862                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9863             }
9864
9865             if(this.labelWidth < 13 && this.labelmd == 0){
9866                 this.labelmd = this.labelWidth;
9867             }
9868
9869             if(this.labellg > 0){
9870                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9871                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9872             }
9873
9874             if(this.labelmd > 0){
9875                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9876                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9877             }
9878
9879             if(this.labelsm > 0){
9880                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9881                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9882             }
9883
9884             if(this.labelxs > 0){
9885                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9886                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9887             }
9888             
9889         } else if ( this.fieldLabel.length) {
9890             cfg.cn = [
9891
9892                {
9893                    tag: 'label',
9894                    //cls : 'input-group-addon',
9895                    html : this.fieldLabel
9896
9897                },
9898
9899                inputblock
9900
9901            ];
9902
9903         } else {
9904
9905             cfg.cn = [
9906
9907                 inputblock
9908
9909             ];
9910                 
9911         }
9912         
9913         if (this.disabled) {
9914             input.disabled=true;
9915         }
9916         
9917         return cfg;
9918         
9919     },
9920     /**
9921      * return the real textarea element.
9922      */
9923     inputEl: function ()
9924     {
9925         return this.el.select('textarea.form-control',true).first();
9926     },
9927     
9928     /**
9929      * Clear any invalid styles/messages for this field
9930      */
9931     clearInvalid : function()
9932     {
9933         
9934         if(!this.el || this.preventMark){ // not rendered
9935             return;
9936         }
9937         
9938         var label = this.el.select('label', true).first();
9939         var icon = this.el.select('i.fa-star', true).first();
9940         
9941         if(label && icon){
9942             icon.remove();
9943         }
9944         
9945         this.el.removeClass(this.invalidClass);
9946         
9947         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9948             
9949             var feedback = this.el.select('.form-control-feedback', true).first();
9950             
9951             if(feedback){
9952                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9953             }
9954             
9955         }
9956         
9957         this.fireEvent('valid', this);
9958     },
9959     
9960      /**
9961      * Mark this field as valid
9962      */
9963     markValid : function()
9964     {
9965         if(!this.el  || this.preventMark){ // not rendered
9966             return;
9967         }
9968         
9969         this.el.removeClass([this.invalidClass, this.validClass]);
9970         
9971         var feedback = this.el.select('.form-control-feedback', true).first();
9972             
9973         if(feedback){
9974             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9975         }
9976
9977         if(this.disabled || this.allowBlank){
9978             return;
9979         }
9980         
9981         var label = this.el.select('label', true).first();
9982         var icon = this.el.select('i.fa-star', true).first();
9983         
9984         if(label && icon){
9985             icon.remove();
9986         }
9987         
9988         this.el.addClass(this.validClass);
9989         
9990         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9991             
9992             var feedback = this.el.select('.form-control-feedback', true).first();
9993             
9994             if(feedback){
9995                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9996                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9997             }
9998             
9999         }
10000         
10001         this.fireEvent('valid', this);
10002     },
10003     
10004      /**
10005      * Mark this field as invalid
10006      * @param {String} msg The validation message
10007      */
10008     markInvalid : function(msg)
10009     {
10010         if(!this.el  || this.preventMark){ // not rendered
10011             return;
10012         }
10013         
10014         this.el.removeClass([this.invalidClass, this.validClass]);
10015         
10016         var feedback = this.el.select('.form-control-feedback', true).first();
10017             
10018         if(feedback){
10019             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10020         }
10021
10022         if(this.disabled || this.allowBlank){
10023             return;
10024         }
10025         
10026         var label = this.el.select('label', true).first();
10027         var icon = this.el.select('i.fa-star', true).first();
10028         
10029         if(!this.getValue().length && label && !icon){
10030             this.el.createChild({
10031                 tag : 'i',
10032                 cls : 'text-danger fa fa-lg fa-star',
10033                 tooltip : 'This field is required',
10034                 style : 'margin-right:5px;'
10035             }, label, true);
10036         }
10037
10038         this.el.addClass(this.invalidClass);
10039         
10040         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10041             
10042             var feedback = this.el.select('.form-control-feedback', true).first();
10043             
10044             if(feedback){
10045                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10046                 
10047                 if(this.getValue().length || this.forceFeedback){
10048                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10049                 }
10050                 
10051             }
10052             
10053         }
10054         
10055         this.fireEvent('invalid', this, msg);
10056     }
10057 });
10058
10059  
10060 /*
10061  * - LGPL
10062  *
10063  * trigger field - base class for combo..
10064  * 
10065  */
10066  
10067 /**
10068  * @class Roo.bootstrap.TriggerField
10069  * @extends Roo.bootstrap.Input
10070  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10071  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10072  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10073  * for which you can provide a custom implementation.  For example:
10074  * <pre><code>
10075 var trigger = new Roo.bootstrap.TriggerField();
10076 trigger.onTriggerClick = myTriggerFn;
10077 trigger.applyTo('my-field');
10078 </code></pre>
10079  *
10080  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10081  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10082  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10083  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10084  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10085
10086  * @constructor
10087  * Create a new TriggerField.
10088  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10089  * to the base TextField)
10090  */
10091 Roo.bootstrap.TriggerField = function(config){
10092     this.mimicing = false;
10093     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10094 };
10095
10096 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10097     /**
10098      * @cfg {String} triggerClass A CSS class to apply to the trigger
10099      */
10100      /**
10101      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10102      */
10103     hideTrigger:false,
10104
10105     /**
10106      * @cfg {Boolean} removable (true|false) special filter default false
10107      */
10108     removable : false,
10109     
10110     /** @cfg {Boolean} grow @hide */
10111     /** @cfg {Number} growMin @hide */
10112     /** @cfg {Number} growMax @hide */
10113
10114     /**
10115      * @hide 
10116      * @method
10117      */
10118     autoSize: Roo.emptyFn,
10119     // private
10120     monitorTab : true,
10121     // private
10122     deferHeight : true,
10123
10124     
10125     actionMode : 'wrap',
10126     
10127     caret : false,
10128     
10129     
10130     getAutoCreate : function(){
10131        
10132         var align = this.labelAlign || this.parentLabelAlign();
10133         
10134         var id = Roo.id();
10135         
10136         var cfg = {
10137             cls: 'form-group' //input-group
10138         };
10139         
10140         
10141         var input =  {
10142             tag: 'input',
10143             id : id,
10144             type : this.inputType,
10145             cls : 'form-control',
10146             autocomplete: 'new-password',
10147             placeholder : this.placeholder || '' 
10148             
10149         };
10150         if (this.name) {
10151             input.name = this.name;
10152         }
10153         if (this.size) {
10154             input.cls += ' input-' + this.size;
10155         }
10156         
10157         if (this.disabled) {
10158             input.disabled=true;
10159         }
10160         
10161         var inputblock = input;
10162         
10163         if(this.hasFeedback && !this.allowBlank){
10164             
10165             var feedback = {
10166                 tag: 'span',
10167                 cls: 'glyphicon form-control-feedback'
10168             };
10169             
10170             if(this.removable && !this.editable && !this.tickable){
10171                 inputblock = {
10172                     cls : 'has-feedback',
10173                     cn :  [
10174                         inputblock,
10175                         {
10176                             tag: 'button',
10177                             html : 'x',
10178                             cls : 'roo-combo-removable-btn close'
10179                         },
10180                         feedback
10181                     ] 
10182                 };
10183             } else {
10184                 inputblock = {
10185                     cls : 'has-feedback',
10186                     cn :  [
10187                         inputblock,
10188                         feedback
10189                     ] 
10190                 };
10191             }
10192
10193         } else {
10194             if(this.removable && !this.editable && !this.tickable){
10195                 inputblock = {
10196                     cls : 'roo-removable',
10197                     cn :  [
10198                         inputblock,
10199                         {
10200                             tag: 'button',
10201                             html : 'x',
10202                             cls : 'roo-combo-removable-btn close'
10203                         }
10204                     ] 
10205                 };
10206             }
10207         }
10208         
10209         if (this.before || this.after) {
10210             
10211             inputblock = {
10212                 cls : 'input-group',
10213                 cn :  [] 
10214             };
10215             if (this.before) {
10216                 inputblock.cn.push({
10217                     tag :'span',
10218                     cls : 'input-group-addon',
10219                     html : this.before
10220                 });
10221             }
10222             
10223             inputblock.cn.push(input);
10224             
10225             if(this.hasFeedback && !this.allowBlank){
10226                 inputblock.cls += ' has-feedback';
10227                 inputblock.cn.push(feedback);
10228             }
10229             
10230             if (this.after) {
10231                 inputblock.cn.push({
10232                     tag :'span',
10233                     cls : 'input-group-addon',
10234                     html : this.after
10235                 });
10236             }
10237             
10238         };
10239         
10240         var box = {
10241             tag: 'div',
10242             cn: [
10243                 {
10244                     tag: 'input',
10245                     type : 'hidden',
10246                     cls: 'form-hidden-field'
10247                 },
10248                 inputblock
10249             ]
10250             
10251         };
10252         
10253         if(this.multiple){
10254             box = {
10255                 tag: 'div',
10256                 cn: [
10257                     {
10258                         tag: 'input',
10259                         type : 'hidden',
10260                         cls: 'form-hidden-field'
10261                     },
10262                     {
10263                         tag: 'ul',
10264                         cls: 'roo-select2-choices',
10265                         cn:[
10266                             {
10267                                 tag: 'li',
10268                                 cls: 'roo-select2-search-field',
10269                                 cn: [
10270
10271                                     inputblock
10272                                 ]
10273                             }
10274                         ]
10275                     }
10276                 ]
10277             }
10278         };
10279         
10280         var combobox = {
10281             cls: 'roo-select2-container input-group',
10282             cn: [
10283                 box
10284 //                {
10285 //                    tag: 'ul',
10286 //                    cls: 'typeahead typeahead-long dropdown-menu',
10287 //                    style: 'display:none'
10288 //                }
10289             ]
10290         };
10291         
10292         if(!this.multiple && this.showToggleBtn){
10293             
10294             var caret = {
10295                         tag: 'span',
10296                         cls: 'caret'
10297              };
10298             if (this.caret != false) {
10299                 caret = {
10300                      tag: 'i',
10301                      cls: 'fa fa-' + this.caret
10302                 };
10303                 
10304             }
10305             
10306             combobox.cn.push({
10307                 tag :'span',
10308                 cls : 'input-group-addon btn dropdown-toggle',
10309                 cn : [
10310                     caret,
10311                     {
10312                         tag: 'span',
10313                         cls: 'combobox-clear',
10314                         cn  : [
10315                             {
10316                                 tag : 'i',
10317                                 cls: 'icon-remove'
10318                             }
10319                         ]
10320                     }
10321                 ]
10322
10323             })
10324         }
10325         
10326         if(this.multiple){
10327             combobox.cls += ' roo-select2-container-multi';
10328         }
10329         
10330         if (align ==='left' && this.fieldLabel.length) {
10331             
10332             cfg.cls += ' roo-form-group-label-left';
10333
10334             cfg.cn = [
10335                 {
10336                     tag : 'i',
10337                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10338                     tooltip : 'This field is required'
10339                 },
10340                 {
10341                     tag: 'label',
10342                     'for' :  id,
10343                     cls : 'control-label',
10344                     html : this.fieldLabel
10345
10346                 },
10347                 {
10348                     cls : "", 
10349                     cn: [
10350                         combobox
10351                     ]
10352                 }
10353
10354             ];
10355             
10356             var labelCfg = cfg.cn[1];
10357             var contentCfg = cfg.cn[2];
10358             
10359             if(this.indicatorpos == 'right'){
10360                 cfg.cn = [
10361                     {
10362                         tag: 'label',
10363                         'for' :  id,
10364                         cls : 'control-label',
10365                         cn : [
10366                             {
10367                                 tag : 'span',
10368                                 html : this.fieldLabel
10369                             },
10370                             {
10371                                 tag : 'i',
10372                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10373                                 tooltip : 'This field is required'
10374                             }
10375                         ]
10376                     },
10377                     {
10378                         cls : "", 
10379                         cn: [
10380                             combobox
10381                         ]
10382                     }
10383
10384                 ];
10385                 
10386                 labelCfg = cfg.cn[0];
10387                 contentCfg = cfg.cn[1];
10388             }
10389             
10390             if(this.labelWidth > 12){
10391                 labelCfg.style = "width: " + this.labelWidth + 'px';
10392             }
10393             
10394             if(this.labelWidth < 13 && this.labelmd == 0){
10395                 this.labelmd = this.labelWidth;
10396             }
10397             
10398             if(this.labellg > 0){
10399                 labelCfg.cls += ' col-lg-' + this.labellg;
10400                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10401             }
10402             
10403             if(this.labelmd > 0){
10404                 labelCfg.cls += ' col-md-' + this.labelmd;
10405                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10406             }
10407             
10408             if(this.labelsm > 0){
10409                 labelCfg.cls += ' col-sm-' + this.labelsm;
10410                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10411             }
10412             
10413             if(this.labelxs > 0){
10414                 labelCfg.cls += ' col-xs-' + this.labelxs;
10415                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10416             }
10417             
10418         } else if ( this.fieldLabel.length) {
10419 //                Roo.log(" label");
10420             cfg.cn = [
10421                 {
10422                    tag : 'i',
10423                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10424                    tooltip : 'This field is required'
10425                },
10426                {
10427                    tag: 'label',
10428                    //cls : 'input-group-addon',
10429                    html : this.fieldLabel
10430
10431                },
10432
10433                combobox
10434
10435             ];
10436             
10437             if(this.indicatorpos == 'right'){
10438                 
10439                 cfg.cn = [
10440                     {
10441                        tag: 'label',
10442                        cn : [
10443                            {
10444                                tag : 'span',
10445                                html : this.fieldLabel
10446                            },
10447                            {
10448                               tag : 'i',
10449                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10450                               tooltip : 'This field is required'
10451                            }
10452                        ]
10453
10454                     },
10455                     combobox
10456
10457                 ];
10458
10459             }
10460
10461         } else {
10462             
10463 //                Roo.log(" no label && no align");
10464                 cfg = combobox
10465                      
10466                 
10467         }
10468         
10469         var settings=this;
10470         ['xs','sm','md','lg'].map(function(size){
10471             if (settings[size]) {
10472                 cfg.cls += ' col-' + size + '-' + settings[size];
10473             }
10474         });
10475         
10476         return cfg;
10477         
10478     },
10479     
10480     
10481     
10482     // private
10483     onResize : function(w, h){
10484 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10485 //        if(typeof w == 'number'){
10486 //            var x = w - this.trigger.getWidth();
10487 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10488 //            this.trigger.setStyle('left', x+'px');
10489 //        }
10490     },
10491
10492     // private
10493     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10494
10495     // private
10496     getResizeEl : function(){
10497         return this.inputEl();
10498     },
10499
10500     // private
10501     getPositionEl : function(){
10502         return this.inputEl();
10503     },
10504
10505     // private
10506     alignErrorIcon : function(){
10507         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10508     },
10509
10510     // private
10511     initEvents : function(){
10512         
10513         this.createList();
10514         
10515         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10516         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10517         if(!this.multiple && this.showToggleBtn){
10518             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10519             if(this.hideTrigger){
10520                 this.trigger.setDisplayed(false);
10521             }
10522             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10523         }
10524         
10525         if(this.multiple){
10526             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10527         }
10528         
10529         if(this.removable && !this.editable && !this.tickable){
10530             var close = this.closeTriggerEl();
10531             
10532             if(close){
10533                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10534                 close.on('click', this.removeBtnClick, this, close);
10535             }
10536         }
10537         
10538         //this.trigger.addClassOnOver('x-form-trigger-over');
10539         //this.trigger.addClassOnClick('x-form-trigger-click');
10540         
10541         //if(!this.width){
10542         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10543         //}
10544     },
10545     
10546     closeTriggerEl : function()
10547     {
10548         var close = this.el.select('.roo-combo-removable-btn', true).first();
10549         return close ? close : false;
10550     },
10551     
10552     removeBtnClick : function(e, h, el)
10553     {
10554         e.preventDefault();
10555         
10556         if(this.fireEvent("remove", this) !== false){
10557             this.reset();
10558             this.fireEvent("afterremove", this)
10559         }
10560     },
10561     
10562     createList : function()
10563     {
10564         this.list = Roo.get(document.body).createChild({
10565             tag: 'ul',
10566             cls: 'typeahead typeahead-long dropdown-menu',
10567             style: 'display:none'
10568         });
10569         
10570         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10571         
10572     },
10573
10574     // private
10575     initTrigger : function(){
10576        
10577     },
10578
10579     // private
10580     onDestroy : function(){
10581         if(this.trigger){
10582             this.trigger.removeAllListeners();
10583           //  this.trigger.remove();
10584         }
10585         //if(this.wrap){
10586         //    this.wrap.remove();
10587         //}
10588         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10589     },
10590
10591     // private
10592     onFocus : function(){
10593         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10594         /*
10595         if(!this.mimicing){
10596             this.wrap.addClass('x-trigger-wrap-focus');
10597             this.mimicing = true;
10598             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10599             if(this.monitorTab){
10600                 this.el.on("keydown", this.checkTab, this);
10601             }
10602         }
10603         */
10604     },
10605
10606     // private
10607     checkTab : function(e){
10608         if(e.getKey() == e.TAB){
10609             this.triggerBlur();
10610         }
10611     },
10612
10613     // private
10614     onBlur : function(){
10615         // do nothing
10616     },
10617
10618     // private
10619     mimicBlur : function(e, t){
10620         /*
10621         if(!this.wrap.contains(t) && this.validateBlur()){
10622             this.triggerBlur();
10623         }
10624         */
10625     },
10626
10627     // private
10628     triggerBlur : function(){
10629         this.mimicing = false;
10630         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10631         if(this.monitorTab){
10632             this.el.un("keydown", this.checkTab, this);
10633         }
10634         //this.wrap.removeClass('x-trigger-wrap-focus');
10635         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10636     },
10637
10638     // private
10639     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10640     validateBlur : function(e, t){
10641         return true;
10642     },
10643
10644     // private
10645     onDisable : function(){
10646         this.inputEl().dom.disabled = true;
10647         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10648         //if(this.wrap){
10649         //    this.wrap.addClass('x-item-disabled');
10650         //}
10651     },
10652
10653     // private
10654     onEnable : function(){
10655         this.inputEl().dom.disabled = false;
10656         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10657         //if(this.wrap){
10658         //    this.el.removeClass('x-item-disabled');
10659         //}
10660     },
10661
10662     // private
10663     onShow : function(){
10664         var ae = this.getActionEl();
10665         
10666         if(ae){
10667             ae.dom.style.display = '';
10668             ae.dom.style.visibility = 'visible';
10669         }
10670     },
10671
10672     // private
10673     
10674     onHide : function(){
10675         var ae = this.getActionEl();
10676         ae.dom.style.display = 'none';
10677     },
10678
10679     /**
10680      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10681      * by an implementing function.
10682      * @method
10683      * @param {EventObject} e
10684      */
10685     onTriggerClick : Roo.emptyFn
10686 });
10687  /*
10688  * Based on:
10689  * Ext JS Library 1.1.1
10690  * Copyright(c) 2006-2007, Ext JS, LLC.
10691  *
10692  * Originally Released Under LGPL - original licence link has changed is not relivant.
10693  *
10694  * Fork - LGPL
10695  * <script type="text/javascript">
10696  */
10697
10698
10699 /**
10700  * @class Roo.data.SortTypes
10701  * @singleton
10702  * Defines the default sorting (casting?) comparison functions used when sorting data.
10703  */
10704 Roo.data.SortTypes = {
10705     /**
10706      * Default sort that does nothing
10707      * @param {Mixed} s The value being converted
10708      * @return {Mixed} The comparison value
10709      */
10710     none : function(s){
10711         return s;
10712     },
10713     
10714     /**
10715      * The regular expression used to strip tags
10716      * @type {RegExp}
10717      * @property
10718      */
10719     stripTagsRE : /<\/?[^>]+>/gi,
10720     
10721     /**
10722      * Strips all HTML tags to sort on text only
10723      * @param {Mixed} s The value being converted
10724      * @return {String} The comparison value
10725      */
10726     asText : function(s){
10727         return String(s).replace(this.stripTagsRE, "");
10728     },
10729     
10730     /**
10731      * Strips all HTML tags to sort on text only - Case insensitive
10732      * @param {Mixed} s The value being converted
10733      * @return {String} The comparison value
10734      */
10735     asUCText : function(s){
10736         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10737     },
10738     
10739     /**
10740      * Case insensitive string
10741      * @param {Mixed} s The value being converted
10742      * @return {String} The comparison value
10743      */
10744     asUCString : function(s) {
10745         return String(s).toUpperCase();
10746     },
10747     
10748     /**
10749      * Date sorting
10750      * @param {Mixed} s The value being converted
10751      * @return {Number} The comparison value
10752      */
10753     asDate : function(s) {
10754         if(!s){
10755             return 0;
10756         }
10757         if(s instanceof Date){
10758             return s.getTime();
10759         }
10760         return Date.parse(String(s));
10761     },
10762     
10763     /**
10764      * Float sorting
10765      * @param {Mixed} s The value being converted
10766      * @return {Float} The comparison value
10767      */
10768     asFloat : function(s) {
10769         var val = parseFloat(String(s).replace(/,/g, ""));
10770         if(isNaN(val)) {
10771             val = 0;
10772         }
10773         return val;
10774     },
10775     
10776     /**
10777      * Integer sorting
10778      * @param {Mixed} s The value being converted
10779      * @return {Number} The comparison value
10780      */
10781     asInt : function(s) {
10782         var val = parseInt(String(s).replace(/,/g, ""));
10783         if(isNaN(val)) {
10784             val = 0;
10785         }
10786         return val;
10787     }
10788 };/*
10789  * Based on:
10790  * Ext JS Library 1.1.1
10791  * Copyright(c) 2006-2007, Ext JS, LLC.
10792  *
10793  * Originally Released Under LGPL - original licence link has changed is not relivant.
10794  *
10795  * Fork - LGPL
10796  * <script type="text/javascript">
10797  */
10798
10799 /**
10800 * @class Roo.data.Record
10801  * Instances of this class encapsulate both record <em>definition</em> information, and record
10802  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10803  * to access Records cached in an {@link Roo.data.Store} object.<br>
10804  * <p>
10805  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10806  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10807  * objects.<br>
10808  * <p>
10809  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10810  * @constructor
10811  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10812  * {@link #create}. The parameters are the same.
10813  * @param {Array} data An associative Array of data values keyed by the field name.
10814  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10815  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10816  * not specified an integer id is generated.
10817  */
10818 Roo.data.Record = function(data, id){
10819     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10820     this.data = data;
10821 };
10822
10823 /**
10824  * Generate a constructor for a specific record layout.
10825  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10826  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10827  * Each field definition object may contain the following properties: <ul>
10828  * <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,
10829  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10830  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10831  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10832  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10833  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10834  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10835  * this may be omitted.</p></li>
10836  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10837  * <ul><li>auto (Default, implies no conversion)</li>
10838  * <li>string</li>
10839  * <li>int</li>
10840  * <li>float</li>
10841  * <li>boolean</li>
10842  * <li>date</li></ul></p></li>
10843  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10844  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10845  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10846  * by the Reader into an object that will be stored in the Record. It is passed the
10847  * following parameters:<ul>
10848  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10849  * </ul></p></li>
10850  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10851  * </ul>
10852  * <br>usage:<br><pre><code>
10853 var TopicRecord = Roo.data.Record.create(
10854     {name: 'title', mapping: 'topic_title'},
10855     {name: 'author', mapping: 'username'},
10856     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10857     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10858     {name: 'lastPoster', mapping: 'user2'},
10859     {name: 'excerpt', mapping: 'post_text'}
10860 );
10861
10862 var myNewRecord = new TopicRecord({
10863     title: 'Do my job please',
10864     author: 'noobie',
10865     totalPosts: 1,
10866     lastPost: new Date(),
10867     lastPoster: 'Animal',
10868     excerpt: 'No way dude!'
10869 });
10870 myStore.add(myNewRecord);
10871 </code></pre>
10872  * @method create
10873  * @static
10874  */
10875 Roo.data.Record.create = function(o){
10876     var f = function(){
10877         f.superclass.constructor.apply(this, arguments);
10878     };
10879     Roo.extend(f, Roo.data.Record);
10880     var p = f.prototype;
10881     p.fields = new Roo.util.MixedCollection(false, function(field){
10882         return field.name;
10883     });
10884     for(var i = 0, len = o.length; i < len; i++){
10885         p.fields.add(new Roo.data.Field(o[i]));
10886     }
10887     f.getField = function(name){
10888         return p.fields.get(name);  
10889     };
10890     return f;
10891 };
10892
10893 Roo.data.Record.AUTO_ID = 1000;
10894 Roo.data.Record.EDIT = 'edit';
10895 Roo.data.Record.REJECT = 'reject';
10896 Roo.data.Record.COMMIT = 'commit';
10897
10898 Roo.data.Record.prototype = {
10899     /**
10900      * Readonly flag - true if this record has been modified.
10901      * @type Boolean
10902      */
10903     dirty : false,
10904     editing : false,
10905     error: null,
10906     modified: null,
10907
10908     // private
10909     join : function(store){
10910         this.store = store;
10911     },
10912
10913     /**
10914      * Set the named field to the specified value.
10915      * @param {String} name The name of the field to set.
10916      * @param {Object} value The value to set the field to.
10917      */
10918     set : function(name, value){
10919         if(this.data[name] == value){
10920             return;
10921         }
10922         this.dirty = true;
10923         if(!this.modified){
10924             this.modified = {};
10925         }
10926         if(typeof this.modified[name] == 'undefined'){
10927             this.modified[name] = this.data[name];
10928         }
10929         this.data[name] = value;
10930         if(!this.editing && this.store){
10931             this.store.afterEdit(this);
10932         }       
10933     },
10934
10935     /**
10936      * Get the value of the named field.
10937      * @param {String} name The name of the field to get the value of.
10938      * @return {Object} The value of the field.
10939      */
10940     get : function(name){
10941         return this.data[name]; 
10942     },
10943
10944     // private
10945     beginEdit : function(){
10946         this.editing = true;
10947         this.modified = {}; 
10948     },
10949
10950     // private
10951     cancelEdit : function(){
10952         this.editing = false;
10953         delete this.modified;
10954     },
10955
10956     // private
10957     endEdit : function(){
10958         this.editing = false;
10959         if(this.dirty && this.store){
10960             this.store.afterEdit(this);
10961         }
10962     },
10963
10964     /**
10965      * Usually called by the {@link Roo.data.Store} which owns the Record.
10966      * Rejects all changes made to the Record since either creation, or the last commit operation.
10967      * Modified fields are reverted to their original values.
10968      * <p>
10969      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10970      * of reject operations.
10971      */
10972     reject : function(){
10973         var m = this.modified;
10974         for(var n in m){
10975             if(typeof m[n] != "function"){
10976                 this.data[n] = m[n];
10977             }
10978         }
10979         this.dirty = false;
10980         delete this.modified;
10981         this.editing = false;
10982         if(this.store){
10983             this.store.afterReject(this);
10984         }
10985     },
10986
10987     /**
10988      * Usually called by the {@link Roo.data.Store} which owns the Record.
10989      * Commits all changes made to the Record since either creation, or the last commit operation.
10990      * <p>
10991      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10992      * of commit operations.
10993      */
10994     commit : function(){
10995         this.dirty = false;
10996         delete this.modified;
10997         this.editing = false;
10998         if(this.store){
10999             this.store.afterCommit(this);
11000         }
11001     },
11002
11003     // private
11004     hasError : function(){
11005         return this.error != null;
11006     },
11007
11008     // private
11009     clearError : function(){
11010         this.error = null;
11011     },
11012
11013     /**
11014      * Creates a copy of this record.
11015      * @param {String} id (optional) A new record id if you don't want to use this record's id
11016      * @return {Record}
11017      */
11018     copy : function(newId) {
11019         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11020     }
11021 };/*
11022  * Based on:
11023  * Ext JS Library 1.1.1
11024  * Copyright(c) 2006-2007, Ext JS, LLC.
11025  *
11026  * Originally Released Under LGPL - original licence link has changed is not relivant.
11027  *
11028  * Fork - LGPL
11029  * <script type="text/javascript">
11030  */
11031
11032
11033
11034 /**
11035  * @class Roo.data.Store
11036  * @extends Roo.util.Observable
11037  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11038  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11039  * <p>
11040  * 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
11041  * has no knowledge of the format of the data returned by the Proxy.<br>
11042  * <p>
11043  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11044  * instances from the data object. These records are cached and made available through accessor functions.
11045  * @constructor
11046  * Creates a new Store.
11047  * @param {Object} config A config object containing the objects needed for the Store to access data,
11048  * and read the data into Records.
11049  */
11050 Roo.data.Store = function(config){
11051     this.data = new Roo.util.MixedCollection(false);
11052     this.data.getKey = function(o){
11053         return o.id;
11054     };
11055     this.baseParams = {};
11056     // private
11057     this.paramNames = {
11058         "start" : "start",
11059         "limit" : "limit",
11060         "sort" : "sort",
11061         "dir" : "dir",
11062         "multisort" : "_multisort"
11063     };
11064
11065     if(config && config.data){
11066         this.inlineData = config.data;
11067         delete config.data;
11068     }
11069
11070     Roo.apply(this, config);
11071     
11072     if(this.reader){ // reader passed
11073         this.reader = Roo.factory(this.reader, Roo.data);
11074         this.reader.xmodule = this.xmodule || false;
11075         if(!this.recordType){
11076             this.recordType = this.reader.recordType;
11077         }
11078         if(this.reader.onMetaChange){
11079             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11080         }
11081     }
11082
11083     if(this.recordType){
11084         this.fields = this.recordType.prototype.fields;
11085     }
11086     this.modified = [];
11087
11088     this.addEvents({
11089         /**
11090          * @event datachanged
11091          * Fires when the data cache has changed, and a widget which is using this Store
11092          * as a Record cache should refresh its view.
11093          * @param {Store} this
11094          */
11095         datachanged : true,
11096         /**
11097          * @event metachange
11098          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11099          * @param {Store} this
11100          * @param {Object} meta The JSON metadata
11101          */
11102         metachange : true,
11103         /**
11104          * @event add
11105          * Fires when Records have been added to the Store
11106          * @param {Store} this
11107          * @param {Roo.data.Record[]} records The array of Records added
11108          * @param {Number} index The index at which the record(s) were added
11109          */
11110         add : true,
11111         /**
11112          * @event remove
11113          * Fires when a Record has been removed from the Store
11114          * @param {Store} this
11115          * @param {Roo.data.Record} record The Record that was removed
11116          * @param {Number} index The index at which the record was removed
11117          */
11118         remove : true,
11119         /**
11120          * @event update
11121          * Fires when a Record has been updated
11122          * @param {Store} this
11123          * @param {Roo.data.Record} record The Record that was updated
11124          * @param {String} operation The update operation being performed.  Value may be one of:
11125          * <pre><code>
11126  Roo.data.Record.EDIT
11127  Roo.data.Record.REJECT
11128  Roo.data.Record.COMMIT
11129          * </code></pre>
11130          */
11131         update : true,
11132         /**
11133          * @event clear
11134          * Fires when the data cache has been cleared.
11135          * @param {Store} this
11136          */
11137         clear : true,
11138         /**
11139          * @event beforeload
11140          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11141          * the load action will be canceled.
11142          * @param {Store} this
11143          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11144          */
11145         beforeload : true,
11146         /**
11147          * @event beforeloadadd
11148          * Fires after a new set of Records has been loaded.
11149          * @param {Store} this
11150          * @param {Roo.data.Record[]} records The Records that were loaded
11151          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11152          */
11153         beforeloadadd : true,
11154         /**
11155          * @event load
11156          * Fires after a new set of Records has been loaded, before they are added to the store.
11157          * @param {Store} this
11158          * @param {Roo.data.Record[]} records The Records that were loaded
11159          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11160          * @params {Object} return from reader
11161          */
11162         load : true,
11163         /**
11164          * @event loadexception
11165          * Fires if an exception occurs in the Proxy during loading.
11166          * Called with the signature of the Proxy's "loadexception" event.
11167          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11168          * 
11169          * @param {Proxy} 
11170          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11171          * @param {Object} load options 
11172          * @param {Object} jsonData from your request (normally this contains the Exception)
11173          */
11174         loadexception : true
11175     });
11176     
11177     if(this.proxy){
11178         this.proxy = Roo.factory(this.proxy, Roo.data);
11179         this.proxy.xmodule = this.xmodule || false;
11180         this.relayEvents(this.proxy,  ["loadexception"]);
11181     }
11182     this.sortToggle = {};
11183     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11184
11185     Roo.data.Store.superclass.constructor.call(this);
11186
11187     if(this.inlineData){
11188         this.loadData(this.inlineData);
11189         delete this.inlineData;
11190     }
11191 };
11192
11193 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11194      /**
11195     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11196     * without a remote query - used by combo/forms at present.
11197     */
11198     
11199     /**
11200     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11201     */
11202     /**
11203     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11204     */
11205     /**
11206     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11207     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11208     */
11209     /**
11210     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11211     * on any HTTP request
11212     */
11213     /**
11214     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11215     */
11216     /**
11217     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11218     */
11219     multiSort: false,
11220     /**
11221     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11222     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11223     */
11224     remoteSort : false,
11225
11226     /**
11227     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11228      * loaded or when a record is removed. (defaults to false).
11229     */
11230     pruneModifiedRecords : false,
11231
11232     // private
11233     lastOptions : null,
11234
11235     /**
11236      * Add Records to the Store and fires the add event.
11237      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11238      */
11239     add : function(records){
11240         records = [].concat(records);
11241         for(var i = 0, len = records.length; i < len; i++){
11242             records[i].join(this);
11243         }
11244         var index = this.data.length;
11245         this.data.addAll(records);
11246         this.fireEvent("add", this, records, index);
11247     },
11248
11249     /**
11250      * Remove a Record from the Store and fires the remove event.
11251      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11252      */
11253     remove : function(record){
11254         var index = this.data.indexOf(record);
11255         this.data.removeAt(index);
11256  
11257         if(this.pruneModifiedRecords){
11258             this.modified.remove(record);
11259         }
11260         this.fireEvent("remove", this, record, index);
11261     },
11262
11263     /**
11264      * Remove all Records from the Store and fires the clear event.
11265      */
11266     removeAll : function(){
11267         this.data.clear();
11268         if(this.pruneModifiedRecords){
11269             this.modified = [];
11270         }
11271         this.fireEvent("clear", this);
11272     },
11273
11274     /**
11275      * Inserts Records to the Store at the given index and fires the add event.
11276      * @param {Number} index The start index at which to insert the passed Records.
11277      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11278      */
11279     insert : function(index, records){
11280         records = [].concat(records);
11281         for(var i = 0, len = records.length; i < len; i++){
11282             this.data.insert(index, records[i]);
11283             records[i].join(this);
11284         }
11285         this.fireEvent("add", this, records, index);
11286     },
11287
11288     /**
11289      * Get the index within the cache of the passed Record.
11290      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11291      * @return {Number} The index of the passed Record. Returns -1 if not found.
11292      */
11293     indexOf : function(record){
11294         return this.data.indexOf(record);
11295     },
11296
11297     /**
11298      * Get the index within the cache of the Record with the passed id.
11299      * @param {String} id The id of the Record to find.
11300      * @return {Number} The index of the Record. Returns -1 if not found.
11301      */
11302     indexOfId : function(id){
11303         return this.data.indexOfKey(id);
11304     },
11305
11306     /**
11307      * Get the Record with the specified id.
11308      * @param {String} id The id of the Record to find.
11309      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11310      */
11311     getById : function(id){
11312         return this.data.key(id);
11313     },
11314
11315     /**
11316      * Get the Record at the specified index.
11317      * @param {Number} index The index of the Record to find.
11318      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11319      */
11320     getAt : function(index){
11321         return this.data.itemAt(index);
11322     },
11323
11324     /**
11325      * Returns a range of Records between specified indices.
11326      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11327      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11328      * @return {Roo.data.Record[]} An array of Records
11329      */
11330     getRange : function(start, end){
11331         return this.data.getRange(start, end);
11332     },
11333
11334     // private
11335     storeOptions : function(o){
11336         o = Roo.apply({}, o);
11337         delete o.callback;
11338         delete o.scope;
11339         this.lastOptions = o;
11340     },
11341
11342     /**
11343      * Loads the Record cache from the configured Proxy using the configured Reader.
11344      * <p>
11345      * If using remote paging, then the first load call must specify the <em>start</em>
11346      * and <em>limit</em> properties in the options.params property to establish the initial
11347      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11348      * <p>
11349      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11350      * and this call will return before the new data has been loaded. Perform any post-processing
11351      * in a callback function, or in a "load" event handler.</strong>
11352      * <p>
11353      * @param {Object} options An object containing properties which control loading options:<ul>
11354      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11355      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11356      * passed the following arguments:<ul>
11357      * <li>r : Roo.data.Record[]</li>
11358      * <li>options: Options object from the load call</li>
11359      * <li>success: Boolean success indicator</li></ul></li>
11360      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11361      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11362      * </ul>
11363      */
11364     load : function(options){
11365         options = options || {};
11366         if(this.fireEvent("beforeload", this, options) !== false){
11367             this.storeOptions(options);
11368             var p = Roo.apply(options.params || {}, this.baseParams);
11369             // if meta was not loaded from remote source.. try requesting it.
11370             if (!this.reader.metaFromRemote) {
11371                 p._requestMeta = 1;
11372             }
11373             if(this.sortInfo && this.remoteSort){
11374                 var pn = this.paramNames;
11375                 p[pn["sort"]] = this.sortInfo.field;
11376                 p[pn["dir"]] = this.sortInfo.direction;
11377             }
11378             if (this.multiSort) {
11379                 var pn = this.paramNames;
11380                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11381             }
11382             
11383             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11384         }
11385     },
11386
11387     /**
11388      * Reloads the Record cache from the configured Proxy using the configured Reader and
11389      * the options from the last load operation performed.
11390      * @param {Object} options (optional) An object containing properties which may override the options
11391      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11392      * the most recently used options are reused).
11393      */
11394     reload : function(options){
11395         this.load(Roo.applyIf(options||{}, this.lastOptions));
11396     },
11397
11398     // private
11399     // Called as a callback by the Reader during a load operation.
11400     loadRecords : function(o, options, success){
11401         if(!o || success === false){
11402             if(success !== false){
11403                 this.fireEvent("load", this, [], options, o);
11404             }
11405             if(options.callback){
11406                 options.callback.call(options.scope || this, [], options, false);
11407             }
11408             return;
11409         }
11410         // if data returned failure - throw an exception.
11411         if (o.success === false) {
11412             // show a message if no listener is registered.
11413             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11414                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11415             }
11416             // loadmask wil be hooked into this..
11417             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11418             return;
11419         }
11420         var r = o.records, t = o.totalRecords || r.length;
11421         
11422         this.fireEvent("beforeloadadd", this, r, options, o);
11423         
11424         if(!options || options.add !== true){
11425             if(this.pruneModifiedRecords){
11426                 this.modified = [];
11427             }
11428             for(var i = 0, len = r.length; i < len; i++){
11429                 r[i].join(this);
11430             }
11431             if(this.snapshot){
11432                 this.data = this.snapshot;
11433                 delete this.snapshot;
11434             }
11435             this.data.clear();
11436             this.data.addAll(r);
11437             this.totalLength = t;
11438             this.applySort();
11439             this.fireEvent("datachanged", this);
11440         }else{
11441             this.totalLength = Math.max(t, this.data.length+r.length);
11442             this.add(r);
11443         }
11444         
11445         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11446                 
11447             var e = new Roo.data.Record({});
11448
11449             e.set(this.parent.displayField, this.parent.emptyTitle);
11450             e.set(this.parent.valueField, '');
11451
11452             this.insert(0, e);
11453         }
11454             
11455         this.fireEvent("load", this, r, options, o);
11456         if(options.callback){
11457             options.callback.call(options.scope || this, r, options, true);
11458         }
11459     },
11460
11461
11462     /**
11463      * Loads data from a passed data block. A Reader which understands the format of the data
11464      * must have been configured in the constructor.
11465      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11466      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11467      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11468      */
11469     loadData : function(o, append){
11470         var r = this.reader.readRecords(o);
11471         this.loadRecords(r, {add: append}, true);
11472     },
11473
11474     /**
11475      * Gets the number of cached records.
11476      * <p>
11477      * <em>If using paging, this may not be the total size of the dataset. If the data object
11478      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11479      * the data set size</em>
11480      */
11481     getCount : function(){
11482         return this.data.length || 0;
11483     },
11484
11485     /**
11486      * Gets the total number of records in the dataset as returned by the server.
11487      * <p>
11488      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11489      * the dataset size</em>
11490      */
11491     getTotalCount : function(){
11492         return this.totalLength || 0;
11493     },
11494
11495     /**
11496      * Returns the sort state of the Store as an object with two properties:
11497      * <pre><code>
11498  field {String} The name of the field by which the Records are sorted
11499  direction {String} The sort order, "ASC" or "DESC"
11500      * </code></pre>
11501      */
11502     getSortState : function(){
11503         return this.sortInfo;
11504     },
11505
11506     // private
11507     applySort : function(){
11508         if(this.sortInfo && !this.remoteSort){
11509             var s = this.sortInfo, f = s.field;
11510             var st = this.fields.get(f).sortType;
11511             var fn = function(r1, r2){
11512                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11513                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11514             };
11515             this.data.sort(s.direction, fn);
11516             if(this.snapshot && this.snapshot != this.data){
11517                 this.snapshot.sort(s.direction, fn);
11518             }
11519         }
11520     },
11521
11522     /**
11523      * Sets the default sort column and order to be used by the next load operation.
11524      * @param {String} fieldName The name of the field to sort by.
11525      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11526      */
11527     setDefaultSort : function(field, dir){
11528         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11529     },
11530
11531     /**
11532      * Sort the Records.
11533      * If remote sorting is used, the sort is performed on the server, and the cache is
11534      * reloaded. If local sorting is used, the cache is sorted internally.
11535      * @param {String} fieldName The name of the field to sort by.
11536      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11537      */
11538     sort : function(fieldName, dir){
11539         var f = this.fields.get(fieldName);
11540         if(!dir){
11541             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11542             
11543             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11544                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11545             }else{
11546                 dir = f.sortDir;
11547             }
11548         }
11549         this.sortToggle[f.name] = dir;
11550         this.sortInfo = {field: f.name, direction: dir};
11551         if(!this.remoteSort){
11552             this.applySort();
11553             this.fireEvent("datachanged", this);
11554         }else{
11555             this.load(this.lastOptions);
11556         }
11557     },
11558
11559     /**
11560      * Calls the specified function for each of the Records in the cache.
11561      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11562      * Returning <em>false</em> aborts and exits the iteration.
11563      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11564      */
11565     each : function(fn, scope){
11566         this.data.each(fn, scope);
11567     },
11568
11569     /**
11570      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11571      * (e.g., during paging).
11572      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11573      */
11574     getModifiedRecords : function(){
11575         return this.modified;
11576     },
11577
11578     // private
11579     createFilterFn : function(property, value, anyMatch){
11580         if(!value.exec){ // not a regex
11581             value = String(value);
11582             if(value.length == 0){
11583                 return false;
11584             }
11585             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11586         }
11587         return function(r){
11588             return value.test(r.data[property]);
11589         };
11590     },
11591
11592     /**
11593      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11594      * @param {String} property A field on your records
11595      * @param {Number} start The record index to start at (defaults to 0)
11596      * @param {Number} end The last record index to include (defaults to length - 1)
11597      * @return {Number} The sum
11598      */
11599     sum : function(property, start, end){
11600         var rs = this.data.items, v = 0;
11601         start = start || 0;
11602         end = (end || end === 0) ? end : rs.length-1;
11603
11604         for(var i = start; i <= end; i++){
11605             v += (rs[i].data[property] || 0);
11606         }
11607         return v;
11608     },
11609
11610     /**
11611      * Filter the records by a specified property.
11612      * @param {String} field A field on your records
11613      * @param {String/RegExp} value Either a string that the field
11614      * should start with or a RegExp to test against the field
11615      * @param {Boolean} anyMatch True to match any part not just the beginning
11616      */
11617     filter : function(property, value, anyMatch){
11618         var fn = this.createFilterFn(property, value, anyMatch);
11619         return fn ? this.filterBy(fn) : this.clearFilter();
11620     },
11621
11622     /**
11623      * Filter by a function. The specified function will be called with each
11624      * record in this data source. If the function returns true the record is included,
11625      * otherwise it is filtered.
11626      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11627      * @param {Object} scope (optional) The scope of the function (defaults to this)
11628      */
11629     filterBy : function(fn, scope){
11630         this.snapshot = this.snapshot || this.data;
11631         this.data = this.queryBy(fn, scope||this);
11632         this.fireEvent("datachanged", this);
11633     },
11634
11635     /**
11636      * Query the records by a specified property.
11637      * @param {String} field A field on your records
11638      * @param {String/RegExp} value Either a string that the field
11639      * should start with or a RegExp to test against the field
11640      * @param {Boolean} anyMatch True to match any part not just the beginning
11641      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11642      */
11643     query : function(property, value, anyMatch){
11644         var fn = this.createFilterFn(property, value, anyMatch);
11645         return fn ? this.queryBy(fn) : this.data.clone();
11646     },
11647
11648     /**
11649      * Query by a function. The specified function will be called with each
11650      * record in this data source. If the function returns true the record is included
11651      * in the results.
11652      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11653      * @param {Object} scope (optional) The scope of the function (defaults to this)
11654       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11655      **/
11656     queryBy : function(fn, scope){
11657         var data = this.snapshot || this.data;
11658         return data.filterBy(fn, scope||this);
11659     },
11660
11661     /**
11662      * Collects unique values for a particular dataIndex from this store.
11663      * @param {String} dataIndex The property to collect
11664      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11665      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11666      * @return {Array} An array of the unique values
11667      **/
11668     collect : function(dataIndex, allowNull, bypassFilter){
11669         var d = (bypassFilter === true && this.snapshot) ?
11670                 this.snapshot.items : this.data.items;
11671         var v, sv, r = [], l = {};
11672         for(var i = 0, len = d.length; i < len; i++){
11673             v = d[i].data[dataIndex];
11674             sv = String(v);
11675             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11676                 l[sv] = true;
11677                 r[r.length] = v;
11678             }
11679         }
11680         return r;
11681     },
11682
11683     /**
11684      * Revert to a view of the Record cache with no filtering applied.
11685      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11686      */
11687     clearFilter : function(suppressEvent){
11688         if(this.snapshot && this.snapshot != this.data){
11689             this.data = this.snapshot;
11690             delete this.snapshot;
11691             if(suppressEvent !== true){
11692                 this.fireEvent("datachanged", this);
11693             }
11694         }
11695     },
11696
11697     // private
11698     afterEdit : function(record){
11699         if(this.modified.indexOf(record) == -1){
11700             this.modified.push(record);
11701         }
11702         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11703     },
11704     
11705     // private
11706     afterReject : function(record){
11707         this.modified.remove(record);
11708         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11709     },
11710
11711     // private
11712     afterCommit : function(record){
11713         this.modified.remove(record);
11714         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11715     },
11716
11717     /**
11718      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11719      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11720      */
11721     commitChanges : function(){
11722         var m = this.modified.slice(0);
11723         this.modified = [];
11724         for(var i = 0, len = m.length; i < len; i++){
11725             m[i].commit();
11726         }
11727     },
11728
11729     /**
11730      * Cancel outstanding changes on all changed records.
11731      */
11732     rejectChanges : function(){
11733         var m = this.modified.slice(0);
11734         this.modified = [];
11735         for(var i = 0, len = m.length; i < len; i++){
11736             m[i].reject();
11737         }
11738     },
11739
11740     onMetaChange : function(meta, rtype, o){
11741         this.recordType = rtype;
11742         this.fields = rtype.prototype.fields;
11743         delete this.snapshot;
11744         this.sortInfo = meta.sortInfo || this.sortInfo;
11745         this.modified = [];
11746         this.fireEvent('metachange', this, this.reader.meta);
11747     },
11748     
11749     moveIndex : function(data, type)
11750     {
11751         var index = this.indexOf(data);
11752         
11753         var newIndex = index + type;
11754         
11755         this.remove(data);
11756         
11757         this.insert(newIndex, data);
11758         
11759     }
11760 });/*
11761  * Based on:
11762  * Ext JS Library 1.1.1
11763  * Copyright(c) 2006-2007, Ext JS, LLC.
11764  *
11765  * Originally Released Under LGPL - original licence link has changed is not relivant.
11766  *
11767  * Fork - LGPL
11768  * <script type="text/javascript">
11769  */
11770
11771 /**
11772  * @class Roo.data.SimpleStore
11773  * @extends Roo.data.Store
11774  * Small helper class to make creating Stores from Array data easier.
11775  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11776  * @cfg {Array} fields An array of field definition objects, or field name strings.
11777  * @cfg {Array} data The multi-dimensional array of data
11778  * @constructor
11779  * @param {Object} config
11780  */
11781 Roo.data.SimpleStore = function(config){
11782     Roo.data.SimpleStore.superclass.constructor.call(this, {
11783         isLocal : true,
11784         reader: new Roo.data.ArrayReader({
11785                 id: config.id
11786             },
11787             Roo.data.Record.create(config.fields)
11788         ),
11789         proxy : new Roo.data.MemoryProxy(config.data)
11790     });
11791     this.load();
11792 };
11793 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11794  * Based on:
11795  * Ext JS Library 1.1.1
11796  * Copyright(c) 2006-2007, Ext JS, LLC.
11797  *
11798  * Originally Released Under LGPL - original licence link has changed is not relivant.
11799  *
11800  * Fork - LGPL
11801  * <script type="text/javascript">
11802  */
11803
11804 /**
11805 /**
11806  * @extends Roo.data.Store
11807  * @class Roo.data.JsonStore
11808  * Small helper class to make creating Stores for JSON data easier. <br/>
11809 <pre><code>
11810 var store = new Roo.data.JsonStore({
11811     url: 'get-images.php',
11812     root: 'images',
11813     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11814 });
11815 </code></pre>
11816  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11817  * JsonReader and HttpProxy (unless inline data is provided).</b>
11818  * @cfg {Array} fields An array of field definition objects, or field name strings.
11819  * @constructor
11820  * @param {Object} config
11821  */
11822 Roo.data.JsonStore = function(c){
11823     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11824         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11825         reader: new Roo.data.JsonReader(c, c.fields)
11826     }));
11827 };
11828 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11829  * Based on:
11830  * Ext JS Library 1.1.1
11831  * Copyright(c) 2006-2007, Ext JS, LLC.
11832  *
11833  * Originally Released Under LGPL - original licence link has changed is not relivant.
11834  *
11835  * Fork - LGPL
11836  * <script type="text/javascript">
11837  */
11838
11839  
11840 Roo.data.Field = function(config){
11841     if(typeof config == "string"){
11842         config = {name: config};
11843     }
11844     Roo.apply(this, config);
11845     
11846     if(!this.type){
11847         this.type = "auto";
11848     }
11849     
11850     var st = Roo.data.SortTypes;
11851     // named sortTypes are supported, here we look them up
11852     if(typeof this.sortType == "string"){
11853         this.sortType = st[this.sortType];
11854     }
11855     
11856     // set default sortType for strings and dates
11857     if(!this.sortType){
11858         switch(this.type){
11859             case "string":
11860                 this.sortType = st.asUCString;
11861                 break;
11862             case "date":
11863                 this.sortType = st.asDate;
11864                 break;
11865             default:
11866                 this.sortType = st.none;
11867         }
11868     }
11869
11870     // define once
11871     var stripRe = /[\$,%]/g;
11872
11873     // prebuilt conversion function for this field, instead of
11874     // switching every time we're reading a value
11875     if(!this.convert){
11876         var cv, dateFormat = this.dateFormat;
11877         switch(this.type){
11878             case "":
11879             case "auto":
11880             case undefined:
11881                 cv = function(v){ return v; };
11882                 break;
11883             case "string":
11884                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11885                 break;
11886             case "int":
11887                 cv = function(v){
11888                     return v !== undefined && v !== null && v !== '' ?
11889                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11890                     };
11891                 break;
11892             case "float":
11893                 cv = function(v){
11894                     return v !== undefined && v !== null && v !== '' ?
11895                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11896                     };
11897                 break;
11898             case "bool":
11899             case "boolean":
11900                 cv = function(v){ return v === true || v === "true" || v == 1; };
11901                 break;
11902             case "date":
11903                 cv = function(v){
11904                     if(!v){
11905                         return '';
11906                     }
11907                     if(v instanceof Date){
11908                         return v;
11909                     }
11910                     if(dateFormat){
11911                         if(dateFormat == "timestamp"){
11912                             return new Date(v*1000);
11913                         }
11914                         return Date.parseDate(v, dateFormat);
11915                     }
11916                     var parsed = Date.parse(v);
11917                     return parsed ? new Date(parsed) : null;
11918                 };
11919              break;
11920             
11921         }
11922         this.convert = cv;
11923     }
11924 };
11925
11926 Roo.data.Field.prototype = {
11927     dateFormat: null,
11928     defaultValue: "",
11929     mapping: null,
11930     sortType : null,
11931     sortDir : "ASC"
11932 };/*
11933  * Based on:
11934  * Ext JS Library 1.1.1
11935  * Copyright(c) 2006-2007, Ext JS, LLC.
11936  *
11937  * Originally Released Under LGPL - original licence link has changed is not relivant.
11938  *
11939  * Fork - LGPL
11940  * <script type="text/javascript">
11941  */
11942  
11943 // Base class for reading structured data from a data source.  This class is intended to be
11944 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11945
11946 /**
11947  * @class Roo.data.DataReader
11948  * Base class for reading structured data from a data source.  This class is intended to be
11949  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11950  */
11951
11952 Roo.data.DataReader = function(meta, recordType){
11953     
11954     this.meta = meta;
11955     
11956     this.recordType = recordType instanceof Array ? 
11957         Roo.data.Record.create(recordType) : recordType;
11958 };
11959
11960 Roo.data.DataReader.prototype = {
11961      /**
11962      * Create an empty record
11963      * @param {Object} data (optional) - overlay some values
11964      * @return {Roo.data.Record} record created.
11965      */
11966     newRow :  function(d) {
11967         var da =  {};
11968         this.recordType.prototype.fields.each(function(c) {
11969             switch( c.type) {
11970                 case 'int' : da[c.name] = 0; break;
11971                 case 'date' : da[c.name] = new Date(); break;
11972                 case 'float' : da[c.name] = 0.0; break;
11973                 case 'boolean' : da[c.name] = false; break;
11974                 default : da[c.name] = ""; break;
11975             }
11976             
11977         });
11978         return new this.recordType(Roo.apply(da, d));
11979     }
11980     
11981 };/*
11982  * Based on:
11983  * Ext JS Library 1.1.1
11984  * Copyright(c) 2006-2007, Ext JS, LLC.
11985  *
11986  * Originally Released Under LGPL - original licence link has changed is not relivant.
11987  *
11988  * Fork - LGPL
11989  * <script type="text/javascript">
11990  */
11991
11992 /**
11993  * @class Roo.data.DataProxy
11994  * @extends Roo.data.Observable
11995  * This class is an abstract base class for implementations which provide retrieval of
11996  * unformatted data objects.<br>
11997  * <p>
11998  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11999  * (of the appropriate type which knows how to parse the data object) to provide a block of
12000  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12001  * <p>
12002  * Custom implementations must implement the load method as described in
12003  * {@link Roo.data.HttpProxy#load}.
12004  */
12005 Roo.data.DataProxy = function(){
12006     this.addEvents({
12007         /**
12008          * @event beforeload
12009          * Fires before a network request is made to retrieve a data object.
12010          * @param {Object} This DataProxy object.
12011          * @param {Object} params The params parameter to the load function.
12012          */
12013         beforeload : true,
12014         /**
12015          * @event load
12016          * Fires before the load method's callback is called.
12017          * @param {Object} This DataProxy object.
12018          * @param {Object} o The data object.
12019          * @param {Object} arg The callback argument object passed to the load function.
12020          */
12021         load : true,
12022         /**
12023          * @event loadexception
12024          * Fires if an Exception occurs during data retrieval.
12025          * @param {Object} This DataProxy object.
12026          * @param {Object} o The data object.
12027          * @param {Object} arg The callback argument object passed to the load function.
12028          * @param {Object} e The Exception.
12029          */
12030         loadexception : true
12031     });
12032     Roo.data.DataProxy.superclass.constructor.call(this);
12033 };
12034
12035 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12036
12037     /**
12038      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12039      */
12040 /*
12041  * Based on:
12042  * Ext JS Library 1.1.1
12043  * Copyright(c) 2006-2007, Ext JS, LLC.
12044  *
12045  * Originally Released Under LGPL - original licence link has changed is not relivant.
12046  *
12047  * Fork - LGPL
12048  * <script type="text/javascript">
12049  */
12050 /**
12051  * @class Roo.data.MemoryProxy
12052  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12053  * to the Reader when its load method is called.
12054  * @constructor
12055  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12056  */
12057 Roo.data.MemoryProxy = function(data){
12058     if (data.data) {
12059         data = data.data;
12060     }
12061     Roo.data.MemoryProxy.superclass.constructor.call(this);
12062     this.data = data;
12063 };
12064
12065 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12066     
12067     /**
12068      * Load data from the requested source (in this case an in-memory
12069      * data object passed to the constructor), read the data object into
12070      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12071      * process that block using the passed callback.
12072      * @param {Object} params This parameter is not used by the MemoryProxy class.
12073      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12074      * object into a block of Roo.data.Records.
12075      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12076      * The function must be passed <ul>
12077      * <li>The Record block object</li>
12078      * <li>The "arg" argument from the load function</li>
12079      * <li>A boolean success indicator</li>
12080      * </ul>
12081      * @param {Object} scope The scope in which to call the callback
12082      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12083      */
12084     load : function(params, reader, callback, scope, arg){
12085         params = params || {};
12086         var result;
12087         try {
12088             result = reader.readRecords(this.data);
12089         }catch(e){
12090             this.fireEvent("loadexception", this, arg, null, e);
12091             callback.call(scope, null, arg, false);
12092             return;
12093         }
12094         callback.call(scope, result, arg, true);
12095     },
12096     
12097     // private
12098     update : function(params, records){
12099         
12100     }
12101 });/*
12102  * Based on:
12103  * Ext JS Library 1.1.1
12104  * Copyright(c) 2006-2007, Ext JS, LLC.
12105  *
12106  * Originally Released Under LGPL - original licence link has changed is not relivant.
12107  *
12108  * Fork - LGPL
12109  * <script type="text/javascript">
12110  */
12111 /**
12112  * @class Roo.data.HttpProxy
12113  * @extends Roo.data.DataProxy
12114  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12115  * configured to reference a certain URL.<br><br>
12116  * <p>
12117  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12118  * from which the running page was served.<br><br>
12119  * <p>
12120  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12121  * <p>
12122  * Be aware that to enable the browser to parse an XML document, the server must set
12123  * the Content-Type header in the HTTP response to "text/xml".
12124  * @constructor
12125  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12126  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12127  * will be used to make the request.
12128  */
12129 Roo.data.HttpProxy = function(conn){
12130     Roo.data.HttpProxy.superclass.constructor.call(this);
12131     // is conn a conn config or a real conn?
12132     this.conn = conn;
12133     this.useAjax = !conn || !conn.events;
12134   
12135 };
12136
12137 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12138     // thse are take from connection...
12139     
12140     /**
12141      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12142      */
12143     /**
12144      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12145      * extra parameters to each request made by this object. (defaults to undefined)
12146      */
12147     /**
12148      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12149      *  to each request made by this object. (defaults to undefined)
12150      */
12151     /**
12152      * @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)
12153      */
12154     /**
12155      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12156      */
12157      /**
12158      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12159      * @type Boolean
12160      */
12161   
12162
12163     /**
12164      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12165      * @type Boolean
12166      */
12167     /**
12168      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12169      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12170      * a finer-grained basis than the DataProxy events.
12171      */
12172     getConnection : function(){
12173         return this.useAjax ? Roo.Ajax : this.conn;
12174     },
12175
12176     /**
12177      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12178      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12179      * process that block using the passed callback.
12180      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12181      * for the request to the remote server.
12182      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12183      * object into a block of Roo.data.Records.
12184      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12185      * The function must be passed <ul>
12186      * <li>The Record block object</li>
12187      * <li>The "arg" argument from the load function</li>
12188      * <li>A boolean success indicator</li>
12189      * </ul>
12190      * @param {Object} scope The scope in which to call the callback
12191      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12192      */
12193     load : function(params, reader, callback, scope, arg){
12194         if(this.fireEvent("beforeload", this, params) !== false){
12195             var  o = {
12196                 params : params || {},
12197                 request: {
12198                     callback : callback,
12199                     scope : scope,
12200                     arg : arg
12201                 },
12202                 reader: reader,
12203                 callback : this.loadResponse,
12204                 scope: this
12205             };
12206             if(this.useAjax){
12207                 Roo.applyIf(o, this.conn);
12208                 if(this.activeRequest){
12209                     Roo.Ajax.abort(this.activeRequest);
12210                 }
12211                 this.activeRequest = Roo.Ajax.request(o);
12212             }else{
12213                 this.conn.request(o);
12214             }
12215         }else{
12216             callback.call(scope||this, null, arg, false);
12217         }
12218     },
12219
12220     // private
12221     loadResponse : function(o, success, response){
12222         delete this.activeRequest;
12223         if(!success){
12224             this.fireEvent("loadexception", this, o, response);
12225             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12226             return;
12227         }
12228         var result;
12229         try {
12230             result = o.reader.read(response);
12231         }catch(e){
12232             this.fireEvent("loadexception", this, o, response, e);
12233             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12234             return;
12235         }
12236         
12237         this.fireEvent("load", this, o, o.request.arg);
12238         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12239     },
12240
12241     // private
12242     update : function(dataSet){
12243
12244     },
12245
12246     // private
12247     updateResponse : function(dataSet){
12248
12249     }
12250 });/*
12251  * Based on:
12252  * Ext JS Library 1.1.1
12253  * Copyright(c) 2006-2007, Ext JS, LLC.
12254  *
12255  * Originally Released Under LGPL - original licence link has changed is not relivant.
12256  *
12257  * Fork - LGPL
12258  * <script type="text/javascript">
12259  */
12260
12261 /**
12262  * @class Roo.data.ScriptTagProxy
12263  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12264  * other than the originating domain of the running page.<br><br>
12265  * <p>
12266  * <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
12267  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12268  * <p>
12269  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12270  * source code that is used as the source inside a &lt;script> tag.<br><br>
12271  * <p>
12272  * In order for the browser to process the returned data, the server must wrap the data object
12273  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12274  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12275  * depending on whether the callback name was passed:
12276  * <p>
12277  * <pre><code>
12278 boolean scriptTag = false;
12279 String cb = request.getParameter("callback");
12280 if (cb != null) {
12281     scriptTag = true;
12282     response.setContentType("text/javascript");
12283 } else {
12284     response.setContentType("application/x-json");
12285 }
12286 Writer out = response.getWriter();
12287 if (scriptTag) {
12288     out.write(cb + "(");
12289 }
12290 out.print(dataBlock.toJsonString());
12291 if (scriptTag) {
12292     out.write(");");
12293 }
12294 </pre></code>
12295  *
12296  * @constructor
12297  * @param {Object} config A configuration object.
12298  */
12299 Roo.data.ScriptTagProxy = function(config){
12300     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12301     Roo.apply(this, config);
12302     this.head = document.getElementsByTagName("head")[0];
12303 };
12304
12305 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12306
12307 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12308     /**
12309      * @cfg {String} url The URL from which to request the data object.
12310      */
12311     /**
12312      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12313      */
12314     timeout : 30000,
12315     /**
12316      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12317      * the server the name of the callback function set up by the load call to process the returned data object.
12318      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12319      * javascript output which calls this named function passing the data object as its only parameter.
12320      */
12321     callbackParam : "callback",
12322     /**
12323      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12324      * name to the request.
12325      */
12326     nocache : true,
12327
12328     /**
12329      * Load data from the configured URL, read the data object into
12330      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12331      * process that block using the passed callback.
12332      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12333      * for the request to the remote server.
12334      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12335      * object into a block of Roo.data.Records.
12336      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12337      * The function must be passed <ul>
12338      * <li>The Record block object</li>
12339      * <li>The "arg" argument from the load function</li>
12340      * <li>A boolean success indicator</li>
12341      * </ul>
12342      * @param {Object} scope The scope in which to call the callback
12343      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12344      */
12345     load : function(params, reader, callback, scope, arg){
12346         if(this.fireEvent("beforeload", this, params) !== false){
12347
12348             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12349
12350             var url = this.url;
12351             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12352             if(this.nocache){
12353                 url += "&_dc=" + (new Date().getTime());
12354             }
12355             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12356             var trans = {
12357                 id : transId,
12358                 cb : "stcCallback"+transId,
12359                 scriptId : "stcScript"+transId,
12360                 params : params,
12361                 arg : arg,
12362                 url : url,
12363                 callback : callback,
12364                 scope : scope,
12365                 reader : reader
12366             };
12367             var conn = this;
12368
12369             window[trans.cb] = function(o){
12370                 conn.handleResponse(o, trans);
12371             };
12372
12373             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12374
12375             if(this.autoAbort !== false){
12376                 this.abort();
12377             }
12378
12379             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12380
12381             var script = document.createElement("script");
12382             script.setAttribute("src", url);
12383             script.setAttribute("type", "text/javascript");
12384             script.setAttribute("id", trans.scriptId);
12385             this.head.appendChild(script);
12386
12387             this.trans = trans;
12388         }else{
12389             callback.call(scope||this, null, arg, false);
12390         }
12391     },
12392
12393     // private
12394     isLoading : function(){
12395         return this.trans ? true : false;
12396     },
12397
12398     /**
12399      * Abort the current server request.
12400      */
12401     abort : function(){
12402         if(this.isLoading()){
12403             this.destroyTrans(this.trans);
12404         }
12405     },
12406
12407     // private
12408     destroyTrans : function(trans, isLoaded){
12409         this.head.removeChild(document.getElementById(trans.scriptId));
12410         clearTimeout(trans.timeoutId);
12411         if(isLoaded){
12412             window[trans.cb] = undefined;
12413             try{
12414                 delete window[trans.cb];
12415             }catch(e){}
12416         }else{
12417             // if hasn't been loaded, wait for load to remove it to prevent script error
12418             window[trans.cb] = function(){
12419                 window[trans.cb] = undefined;
12420                 try{
12421                     delete window[trans.cb];
12422                 }catch(e){}
12423             };
12424         }
12425     },
12426
12427     // private
12428     handleResponse : function(o, trans){
12429         this.trans = false;
12430         this.destroyTrans(trans, true);
12431         var result;
12432         try {
12433             result = trans.reader.readRecords(o);
12434         }catch(e){
12435             this.fireEvent("loadexception", this, o, trans.arg, e);
12436             trans.callback.call(trans.scope||window, null, trans.arg, false);
12437             return;
12438         }
12439         this.fireEvent("load", this, o, trans.arg);
12440         trans.callback.call(trans.scope||window, result, trans.arg, true);
12441     },
12442
12443     // private
12444     handleFailure : function(trans){
12445         this.trans = false;
12446         this.destroyTrans(trans, false);
12447         this.fireEvent("loadexception", this, null, trans.arg);
12448         trans.callback.call(trans.scope||window, null, trans.arg, false);
12449     }
12450 });/*
12451  * Based on:
12452  * Ext JS Library 1.1.1
12453  * Copyright(c) 2006-2007, Ext JS, LLC.
12454  *
12455  * Originally Released Under LGPL - original licence link has changed is not relivant.
12456  *
12457  * Fork - LGPL
12458  * <script type="text/javascript">
12459  */
12460
12461 /**
12462  * @class Roo.data.JsonReader
12463  * @extends Roo.data.DataReader
12464  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12465  * based on mappings in a provided Roo.data.Record constructor.
12466  * 
12467  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12468  * in the reply previously. 
12469  * 
12470  * <p>
12471  * Example code:
12472  * <pre><code>
12473 var RecordDef = Roo.data.Record.create([
12474     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12475     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12476 ]);
12477 var myReader = new Roo.data.JsonReader({
12478     totalProperty: "results",    // The property which contains the total dataset size (optional)
12479     root: "rows",                // The property which contains an Array of row objects
12480     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12481 }, RecordDef);
12482 </code></pre>
12483  * <p>
12484  * This would consume a JSON file like this:
12485  * <pre><code>
12486 { 'results': 2, 'rows': [
12487     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12488     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12489 }
12490 </code></pre>
12491  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12492  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12493  * paged from the remote server.
12494  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12495  * @cfg {String} root name of the property which contains the Array of row objects.
12496  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12497  * @cfg {Array} fields Array of field definition objects
12498  * @constructor
12499  * Create a new JsonReader
12500  * @param {Object} meta Metadata configuration options
12501  * @param {Object} recordType Either an Array of field definition objects,
12502  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12503  */
12504 Roo.data.JsonReader = function(meta, recordType){
12505     
12506     meta = meta || {};
12507     // set some defaults:
12508     Roo.applyIf(meta, {
12509         totalProperty: 'total',
12510         successProperty : 'success',
12511         root : 'data',
12512         id : 'id'
12513     });
12514     
12515     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12516 };
12517 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12518     
12519     /**
12520      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12521      * Used by Store query builder to append _requestMeta to params.
12522      * 
12523      */
12524     metaFromRemote : false,
12525     /**
12526      * This method is only used by a DataProxy which has retrieved data from a remote server.
12527      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12528      * @return {Object} data A data block which is used by an Roo.data.Store object as
12529      * a cache of Roo.data.Records.
12530      */
12531     read : function(response){
12532         var json = response.responseText;
12533        
12534         var o = /* eval:var:o */ eval("("+json+")");
12535         if(!o) {
12536             throw {message: "JsonReader.read: Json object not found"};
12537         }
12538         
12539         if(o.metaData){
12540             
12541             delete this.ef;
12542             this.metaFromRemote = true;
12543             this.meta = o.metaData;
12544             this.recordType = Roo.data.Record.create(o.metaData.fields);
12545             this.onMetaChange(this.meta, this.recordType, o);
12546         }
12547         return this.readRecords(o);
12548     },
12549
12550     // private function a store will implement
12551     onMetaChange : function(meta, recordType, o){
12552
12553     },
12554
12555     /**
12556          * @ignore
12557          */
12558     simpleAccess: function(obj, subsc) {
12559         return obj[subsc];
12560     },
12561
12562         /**
12563          * @ignore
12564          */
12565     getJsonAccessor: function(){
12566         var re = /[\[\.]/;
12567         return function(expr) {
12568             try {
12569                 return(re.test(expr))
12570                     ? new Function("obj", "return obj." + expr)
12571                     : function(obj){
12572                         return obj[expr];
12573                     };
12574             } catch(e){}
12575             return Roo.emptyFn;
12576         };
12577     }(),
12578
12579     /**
12580      * Create a data block containing Roo.data.Records from an XML document.
12581      * @param {Object} o An object which contains an Array of row objects in the property specified
12582      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12583      * which contains the total size of the dataset.
12584      * @return {Object} data A data block which is used by an Roo.data.Store object as
12585      * a cache of Roo.data.Records.
12586      */
12587     readRecords : function(o){
12588         /**
12589          * After any data loads, the raw JSON data is available for further custom processing.
12590          * @type Object
12591          */
12592         this.o = o;
12593         var s = this.meta, Record = this.recordType,
12594             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12595
12596 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12597         if (!this.ef) {
12598             if(s.totalProperty) {
12599                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12600                 }
12601                 if(s.successProperty) {
12602                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12603                 }
12604                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12605                 if (s.id) {
12606                         var g = this.getJsonAccessor(s.id);
12607                         this.getId = function(rec) {
12608                                 var r = g(rec);  
12609                                 return (r === undefined || r === "") ? null : r;
12610                         };
12611                 } else {
12612                         this.getId = function(){return null;};
12613                 }
12614             this.ef = [];
12615             for(var jj = 0; jj < fl; jj++){
12616                 f = fi[jj];
12617                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12618                 this.ef[jj] = this.getJsonAccessor(map);
12619             }
12620         }
12621
12622         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12623         if(s.totalProperty){
12624             var vt = parseInt(this.getTotal(o), 10);
12625             if(!isNaN(vt)){
12626                 totalRecords = vt;
12627             }
12628         }
12629         if(s.successProperty){
12630             var vs = this.getSuccess(o);
12631             if(vs === false || vs === 'false'){
12632                 success = false;
12633             }
12634         }
12635         var records = [];
12636         for(var i = 0; i < c; i++){
12637                 var n = root[i];
12638             var values = {};
12639             var id = this.getId(n);
12640             for(var j = 0; j < fl; j++){
12641                 f = fi[j];
12642             var v = this.ef[j](n);
12643             if (!f.convert) {
12644                 Roo.log('missing convert for ' + f.name);
12645                 Roo.log(f);
12646                 continue;
12647             }
12648             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12649             }
12650             var record = new Record(values, id);
12651             record.json = n;
12652             records[i] = record;
12653         }
12654         return {
12655             raw : o,
12656             success : success,
12657             records : records,
12658             totalRecords : totalRecords
12659         };
12660     }
12661 });/*
12662  * Based on:
12663  * Ext JS Library 1.1.1
12664  * Copyright(c) 2006-2007, Ext JS, LLC.
12665  *
12666  * Originally Released Under LGPL - original licence link has changed is not relivant.
12667  *
12668  * Fork - LGPL
12669  * <script type="text/javascript">
12670  */
12671
12672 /**
12673  * @class Roo.data.ArrayReader
12674  * @extends Roo.data.DataReader
12675  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12676  * Each element of that Array represents a row of data fields. The
12677  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12678  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12679  * <p>
12680  * Example code:.
12681  * <pre><code>
12682 var RecordDef = Roo.data.Record.create([
12683     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12684     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12685 ]);
12686 var myReader = new Roo.data.ArrayReader({
12687     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12688 }, RecordDef);
12689 </code></pre>
12690  * <p>
12691  * This would consume an Array like this:
12692  * <pre><code>
12693 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12694   </code></pre>
12695  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12696  * @constructor
12697  * Create a new JsonReader
12698  * @param {Object} meta Metadata configuration options.
12699  * @param {Object} recordType Either an Array of field definition objects
12700  * as specified to {@link Roo.data.Record#create},
12701  * or an {@link Roo.data.Record} object
12702  * created using {@link Roo.data.Record#create}.
12703  */
12704 Roo.data.ArrayReader = function(meta, recordType){
12705     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12706 };
12707
12708 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12709     /**
12710      * Create a data block containing Roo.data.Records from an XML document.
12711      * @param {Object} o An Array of row objects which represents the dataset.
12712      * @return {Object} data A data block which is used by an Roo.data.Store object as
12713      * a cache of Roo.data.Records.
12714      */
12715     readRecords : function(o){
12716         var sid = this.meta ? this.meta.id : null;
12717         var recordType = this.recordType, fields = recordType.prototype.fields;
12718         var records = [];
12719         var root = o;
12720             for(var i = 0; i < root.length; i++){
12721                     var n = root[i];
12722                 var values = {};
12723                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12724                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12725                 var f = fields.items[j];
12726                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12727                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12728                 v = f.convert(v);
12729                 values[f.name] = v;
12730             }
12731                 var record = new recordType(values, id);
12732                 record.json = n;
12733                 records[records.length] = record;
12734             }
12735             return {
12736                 records : records,
12737                 totalRecords : records.length
12738             };
12739     }
12740 });/*
12741  * - LGPL
12742  * * 
12743  */
12744
12745 /**
12746  * @class Roo.bootstrap.ComboBox
12747  * @extends Roo.bootstrap.TriggerField
12748  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12749  * @cfg {Boolean} append (true|false) default false
12750  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12751  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12752  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12753  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12754  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12755  * @cfg {Boolean} animate default true
12756  * @cfg {Boolean} emptyResultText only for touch device
12757  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12758  * @cfg {String} emptyTitle default ''
12759  * @constructor
12760  * Create a new ComboBox.
12761  * @param {Object} config Configuration options
12762  */
12763 Roo.bootstrap.ComboBox = function(config){
12764     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12765     this.addEvents({
12766         /**
12767          * @event expand
12768          * Fires when the dropdown list is expanded
12769         * @param {Roo.bootstrap.ComboBox} combo This combo box
12770         */
12771         'expand' : true,
12772         /**
12773          * @event collapse
12774          * Fires when the dropdown list is collapsed
12775         * @param {Roo.bootstrap.ComboBox} combo This combo box
12776         */
12777         'collapse' : true,
12778         /**
12779          * @event beforeselect
12780          * Fires before a list item is selected. Return false to cancel the selection.
12781         * @param {Roo.bootstrap.ComboBox} combo This combo box
12782         * @param {Roo.data.Record} record The data record returned from the underlying store
12783         * @param {Number} index The index of the selected item in the dropdown list
12784         */
12785         'beforeselect' : true,
12786         /**
12787          * @event select
12788          * Fires when a list item is selected
12789         * @param {Roo.bootstrap.ComboBox} combo This combo box
12790         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12791         * @param {Number} index The index of the selected item in the dropdown list
12792         */
12793         'select' : true,
12794         /**
12795          * @event beforequery
12796          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12797          * The event object passed has these properties:
12798         * @param {Roo.bootstrap.ComboBox} combo This combo box
12799         * @param {String} query The query
12800         * @param {Boolean} forceAll true to force "all" query
12801         * @param {Boolean} cancel true to cancel the query
12802         * @param {Object} e The query event object
12803         */
12804         'beforequery': true,
12805          /**
12806          * @event add
12807          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12808         * @param {Roo.bootstrap.ComboBox} combo This combo box
12809         */
12810         'add' : true,
12811         /**
12812          * @event edit
12813          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12814         * @param {Roo.bootstrap.ComboBox} combo This combo box
12815         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12816         */
12817         'edit' : true,
12818         /**
12819          * @event remove
12820          * Fires when the remove value from the combobox array
12821         * @param {Roo.bootstrap.ComboBox} combo This combo box
12822         */
12823         'remove' : true,
12824         /**
12825          * @event afterremove
12826          * Fires when the remove value from the combobox array
12827         * @param {Roo.bootstrap.ComboBox} combo This combo box
12828         */
12829         'afterremove' : true,
12830         /**
12831          * @event specialfilter
12832          * Fires when specialfilter
12833             * @param {Roo.bootstrap.ComboBox} combo This combo box
12834             */
12835         'specialfilter' : true,
12836         /**
12837          * @event tick
12838          * Fires when tick the element
12839             * @param {Roo.bootstrap.ComboBox} combo This combo box
12840             */
12841         'tick' : true,
12842         /**
12843          * @event touchviewdisplay
12844          * Fires when touch view require special display (default is using displayField)
12845             * @param {Roo.bootstrap.ComboBox} combo This combo box
12846             * @param {Object} cfg set html .
12847             */
12848         'touchviewdisplay' : true
12849         
12850     });
12851     
12852     this.item = [];
12853     this.tickItems = [];
12854     
12855     this.selectedIndex = -1;
12856     if(this.mode == 'local'){
12857         if(config.queryDelay === undefined){
12858             this.queryDelay = 10;
12859         }
12860         if(config.minChars === undefined){
12861             this.minChars = 0;
12862         }
12863     }
12864 };
12865
12866 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12867      
12868     /**
12869      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12870      * rendering into an Roo.Editor, defaults to false)
12871      */
12872     /**
12873      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12874      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12875      */
12876     /**
12877      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12878      */
12879     /**
12880      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12881      * the dropdown list (defaults to undefined, with no header element)
12882      */
12883
12884      /**
12885      * @cfg {String/Roo.Template} tpl The template to use to render the output
12886      */
12887      
12888      /**
12889      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12890      */
12891     listWidth: undefined,
12892     /**
12893      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12894      * mode = 'remote' or 'text' if mode = 'local')
12895      */
12896     displayField: undefined,
12897     
12898     /**
12899      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12900      * mode = 'remote' or 'value' if mode = 'local'). 
12901      * Note: use of a valueField requires the user make a selection
12902      * in order for a value to be mapped.
12903      */
12904     valueField: undefined,
12905     /**
12906      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12907      */
12908     modalTitle : '',
12909     
12910     /**
12911      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12912      * field's data value (defaults to the underlying DOM element's name)
12913      */
12914     hiddenName: undefined,
12915     /**
12916      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12917      */
12918     listClass: '',
12919     /**
12920      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12921      */
12922     selectedClass: 'active',
12923     
12924     /**
12925      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12926      */
12927     shadow:'sides',
12928     /**
12929      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12930      * anchor positions (defaults to 'tl-bl')
12931      */
12932     listAlign: 'tl-bl?',
12933     /**
12934      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12935      */
12936     maxHeight: 300,
12937     /**
12938      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12939      * query specified by the allQuery config option (defaults to 'query')
12940      */
12941     triggerAction: 'query',
12942     /**
12943      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12944      * (defaults to 4, does not apply if editable = false)
12945      */
12946     minChars : 4,
12947     /**
12948      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12949      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12950      */
12951     typeAhead: false,
12952     /**
12953      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12954      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12955      */
12956     queryDelay: 500,
12957     /**
12958      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12959      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12960      */
12961     pageSize: 0,
12962     /**
12963      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12964      * when editable = true (defaults to false)
12965      */
12966     selectOnFocus:false,
12967     /**
12968      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12969      */
12970     queryParam: 'query',
12971     /**
12972      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12973      * when mode = 'remote' (defaults to 'Loading...')
12974      */
12975     loadingText: 'Loading...',
12976     /**
12977      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12978      */
12979     resizable: false,
12980     /**
12981      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12982      */
12983     handleHeight : 8,
12984     /**
12985      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12986      * traditional select (defaults to true)
12987      */
12988     editable: true,
12989     /**
12990      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12991      */
12992     allQuery: '',
12993     /**
12994      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12995      */
12996     mode: 'remote',
12997     /**
12998      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12999      * listWidth has a higher value)
13000      */
13001     minListWidth : 70,
13002     /**
13003      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13004      * allow the user to set arbitrary text into the field (defaults to false)
13005      */
13006     forceSelection:false,
13007     /**
13008      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13009      * if typeAhead = true (defaults to 250)
13010      */
13011     typeAheadDelay : 250,
13012     /**
13013      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13014      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13015      */
13016     valueNotFoundText : undefined,
13017     /**
13018      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13019      */
13020     blockFocus : false,
13021     
13022     /**
13023      * @cfg {Boolean} disableClear Disable showing of clear button.
13024      */
13025     disableClear : false,
13026     /**
13027      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13028      */
13029     alwaysQuery : false,
13030     
13031     /**
13032      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13033      */
13034     multiple : false,
13035     
13036     /**
13037      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13038      */
13039     invalidClass : "has-warning",
13040     
13041     /**
13042      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13043      */
13044     validClass : "has-success",
13045     
13046     /**
13047      * @cfg {Boolean} specialFilter (true|false) special filter default false
13048      */
13049     specialFilter : false,
13050     
13051     /**
13052      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13053      */
13054     mobileTouchView : true,
13055     
13056     /**
13057      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13058      */
13059     useNativeIOS : false,
13060     
13061     /**
13062      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13063      */
13064     mobile_restrict_height : false,
13065     
13066     ios_options : false,
13067     
13068     //private
13069     addicon : false,
13070     editicon: false,
13071     
13072     page: 0,
13073     hasQuery: false,
13074     append: false,
13075     loadNext: false,
13076     autoFocus : true,
13077     tickable : false,
13078     btnPosition : 'right',
13079     triggerList : true,
13080     showToggleBtn : true,
13081     animate : true,
13082     emptyResultText: 'Empty',
13083     triggerText : 'Select',
13084     emptyTitle : '',
13085     
13086     // element that contains real text value.. (when hidden is used..)
13087     
13088     getAutoCreate : function()
13089     {   
13090         var cfg = false;
13091         //render
13092         /*
13093          * Render classic select for iso
13094          */
13095         
13096         if(Roo.isIOS && this.useNativeIOS){
13097             cfg = this.getAutoCreateNativeIOS();
13098             return cfg;
13099         }
13100         
13101         /*
13102          * Touch Devices
13103          */
13104         
13105         if(Roo.isTouch && this.mobileTouchView){
13106             cfg = this.getAutoCreateTouchView();
13107             return cfg;;
13108         }
13109         
13110         /*
13111          *  Normal ComboBox
13112          */
13113         if(!this.tickable){
13114             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13115             return cfg;
13116         }
13117         
13118         /*
13119          *  ComboBox with tickable selections
13120          */
13121              
13122         var align = this.labelAlign || this.parentLabelAlign();
13123         
13124         cfg = {
13125             cls : 'form-group roo-combobox-tickable' //input-group
13126         };
13127         
13128         var btn_text_select = '';
13129         var btn_text_done = '';
13130         var btn_text_cancel = '';
13131         
13132         if (this.btn_text_show) {
13133             btn_text_select = 'Select';
13134             btn_text_done = 'Done';
13135             btn_text_cancel = 'Cancel'; 
13136         }
13137         
13138         var buttons = {
13139             tag : 'div',
13140             cls : 'tickable-buttons',
13141             cn : [
13142                 {
13143                     tag : 'button',
13144                     type : 'button',
13145                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13146                     //html : this.triggerText
13147                     html: btn_text_select
13148                 },
13149                 {
13150                     tag : 'button',
13151                     type : 'button',
13152                     name : 'ok',
13153                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13154                     //html : 'Done'
13155                     html: btn_text_done
13156                 },
13157                 {
13158                     tag : 'button',
13159                     type : 'button',
13160                     name : 'cancel',
13161                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13162                     //html : 'Cancel'
13163                     html: btn_text_cancel
13164                 }
13165             ]
13166         };
13167         
13168         if(this.editable){
13169             buttons.cn.unshift({
13170                 tag: 'input',
13171                 cls: 'roo-select2-search-field-input'
13172             });
13173         }
13174         
13175         var _this = this;
13176         
13177         Roo.each(buttons.cn, function(c){
13178             if (_this.size) {
13179                 c.cls += ' btn-' + _this.size;
13180             }
13181
13182             if (_this.disabled) {
13183                 c.disabled = true;
13184             }
13185         });
13186         
13187         var box = {
13188             tag: 'div',
13189             cn: [
13190                 {
13191                     tag: 'input',
13192                     type : 'hidden',
13193                     cls: 'form-hidden-field'
13194                 },
13195                 {
13196                     tag: 'ul',
13197                     cls: 'roo-select2-choices',
13198                     cn:[
13199                         {
13200                             tag: 'li',
13201                             cls: 'roo-select2-search-field',
13202                             cn: [
13203                                 buttons
13204                             ]
13205                         }
13206                     ]
13207                 }
13208             ]
13209         };
13210         
13211         var combobox = {
13212             cls: 'roo-select2-container input-group roo-select2-container-multi',
13213             cn: [
13214                 box
13215 //                {
13216 //                    tag: 'ul',
13217 //                    cls: 'typeahead typeahead-long dropdown-menu',
13218 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13219 //                }
13220             ]
13221         };
13222         
13223         if(this.hasFeedback && !this.allowBlank){
13224             
13225             var feedback = {
13226                 tag: 'span',
13227                 cls: 'glyphicon form-control-feedback'
13228             };
13229
13230             combobox.cn.push(feedback);
13231         }
13232         
13233         
13234         if (align ==='left' && this.fieldLabel.length) {
13235             
13236             cfg.cls += ' roo-form-group-label-left';
13237             
13238             cfg.cn = [
13239                 {
13240                     tag : 'i',
13241                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13242                     tooltip : 'This field is required'
13243                 },
13244                 {
13245                     tag: 'label',
13246                     'for' :  id,
13247                     cls : 'control-label',
13248                     html : this.fieldLabel
13249
13250                 },
13251                 {
13252                     cls : "", 
13253                     cn: [
13254                         combobox
13255                     ]
13256                 }
13257
13258             ];
13259             
13260             var labelCfg = cfg.cn[1];
13261             var contentCfg = cfg.cn[2];
13262             
13263
13264             if(this.indicatorpos == 'right'){
13265                 
13266                 cfg.cn = [
13267                     {
13268                         tag: 'label',
13269                         'for' :  id,
13270                         cls : 'control-label',
13271                         cn : [
13272                             {
13273                                 tag : 'span',
13274                                 html : this.fieldLabel
13275                             },
13276                             {
13277                                 tag : 'i',
13278                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13279                                 tooltip : 'This field is required'
13280                             }
13281                         ]
13282                     },
13283                     {
13284                         cls : "",
13285                         cn: [
13286                             combobox
13287                         ]
13288                     }
13289
13290                 ];
13291                 
13292                 
13293                 
13294                 labelCfg = cfg.cn[0];
13295                 contentCfg = cfg.cn[1];
13296             
13297             }
13298             
13299             if(this.labelWidth > 12){
13300                 labelCfg.style = "width: " + this.labelWidth + 'px';
13301             }
13302             
13303             if(this.labelWidth < 13 && this.labelmd == 0){
13304                 this.labelmd = this.labelWidth;
13305             }
13306             
13307             if(this.labellg > 0){
13308                 labelCfg.cls += ' col-lg-' + this.labellg;
13309                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13310             }
13311             
13312             if(this.labelmd > 0){
13313                 labelCfg.cls += ' col-md-' + this.labelmd;
13314                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13315             }
13316             
13317             if(this.labelsm > 0){
13318                 labelCfg.cls += ' col-sm-' + this.labelsm;
13319                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13320             }
13321             
13322             if(this.labelxs > 0){
13323                 labelCfg.cls += ' col-xs-' + this.labelxs;
13324                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13325             }
13326                 
13327                 
13328         } else if ( this.fieldLabel.length) {
13329 //                Roo.log(" label");
13330                  cfg.cn = [
13331                     {
13332                         tag : 'i',
13333                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13334                         tooltip : 'This field is required'
13335                     },
13336                     {
13337                         tag: 'label',
13338                         //cls : 'input-group-addon',
13339                         html : this.fieldLabel
13340                     },
13341                     combobox
13342                 ];
13343                 
13344                 if(this.indicatorpos == 'right'){
13345                     cfg.cn = [
13346                         {
13347                             tag: 'label',
13348                             //cls : 'input-group-addon',
13349                             html : this.fieldLabel
13350                         },
13351                         {
13352                             tag : 'i',
13353                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13354                             tooltip : 'This field is required'
13355                         },
13356                         combobox
13357                     ];
13358                     
13359                 }
13360
13361         } else {
13362             
13363 //                Roo.log(" no label && no align");
13364                 cfg = combobox
13365                      
13366                 
13367         }
13368          
13369         var settings=this;
13370         ['xs','sm','md','lg'].map(function(size){
13371             if (settings[size]) {
13372                 cfg.cls += ' col-' + size + '-' + settings[size];
13373             }
13374         });
13375         
13376         return cfg;
13377         
13378     },
13379     
13380     _initEventsCalled : false,
13381     
13382     // private
13383     initEvents: function()
13384     {   
13385         if (this._initEventsCalled) { // as we call render... prevent looping...
13386             return;
13387         }
13388         this._initEventsCalled = true;
13389         
13390         if (!this.store) {
13391             throw "can not find store for combo";
13392         }
13393         
13394         this.indicator = this.indicatorEl();
13395         
13396         this.store = Roo.factory(this.store, Roo.data);
13397         this.store.parent = this;
13398         
13399         // if we are building from html. then this element is so complex, that we can not really
13400         // use the rendered HTML.
13401         // so we have to trash and replace the previous code.
13402         if (Roo.XComponent.build_from_html) {
13403             // remove this element....
13404             var e = this.el.dom, k=0;
13405             while (e ) { e = e.previousSibling;  ++k;}
13406
13407             this.el.remove();
13408             
13409             this.el=false;
13410             this.rendered = false;
13411             
13412             this.render(this.parent().getChildContainer(true), k);
13413         }
13414         
13415         if(Roo.isIOS && this.useNativeIOS){
13416             this.initIOSView();
13417             return;
13418         }
13419         
13420         /*
13421          * Touch Devices
13422          */
13423         
13424         if(Roo.isTouch && this.mobileTouchView){
13425             this.initTouchView();
13426             return;
13427         }
13428         
13429         if(this.tickable){
13430             this.initTickableEvents();
13431             return;
13432         }
13433         
13434         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13435         
13436         if(this.hiddenName){
13437             
13438             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13439             
13440             this.hiddenField.dom.value =
13441                 this.hiddenValue !== undefined ? this.hiddenValue :
13442                 this.value !== undefined ? this.value : '';
13443
13444             // prevent input submission
13445             this.el.dom.removeAttribute('name');
13446             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13447              
13448              
13449         }
13450         //if(Roo.isGecko){
13451         //    this.el.dom.setAttribute('autocomplete', 'off');
13452         //}
13453         
13454         var cls = 'x-combo-list';
13455         
13456         //this.list = new Roo.Layer({
13457         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13458         //});
13459         
13460         var _this = this;
13461         
13462         (function(){
13463             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13464             _this.list.setWidth(lw);
13465         }).defer(100);
13466         
13467         this.list.on('mouseover', this.onViewOver, this);
13468         this.list.on('mousemove', this.onViewMove, this);
13469         this.list.on('scroll', this.onViewScroll, this);
13470         
13471         /*
13472         this.list.swallowEvent('mousewheel');
13473         this.assetHeight = 0;
13474
13475         if(this.title){
13476             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13477             this.assetHeight += this.header.getHeight();
13478         }
13479
13480         this.innerList = this.list.createChild({cls:cls+'-inner'});
13481         this.innerList.on('mouseover', this.onViewOver, this);
13482         this.innerList.on('mousemove', this.onViewMove, this);
13483         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13484         
13485         if(this.allowBlank && !this.pageSize && !this.disableClear){
13486             this.footer = this.list.createChild({cls:cls+'-ft'});
13487             this.pageTb = new Roo.Toolbar(this.footer);
13488            
13489         }
13490         if(this.pageSize){
13491             this.footer = this.list.createChild({cls:cls+'-ft'});
13492             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13493                     {pageSize: this.pageSize});
13494             
13495         }
13496         
13497         if (this.pageTb && this.allowBlank && !this.disableClear) {
13498             var _this = this;
13499             this.pageTb.add(new Roo.Toolbar.Fill(), {
13500                 cls: 'x-btn-icon x-btn-clear',
13501                 text: '&#160;',
13502                 handler: function()
13503                 {
13504                     _this.collapse();
13505                     _this.clearValue();
13506                     _this.onSelect(false, -1);
13507                 }
13508             });
13509         }
13510         if (this.footer) {
13511             this.assetHeight += this.footer.getHeight();
13512         }
13513         */
13514             
13515         if(!this.tpl){
13516             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13517         }
13518
13519         this.view = new Roo.View(this.list, this.tpl, {
13520             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13521         });
13522         //this.view.wrapEl.setDisplayed(false);
13523         this.view.on('click', this.onViewClick, this);
13524         
13525         
13526         this.store.on('beforeload', this.onBeforeLoad, this);
13527         this.store.on('load', this.onLoad, this);
13528         this.store.on('loadexception', this.onLoadException, this);
13529         /*
13530         if(this.resizable){
13531             this.resizer = new Roo.Resizable(this.list,  {
13532                pinned:true, handles:'se'
13533             });
13534             this.resizer.on('resize', function(r, w, h){
13535                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13536                 this.listWidth = w;
13537                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13538                 this.restrictHeight();
13539             }, this);
13540             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13541         }
13542         */
13543         if(!this.editable){
13544             this.editable = true;
13545             this.setEditable(false);
13546         }
13547         
13548         /*
13549         
13550         if (typeof(this.events.add.listeners) != 'undefined') {
13551             
13552             this.addicon = this.wrap.createChild(
13553                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13554        
13555             this.addicon.on('click', function(e) {
13556                 this.fireEvent('add', this);
13557             }, this);
13558         }
13559         if (typeof(this.events.edit.listeners) != 'undefined') {
13560             
13561             this.editicon = this.wrap.createChild(
13562                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13563             if (this.addicon) {
13564                 this.editicon.setStyle('margin-left', '40px');
13565             }
13566             this.editicon.on('click', function(e) {
13567                 
13568                 // we fire even  if inothing is selected..
13569                 this.fireEvent('edit', this, this.lastData );
13570                 
13571             }, this);
13572         }
13573         */
13574         
13575         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13576             "up" : function(e){
13577                 this.inKeyMode = true;
13578                 this.selectPrev();
13579             },
13580
13581             "down" : function(e){
13582                 if(!this.isExpanded()){
13583                     this.onTriggerClick();
13584                 }else{
13585                     this.inKeyMode = true;
13586                     this.selectNext();
13587                 }
13588             },
13589
13590             "enter" : function(e){
13591 //                this.onViewClick();
13592                 //return true;
13593                 this.collapse();
13594                 
13595                 if(this.fireEvent("specialkey", this, e)){
13596                     this.onViewClick(false);
13597                 }
13598                 
13599                 return true;
13600             },
13601
13602             "esc" : function(e){
13603                 this.collapse();
13604             },
13605
13606             "tab" : function(e){
13607                 this.collapse();
13608                 
13609                 if(this.fireEvent("specialkey", this, e)){
13610                     this.onViewClick(false);
13611                 }
13612                 
13613                 return true;
13614             },
13615
13616             scope : this,
13617
13618             doRelay : function(foo, bar, hname){
13619                 if(hname == 'down' || this.scope.isExpanded()){
13620                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13621                 }
13622                 return true;
13623             },
13624
13625             forceKeyDown: true
13626         });
13627         
13628         
13629         this.queryDelay = Math.max(this.queryDelay || 10,
13630                 this.mode == 'local' ? 10 : 250);
13631         
13632         
13633         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13634         
13635         if(this.typeAhead){
13636             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13637         }
13638         if(this.editable !== false){
13639             this.inputEl().on("keyup", this.onKeyUp, this);
13640         }
13641         if(this.forceSelection){
13642             this.inputEl().on('blur', this.doForce, this);
13643         }
13644         
13645         if(this.multiple){
13646             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13647             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13648         }
13649     },
13650     
13651     initTickableEvents: function()
13652     {   
13653         this.createList();
13654         
13655         if(this.hiddenName){
13656             
13657             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13658             
13659             this.hiddenField.dom.value =
13660                 this.hiddenValue !== undefined ? this.hiddenValue :
13661                 this.value !== undefined ? this.value : '';
13662
13663             // prevent input submission
13664             this.el.dom.removeAttribute('name');
13665             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13666              
13667              
13668         }
13669         
13670 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13671         
13672         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13673         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13674         if(this.triggerList){
13675             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13676         }
13677          
13678         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13679         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13680         
13681         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13682         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13683         
13684         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13685         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13686         
13687         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13688         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13689         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13690         
13691         this.okBtn.hide();
13692         this.cancelBtn.hide();
13693         
13694         var _this = this;
13695         
13696         (function(){
13697             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13698             _this.list.setWidth(lw);
13699         }).defer(100);
13700         
13701         this.list.on('mouseover', this.onViewOver, this);
13702         this.list.on('mousemove', this.onViewMove, this);
13703         
13704         this.list.on('scroll', this.onViewScroll, this);
13705         
13706         if(!this.tpl){
13707             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13708                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13709         }
13710
13711         this.view = new Roo.View(this.list, this.tpl, {
13712             singleSelect:true,
13713             tickable:true,
13714             parent:this,
13715             store: this.store,
13716             selectedClass: this.selectedClass
13717         });
13718         
13719         //this.view.wrapEl.setDisplayed(false);
13720         this.view.on('click', this.onViewClick, this);
13721         
13722         
13723         
13724         this.store.on('beforeload', this.onBeforeLoad, this);
13725         this.store.on('load', this.onLoad, this);
13726         this.store.on('loadexception', this.onLoadException, this);
13727         
13728         if(this.editable){
13729             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13730                 "up" : function(e){
13731                     this.inKeyMode = true;
13732                     this.selectPrev();
13733                 },
13734
13735                 "down" : function(e){
13736                     this.inKeyMode = true;
13737                     this.selectNext();
13738                 },
13739
13740                 "enter" : function(e){
13741                     if(this.fireEvent("specialkey", this, e)){
13742                         this.onViewClick(false);
13743                     }
13744                     
13745                     return true;
13746                 },
13747
13748                 "esc" : function(e){
13749                     this.onTickableFooterButtonClick(e, false, false);
13750                 },
13751
13752                 "tab" : function(e){
13753                     this.fireEvent("specialkey", this, e);
13754                     
13755                     this.onTickableFooterButtonClick(e, false, false);
13756                     
13757                     return true;
13758                 },
13759
13760                 scope : this,
13761
13762                 doRelay : function(e, fn, key){
13763                     if(this.scope.isExpanded()){
13764                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13765                     }
13766                     return true;
13767                 },
13768
13769                 forceKeyDown: true
13770             });
13771         }
13772         
13773         this.queryDelay = Math.max(this.queryDelay || 10,
13774                 this.mode == 'local' ? 10 : 250);
13775         
13776         
13777         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13778         
13779         if(this.typeAhead){
13780             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13781         }
13782         
13783         if(this.editable !== false){
13784             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13785         }
13786         
13787         this.indicator = this.indicatorEl();
13788         
13789         if(this.indicator){
13790             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13791             this.indicator.hide();
13792         }
13793         
13794     },
13795
13796     onDestroy : function(){
13797         if(this.view){
13798             this.view.setStore(null);
13799             this.view.el.removeAllListeners();
13800             this.view.el.remove();
13801             this.view.purgeListeners();
13802         }
13803         if(this.list){
13804             this.list.dom.innerHTML  = '';
13805         }
13806         
13807         if(this.store){
13808             this.store.un('beforeload', this.onBeforeLoad, this);
13809             this.store.un('load', this.onLoad, this);
13810             this.store.un('loadexception', this.onLoadException, this);
13811         }
13812         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13813     },
13814
13815     // private
13816     fireKey : function(e){
13817         if(e.isNavKeyPress() && !this.list.isVisible()){
13818             this.fireEvent("specialkey", this, e);
13819         }
13820     },
13821
13822     // private
13823     onResize: function(w, h){
13824 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13825 //        
13826 //        if(typeof w != 'number'){
13827 //            // we do not handle it!?!?
13828 //            return;
13829 //        }
13830 //        var tw = this.trigger.getWidth();
13831 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13832 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13833 //        var x = w - tw;
13834 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13835 //            
13836 //        //this.trigger.setStyle('left', x+'px');
13837 //        
13838 //        if(this.list && this.listWidth === undefined){
13839 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13840 //            this.list.setWidth(lw);
13841 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13842 //        }
13843         
13844     
13845         
13846     },
13847
13848     /**
13849      * Allow or prevent the user from directly editing the field text.  If false is passed,
13850      * the user will only be able to select from the items defined in the dropdown list.  This method
13851      * is the runtime equivalent of setting the 'editable' config option at config time.
13852      * @param {Boolean} value True to allow the user to directly edit the field text
13853      */
13854     setEditable : function(value){
13855         if(value == this.editable){
13856             return;
13857         }
13858         this.editable = value;
13859         if(!value){
13860             this.inputEl().dom.setAttribute('readOnly', true);
13861             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13862             this.inputEl().addClass('x-combo-noedit');
13863         }else{
13864             this.inputEl().dom.setAttribute('readOnly', false);
13865             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13866             this.inputEl().removeClass('x-combo-noedit');
13867         }
13868     },
13869
13870     // private
13871     
13872     onBeforeLoad : function(combo,opts){
13873         if(!this.hasFocus){
13874             return;
13875         }
13876          if (!opts.add) {
13877             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13878          }
13879         this.restrictHeight();
13880         this.selectedIndex = -1;
13881     },
13882
13883     // private
13884     onLoad : function(){
13885         
13886         this.hasQuery = false;
13887         
13888         if(!this.hasFocus){
13889             return;
13890         }
13891         
13892         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13893             this.loading.hide();
13894         }
13895         
13896         if(this.store.getCount() > 0){
13897             
13898             this.expand();
13899             this.restrictHeight();
13900             if(this.lastQuery == this.allQuery){
13901                 if(this.editable && !this.tickable){
13902                     this.inputEl().dom.select();
13903                 }
13904                 
13905                 if(
13906                     !this.selectByValue(this.value, true) &&
13907                     this.autoFocus && 
13908                     (
13909                         !this.store.lastOptions ||
13910                         typeof(this.store.lastOptions.add) == 'undefined' || 
13911                         this.store.lastOptions.add != true
13912                     )
13913                 ){
13914                     this.select(0, true);
13915                 }
13916             }else{
13917                 if(this.autoFocus){
13918                     this.selectNext();
13919                 }
13920                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13921                     this.taTask.delay(this.typeAheadDelay);
13922                 }
13923             }
13924         }else{
13925             this.onEmptyResults();
13926         }
13927         
13928         //this.el.focus();
13929     },
13930     // private
13931     onLoadException : function()
13932     {
13933         this.hasQuery = false;
13934         
13935         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13936             this.loading.hide();
13937         }
13938         
13939         if(this.tickable && this.editable){
13940             return;
13941         }
13942         
13943         this.collapse();
13944         // only causes errors at present
13945         //Roo.log(this.store.reader.jsonData);
13946         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13947             // fixme
13948             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13949         //}
13950         
13951         
13952     },
13953     // private
13954     onTypeAhead : function(){
13955         if(this.store.getCount() > 0){
13956             var r = this.store.getAt(0);
13957             var newValue = r.data[this.displayField];
13958             var len = newValue.length;
13959             var selStart = this.getRawValue().length;
13960             
13961             if(selStart != len){
13962                 this.setRawValue(newValue);
13963                 this.selectText(selStart, newValue.length);
13964             }
13965         }
13966     },
13967
13968     // private
13969     onSelect : function(record, index){
13970         
13971         if(this.fireEvent('beforeselect', this, record, index) !== false){
13972         
13973             this.setFromData(index > -1 ? record.data : false);
13974             
13975             this.collapse();
13976             this.fireEvent('select', this, record, index);
13977         }
13978     },
13979
13980     /**
13981      * Returns the currently selected field value or empty string if no value is set.
13982      * @return {String} value The selected value
13983      */
13984     getValue : function()
13985     {
13986         if(Roo.isIOS && this.useNativeIOS){
13987             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13988         }
13989         
13990         if(this.multiple){
13991             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13992         }
13993         
13994         if(this.valueField){
13995             return typeof this.value != 'undefined' ? this.value : '';
13996         }else{
13997             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13998         }
13999     },
14000     
14001     getRawValue : function()
14002     {
14003         if(Roo.isIOS && this.useNativeIOS){
14004             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14005         }
14006         
14007         var v = this.inputEl().getValue();
14008         
14009         return v;
14010     },
14011
14012     /**
14013      * Clears any text/value currently set in the field
14014      */
14015     clearValue : function(){
14016         
14017         if(this.hiddenField){
14018             this.hiddenField.dom.value = '';
14019         }
14020         this.value = '';
14021         this.setRawValue('');
14022         this.lastSelectionText = '';
14023         this.lastData = false;
14024         
14025         var close = this.closeTriggerEl();
14026         
14027         if(close){
14028             close.hide();
14029         }
14030         
14031         this.validate();
14032         
14033     },
14034
14035     /**
14036      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14037      * will be displayed in the field.  If the value does not match the data value of an existing item,
14038      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14039      * Otherwise the field will be blank (although the value will still be set).
14040      * @param {String} value The value to match
14041      */
14042     setValue : function(v)
14043     {
14044         if(Roo.isIOS && this.useNativeIOS){
14045             this.setIOSValue(v);
14046             return;
14047         }
14048         
14049         if(this.multiple){
14050             this.syncValue();
14051             return;
14052         }
14053         
14054         var text = v;
14055         if(this.valueField){
14056             var r = this.findRecord(this.valueField, v);
14057             if(r){
14058                 text = r.data[this.displayField];
14059             }else if(this.valueNotFoundText !== undefined){
14060                 text = this.valueNotFoundText;
14061             }
14062         }
14063         this.lastSelectionText = text;
14064         if(this.hiddenField){
14065             this.hiddenField.dom.value = v;
14066         }
14067         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14068         this.value = v;
14069         
14070         var close = this.closeTriggerEl();
14071         
14072         if(close){
14073             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14074         }
14075         
14076         this.validate();
14077     },
14078     /**
14079      * @property {Object} the last set data for the element
14080      */
14081     
14082     lastData : false,
14083     /**
14084      * Sets the value of the field based on a object which is related to the record format for the store.
14085      * @param {Object} value the value to set as. or false on reset?
14086      */
14087     setFromData : function(o){
14088         
14089         if(this.multiple){
14090             this.addItem(o);
14091             return;
14092         }
14093             
14094         var dv = ''; // display value
14095         var vv = ''; // value value..
14096         this.lastData = o;
14097         if (this.displayField) {
14098             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14099         } else {
14100             // this is an error condition!!!
14101             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14102         }
14103         
14104         if(this.valueField){
14105             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14106         }
14107         
14108         var close = this.closeTriggerEl();
14109         
14110         if(close){
14111             if(dv.length || vv * 1 > 0){
14112                 close.show() ;
14113                 this.blockFocus=true;
14114             } else {
14115                 close.hide();
14116             }             
14117         }
14118         
14119         if(this.hiddenField){
14120             this.hiddenField.dom.value = vv;
14121             
14122             this.lastSelectionText = dv;
14123             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14124             this.value = vv;
14125             return;
14126         }
14127         // no hidden field.. - we store the value in 'value', but still display
14128         // display field!!!!
14129         this.lastSelectionText = dv;
14130         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14131         this.value = vv;
14132         
14133         
14134         
14135     },
14136     // private
14137     reset : function(){
14138         // overridden so that last data is reset..
14139         
14140         if(this.multiple){
14141             this.clearItem();
14142             return;
14143         }
14144         
14145         this.setValue(this.originalValue);
14146         //this.clearInvalid();
14147         this.lastData = false;
14148         if (this.view) {
14149             this.view.clearSelections();
14150         }
14151         
14152         this.validate();
14153     },
14154     // private
14155     findRecord : function(prop, value){
14156         var record;
14157         if(this.store.getCount() > 0){
14158             this.store.each(function(r){
14159                 if(r.data[prop] == value){
14160                     record = r;
14161                     return false;
14162                 }
14163                 return true;
14164             });
14165         }
14166         return record;
14167     },
14168     
14169     getName: function()
14170     {
14171         // returns hidden if it's set..
14172         if (!this.rendered) {return ''};
14173         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14174         
14175     },
14176     // private
14177     onViewMove : function(e, t){
14178         this.inKeyMode = false;
14179     },
14180
14181     // private
14182     onViewOver : function(e, t){
14183         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14184             return;
14185         }
14186         var item = this.view.findItemFromChild(t);
14187         
14188         if(item){
14189             var index = this.view.indexOf(item);
14190             this.select(index, false);
14191         }
14192     },
14193
14194     // private
14195     onViewClick : function(view, doFocus, el, e)
14196     {
14197         var index = this.view.getSelectedIndexes()[0];
14198         
14199         var r = this.store.getAt(index);
14200         
14201         if(this.tickable){
14202             
14203             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14204                 return;
14205             }
14206             
14207             var rm = false;
14208             var _this = this;
14209             
14210             Roo.each(this.tickItems, function(v,k){
14211                 
14212                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14213                     Roo.log(v);
14214                     _this.tickItems.splice(k, 1);
14215                     
14216                     if(typeof(e) == 'undefined' && view == false){
14217                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14218                     }
14219                     
14220                     rm = true;
14221                     return;
14222                 }
14223             });
14224             
14225             if(rm){
14226                 return;
14227             }
14228             
14229             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14230                 this.tickItems.push(r.data);
14231             }
14232             
14233             if(typeof(e) == 'undefined' && view == false){
14234                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14235             }
14236                     
14237             return;
14238         }
14239         
14240         if(r){
14241             this.onSelect(r, index);
14242         }
14243         if(doFocus !== false && !this.blockFocus){
14244             this.inputEl().focus();
14245         }
14246     },
14247
14248     // private
14249     restrictHeight : function(){
14250         //this.innerList.dom.style.height = '';
14251         //var inner = this.innerList.dom;
14252         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14253         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14254         //this.list.beginUpdate();
14255         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14256         this.list.alignTo(this.inputEl(), this.listAlign);
14257         this.list.alignTo(this.inputEl(), this.listAlign);
14258         //this.list.endUpdate();
14259     },
14260
14261     // private
14262     onEmptyResults : function(){
14263         
14264         if(this.tickable && this.editable){
14265             this.hasFocus = false;
14266             this.restrictHeight();
14267             return;
14268         }
14269         
14270         this.collapse();
14271     },
14272
14273     /**
14274      * Returns true if the dropdown list is expanded, else false.
14275      */
14276     isExpanded : function(){
14277         return this.list.isVisible();
14278     },
14279
14280     /**
14281      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14282      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14283      * @param {String} value The data value of the item to select
14284      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14285      * selected item if it is not currently in view (defaults to true)
14286      * @return {Boolean} True if the value matched an item in the list, else false
14287      */
14288     selectByValue : function(v, scrollIntoView){
14289         if(v !== undefined && v !== null){
14290             var r = this.findRecord(this.valueField || this.displayField, v);
14291             if(r){
14292                 this.select(this.store.indexOf(r), scrollIntoView);
14293                 return true;
14294             }
14295         }
14296         return false;
14297     },
14298
14299     /**
14300      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14301      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14302      * @param {Number} index The zero-based index of the list item to select
14303      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14304      * selected item if it is not currently in view (defaults to true)
14305      */
14306     select : function(index, scrollIntoView){
14307         this.selectedIndex = index;
14308         this.view.select(index);
14309         if(scrollIntoView !== false){
14310             var el = this.view.getNode(index);
14311             /*
14312              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14313              */
14314             if(el){
14315                 this.list.scrollChildIntoView(el, false);
14316             }
14317         }
14318     },
14319
14320     // private
14321     selectNext : function(){
14322         var ct = this.store.getCount();
14323         if(ct > 0){
14324             if(this.selectedIndex == -1){
14325                 this.select(0);
14326             }else if(this.selectedIndex < ct-1){
14327                 this.select(this.selectedIndex+1);
14328             }
14329         }
14330     },
14331
14332     // private
14333     selectPrev : function(){
14334         var ct = this.store.getCount();
14335         if(ct > 0){
14336             if(this.selectedIndex == -1){
14337                 this.select(0);
14338             }else if(this.selectedIndex != 0){
14339                 this.select(this.selectedIndex-1);
14340             }
14341         }
14342     },
14343
14344     // private
14345     onKeyUp : function(e){
14346         if(this.editable !== false && !e.isSpecialKey()){
14347             this.lastKey = e.getKey();
14348             this.dqTask.delay(this.queryDelay);
14349         }
14350     },
14351
14352     // private
14353     validateBlur : function(){
14354         return !this.list || !this.list.isVisible();   
14355     },
14356
14357     // private
14358     initQuery : function(){
14359         
14360         var v = this.getRawValue();
14361         
14362         if(this.tickable && this.editable){
14363             v = this.tickableInputEl().getValue();
14364         }
14365         
14366         this.doQuery(v);
14367     },
14368
14369     // private
14370     doForce : function(){
14371         if(this.inputEl().dom.value.length > 0){
14372             this.inputEl().dom.value =
14373                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14374              
14375         }
14376     },
14377
14378     /**
14379      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14380      * query allowing the query action to be canceled if needed.
14381      * @param {String} query The SQL query to execute
14382      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14383      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14384      * saved in the current store (defaults to false)
14385      */
14386     doQuery : function(q, forceAll){
14387         
14388         if(q === undefined || q === null){
14389             q = '';
14390         }
14391         var qe = {
14392             query: q,
14393             forceAll: forceAll,
14394             combo: this,
14395             cancel:false
14396         };
14397         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14398             return false;
14399         }
14400         q = qe.query;
14401         
14402         forceAll = qe.forceAll;
14403         if(forceAll === true || (q.length >= this.minChars)){
14404             
14405             this.hasQuery = true;
14406             
14407             if(this.lastQuery != q || this.alwaysQuery){
14408                 this.lastQuery = q;
14409                 if(this.mode == 'local'){
14410                     this.selectedIndex = -1;
14411                     if(forceAll){
14412                         this.store.clearFilter();
14413                     }else{
14414                         
14415                         if(this.specialFilter){
14416                             this.fireEvent('specialfilter', this);
14417                             this.onLoad();
14418                             return;
14419                         }
14420                         
14421                         this.store.filter(this.displayField, q);
14422                     }
14423                     
14424                     this.store.fireEvent("datachanged", this.store);
14425                     
14426                     this.onLoad();
14427                     
14428                     
14429                 }else{
14430                     
14431                     this.store.baseParams[this.queryParam] = q;
14432                     
14433                     var options = {params : this.getParams(q)};
14434                     
14435                     if(this.loadNext){
14436                         options.add = true;
14437                         options.params.start = this.page * this.pageSize;
14438                     }
14439                     
14440                     this.store.load(options);
14441                     
14442                     /*
14443                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14444                      *  we should expand the list on onLoad
14445                      *  so command out it
14446                      */
14447 //                    this.expand();
14448                 }
14449             }else{
14450                 this.selectedIndex = -1;
14451                 this.onLoad();   
14452             }
14453         }
14454         
14455         this.loadNext = false;
14456     },
14457     
14458     // private
14459     getParams : function(q){
14460         var p = {};
14461         //p[this.queryParam] = q;
14462         
14463         if(this.pageSize){
14464             p.start = 0;
14465             p.limit = this.pageSize;
14466         }
14467         return p;
14468     },
14469
14470     /**
14471      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14472      */
14473     collapse : function(){
14474         if(!this.isExpanded()){
14475             return;
14476         }
14477         
14478         this.list.hide();
14479         
14480         this.hasFocus = false;
14481         
14482         if(this.tickable){
14483             this.okBtn.hide();
14484             this.cancelBtn.hide();
14485             this.trigger.show();
14486             
14487             if(this.editable){
14488                 this.tickableInputEl().dom.value = '';
14489                 this.tickableInputEl().blur();
14490             }
14491             
14492         }
14493         
14494         Roo.get(document).un('mousedown', this.collapseIf, this);
14495         Roo.get(document).un('mousewheel', this.collapseIf, this);
14496         if (!this.editable) {
14497             Roo.get(document).un('keydown', this.listKeyPress, this);
14498         }
14499         this.fireEvent('collapse', this);
14500         
14501         this.validate();
14502     },
14503
14504     // private
14505     collapseIf : function(e){
14506         var in_combo  = e.within(this.el);
14507         var in_list =  e.within(this.list);
14508         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14509         
14510         if (in_combo || in_list || is_list) {
14511             //e.stopPropagation();
14512             return;
14513         }
14514         
14515         if(this.tickable){
14516             this.onTickableFooterButtonClick(e, false, false);
14517         }
14518
14519         this.collapse();
14520         
14521     },
14522
14523     /**
14524      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14525      */
14526     expand : function(){
14527        
14528         if(this.isExpanded() || !this.hasFocus){
14529             return;
14530         }
14531         
14532         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14533         this.list.setWidth(lw);
14534         
14535         Roo.log('expand');
14536         
14537         this.list.show();
14538         
14539         this.restrictHeight();
14540         
14541         if(this.tickable){
14542             
14543             this.tickItems = Roo.apply([], this.item);
14544             
14545             this.okBtn.show();
14546             this.cancelBtn.show();
14547             this.trigger.hide();
14548             
14549             if(this.editable){
14550                 this.tickableInputEl().focus();
14551             }
14552             
14553         }
14554         
14555         Roo.get(document).on('mousedown', this.collapseIf, this);
14556         Roo.get(document).on('mousewheel', this.collapseIf, this);
14557         if (!this.editable) {
14558             Roo.get(document).on('keydown', this.listKeyPress, this);
14559         }
14560         
14561         this.fireEvent('expand', this);
14562     },
14563
14564     // private
14565     // Implements the default empty TriggerField.onTriggerClick function
14566     onTriggerClick : function(e)
14567     {
14568         Roo.log('trigger click');
14569         
14570         if(this.disabled || !this.triggerList){
14571             return;
14572         }
14573         
14574         this.page = 0;
14575         this.loadNext = false;
14576         
14577         if(this.isExpanded()){
14578             this.collapse();
14579             if (!this.blockFocus) {
14580                 this.inputEl().focus();
14581             }
14582             
14583         }else {
14584             this.hasFocus = true;
14585             if(this.triggerAction == 'all') {
14586                 this.doQuery(this.allQuery, true);
14587             } else {
14588                 this.doQuery(this.getRawValue());
14589             }
14590             if (!this.blockFocus) {
14591                 this.inputEl().focus();
14592             }
14593         }
14594     },
14595     
14596     onTickableTriggerClick : function(e)
14597     {
14598         if(this.disabled){
14599             return;
14600         }
14601         
14602         this.page = 0;
14603         this.loadNext = false;
14604         this.hasFocus = true;
14605         
14606         if(this.triggerAction == 'all') {
14607             this.doQuery(this.allQuery, true);
14608         } else {
14609             this.doQuery(this.getRawValue());
14610         }
14611     },
14612     
14613     onSearchFieldClick : function(e)
14614     {
14615         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14616             this.onTickableFooterButtonClick(e, false, false);
14617             return;
14618         }
14619         
14620         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14621             return;
14622         }
14623         
14624         this.page = 0;
14625         this.loadNext = false;
14626         this.hasFocus = true;
14627         
14628         if(this.triggerAction == 'all') {
14629             this.doQuery(this.allQuery, true);
14630         } else {
14631             this.doQuery(this.getRawValue());
14632         }
14633     },
14634     
14635     listKeyPress : function(e)
14636     {
14637         //Roo.log('listkeypress');
14638         // scroll to first matching element based on key pres..
14639         if (e.isSpecialKey()) {
14640             return false;
14641         }
14642         var k = String.fromCharCode(e.getKey()).toUpperCase();
14643         //Roo.log(k);
14644         var match  = false;
14645         var csel = this.view.getSelectedNodes();
14646         var cselitem = false;
14647         if (csel.length) {
14648             var ix = this.view.indexOf(csel[0]);
14649             cselitem  = this.store.getAt(ix);
14650             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14651                 cselitem = false;
14652             }
14653             
14654         }
14655         
14656         this.store.each(function(v) { 
14657             if (cselitem) {
14658                 // start at existing selection.
14659                 if (cselitem.id == v.id) {
14660                     cselitem = false;
14661                 }
14662                 return true;
14663             }
14664                 
14665             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14666                 match = this.store.indexOf(v);
14667                 return false;
14668             }
14669             return true;
14670         }, this);
14671         
14672         if (match === false) {
14673             return true; // no more action?
14674         }
14675         // scroll to?
14676         this.view.select(match);
14677         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14678         sn.scrollIntoView(sn.dom.parentNode, false);
14679     },
14680     
14681     onViewScroll : function(e, t){
14682         
14683         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){
14684             return;
14685         }
14686         
14687         this.hasQuery = true;
14688         
14689         this.loading = this.list.select('.loading', true).first();
14690         
14691         if(this.loading === null){
14692             this.list.createChild({
14693                 tag: 'div',
14694                 cls: 'loading roo-select2-more-results roo-select2-active',
14695                 html: 'Loading more results...'
14696             });
14697             
14698             this.loading = this.list.select('.loading', true).first();
14699             
14700             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14701             
14702             this.loading.hide();
14703         }
14704         
14705         this.loading.show();
14706         
14707         var _combo = this;
14708         
14709         this.page++;
14710         this.loadNext = true;
14711         
14712         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14713         
14714         return;
14715     },
14716     
14717     addItem : function(o)
14718     {   
14719         var dv = ''; // display value
14720         
14721         if (this.displayField) {
14722             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14723         } else {
14724             // this is an error condition!!!
14725             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14726         }
14727         
14728         if(!dv.length){
14729             return;
14730         }
14731         
14732         var choice = this.choices.createChild({
14733             tag: 'li',
14734             cls: 'roo-select2-search-choice',
14735             cn: [
14736                 {
14737                     tag: 'div',
14738                     html: dv
14739                 },
14740                 {
14741                     tag: 'a',
14742                     href: '#',
14743                     cls: 'roo-select2-search-choice-close fa fa-times',
14744                     tabindex: '-1'
14745                 }
14746             ]
14747             
14748         }, this.searchField);
14749         
14750         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14751         
14752         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14753         
14754         this.item.push(o);
14755         
14756         this.lastData = o;
14757         
14758         this.syncValue();
14759         
14760         this.inputEl().dom.value = '';
14761         
14762         this.validate();
14763     },
14764     
14765     onRemoveItem : function(e, _self, o)
14766     {
14767         e.preventDefault();
14768         
14769         this.lastItem = Roo.apply([], this.item);
14770         
14771         var index = this.item.indexOf(o.data) * 1;
14772         
14773         if( index < 0){
14774             Roo.log('not this item?!');
14775             return;
14776         }
14777         
14778         this.item.splice(index, 1);
14779         o.item.remove();
14780         
14781         this.syncValue();
14782         
14783         this.fireEvent('remove', this, e);
14784         
14785         this.validate();
14786         
14787     },
14788     
14789     syncValue : function()
14790     {
14791         if(!this.item.length){
14792             this.clearValue();
14793             return;
14794         }
14795             
14796         var value = [];
14797         var _this = this;
14798         Roo.each(this.item, function(i){
14799             if(_this.valueField){
14800                 value.push(i[_this.valueField]);
14801                 return;
14802             }
14803
14804             value.push(i);
14805         });
14806
14807         this.value = value.join(',');
14808
14809         if(this.hiddenField){
14810             this.hiddenField.dom.value = this.value;
14811         }
14812         
14813         this.store.fireEvent("datachanged", this.store);
14814         
14815         this.validate();
14816     },
14817     
14818     clearItem : function()
14819     {
14820         if(!this.multiple){
14821             return;
14822         }
14823         
14824         this.item = [];
14825         
14826         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14827            c.remove();
14828         });
14829         
14830         this.syncValue();
14831         
14832         this.validate();
14833         
14834         if(this.tickable && !Roo.isTouch){
14835             this.view.refresh();
14836         }
14837     },
14838     
14839     inputEl: function ()
14840     {
14841         if(Roo.isIOS && this.useNativeIOS){
14842             return this.el.select('select.roo-ios-select', true).first();
14843         }
14844         
14845         if(Roo.isTouch && this.mobileTouchView){
14846             return this.el.select('input.form-control',true).first();
14847         }
14848         
14849         if(this.tickable){
14850             return this.searchField;
14851         }
14852         
14853         return this.el.select('input.form-control',true).first();
14854     },
14855     
14856     onTickableFooterButtonClick : function(e, btn, el)
14857     {
14858         e.preventDefault();
14859         
14860         this.lastItem = Roo.apply([], this.item);
14861         
14862         if(btn && btn.name == 'cancel'){
14863             this.tickItems = Roo.apply([], this.item);
14864             this.collapse();
14865             return;
14866         }
14867         
14868         this.clearItem();
14869         
14870         var _this = this;
14871         
14872         Roo.each(this.tickItems, function(o){
14873             _this.addItem(o);
14874         });
14875         
14876         this.collapse();
14877         
14878     },
14879     
14880     validate : function()
14881     {
14882         if(this.getVisibilityEl().hasClass('hidden')){
14883             return true;
14884         }
14885         
14886         var v = this.getRawValue();
14887         
14888         if(this.multiple){
14889             v = this.getValue();
14890         }
14891         
14892         if(this.disabled || this.allowBlank || v.length){
14893             this.markValid();
14894             return true;
14895         }
14896         
14897         this.markInvalid();
14898         return false;
14899     },
14900     
14901     tickableInputEl : function()
14902     {
14903         if(!this.tickable || !this.editable){
14904             return this.inputEl();
14905         }
14906         
14907         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14908     },
14909     
14910     
14911     getAutoCreateTouchView : function()
14912     {
14913         var id = Roo.id();
14914         
14915         var cfg = {
14916             cls: 'form-group' //input-group
14917         };
14918         
14919         var input =  {
14920             tag: 'input',
14921             id : id,
14922             type : this.inputType,
14923             cls : 'form-control x-combo-noedit',
14924             autocomplete: 'new-password',
14925             placeholder : this.placeholder || '',
14926             readonly : true
14927         };
14928         
14929         if (this.name) {
14930             input.name = this.name;
14931         }
14932         
14933         if (this.size) {
14934             input.cls += ' input-' + this.size;
14935         }
14936         
14937         if (this.disabled) {
14938             input.disabled = true;
14939         }
14940         
14941         var inputblock = {
14942             cls : '',
14943             cn : [
14944                 input
14945             ]
14946         };
14947         
14948         if(this.before){
14949             inputblock.cls += ' input-group';
14950             
14951             inputblock.cn.unshift({
14952                 tag :'span',
14953                 cls : 'input-group-addon',
14954                 html : this.before
14955             });
14956         }
14957         
14958         if(this.removable && !this.multiple){
14959             inputblock.cls += ' roo-removable';
14960             
14961             inputblock.cn.push({
14962                 tag: 'button',
14963                 html : 'x',
14964                 cls : 'roo-combo-removable-btn close'
14965             });
14966         }
14967
14968         if(this.hasFeedback && !this.allowBlank){
14969             
14970             inputblock.cls += ' has-feedback';
14971             
14972             inputblock.cn.push({
14973                 tag: 'span',
14974                 cls: 'glyphicon form-control-feedback'
14975             });
14976             
14977         }
14978         
14979         if (this.after) {
14980             
14981             inputblock.cls += (this.before) ? '' : ' input-group';
14982             
14983             inputblock.cn.push({
14984                 tag :'span',
14985                 cls : 'input-group-addon',
14986                 html : this.after
14987             });
14988         }
14989
14990         var box = {
14991             tag: 'div',
14992             cn: [
14993                 {
14994                     tag: 'input',
14995                     type : 'hidden',
14996                     cls: 'form-hidden-field'
14997                 },
14998                 inputblock
14999             ]
15000             
15001         };
15002         
15003         if(this.multiple){
15004             box = {
15005                 tag: 'div',
15006                 cn: [
15007                     {
15008                         tag: 'input',
15009                         type : 'hidden',
15010                         cls: 'form-hidden-field'
15011                     },
15012                     {
15013                         tag: 'ul',
15014                         cls: 'roo-select2-choices',
15015                         cn:[
15016                             {
15017                                 tag: 'li',
15018                                 cls: 'roo-select2-search-field',
15019                                 cn: [
15020
15021                                     inputblock
15022                                 ]
15023                             }
15024                         ]
15025                     }
15026                 ]
15027             }
15028         };
15029         
15030         var combobox = {
15031             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15032             cn: [
15033                 box
15034             ]
15035         };
15036         
15037         if(!this.multiple && this.showToggleBtn){
15038             
15039             var caret = {
15040                         tag: 'span',
15041                         cls: 'caret'
15042             };
15043             
15044             if (this.caret != false) {
15045                 caret = {
15046                      tag: 'i',
15047                      cls: 'fa fa-' + this.caret
15048                 };
15049                 
15050             }
15051             
15052             combobox.cn.push({
15053                 tag :'span',
15054                 cls : 'input-group-addon btn dropdown-toggle',
15055                 cn : [
15056                     caret,
15057                     {
15058                         tag: 'span',
15059                         cls: 'combobox-clear',
15060                         cn  : [
15061                             {
15062                                 tag : 'i',
15063                                 cls: 'icon-remove'
15064                             }
15065                         ]
15066                     }
15067                 ]
15068
15069             })
15070         }
15071         
15072         if(this.multiple){
15073             combobox.cls += ' roo-select2-container-multi';
15074         }
15075         
15076         var align = this.labelAlign || this.parentLabelAlign();
15077         
15078         if (align ==='left' && this.fieldLabel.length) {
15079
15080             cfg.cn = [
15081                 {
15082                    tag : 'i',
15083                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15084                    tooltip : 'This field is required'
15085                 },
15086                 {
15087                     tag: 'label',
15088                     cls : 'control-label',
15089                     html : this.fieldLabel
15090
15091                 },
15092                 {
15093                     cls : '', 
15094                     cn: [
15095                         combobox
15096                     ]
15097                 }
15098             ];
15099             
15100             var labelCfg = cfg.cn[1];
15101             var contentCfg = cfg.cn[2];
15102             
15103
15104             if(this.indicatorpos == 'right'){
15105                 cfg.cn = [
15106                     {
15107                         tag: 'label',
15108                         'for' :  id,
15109                         cls : 'control-label',
15110                         cn : [
15111                             {
15112                                 tag : 'span',
15113                                 html : this.fieldLabel
15114                             },
15115                             {
15116                                 tag : 'i',
15117                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15118                                 tooltip : 'This field is required'
15119                             }
15120                         ]
15121                     },
15122                     {
15123                         cls : "",
15124                         cn: [
15125                             combobox
15126                         ]
15127                     }
15128
15129                 ];
15130                 
15131                 labelCfg = cfg.cn[0];
15132                 contentCfg = cfg.cn[1];
15133             }
15134             
15135            
15136             
15137             if(this.labelWidth > 12){
15138                 labelCfg.style = "width: " + this.labelWidth + 'px';
15139             }
15140             
15141             if(this.labelWidth < 13 && this.labelmd == 0){
15142                 this.labelmd = this.labelWidth;
15143             }
15144             
15145             if(this.labellg > 0){
15146                 labelCfg.cls += ' col-lg-' + this.labellg;
15147                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15148             }
15149             
15150             if(this.labelmd > 0){
15151                 labelCfg.cls += ' col-md-' + this.labelmd;
15152                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15153             }
15154             
15155             if(this.labelsm > 0){
15156                 labelCfg.cls += ' col-sm-' + this.labelsm;
15157                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15158             }
15159             
15160             if(this.labelxs > 0){
15161                 labelCfg.cls += ' col-xs-' + this.labelxs;
15162                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15163             }
15164                 
15165                 
15166         } else if ( this.fieldLabel.length) {
15167             cfg.cn = [
15168                 {
15169                    tag : 'i',
15170                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15171                    tooltip : 'This field is required'
15172                 },
15173                 {
15174                     tag: 'label',
15175                     cls : 'control-label',
15176                     html : this.fieldLabel
15177
15178                 },
15179                 {
15180                     cls : '', 
15181                     cn: [
15182                         combobox
15183                     ]
15184                 }
15185             ];
15186             
15187             if(this.indicatorpos == 'right'){
15188                 cfg.cn = [
15189                     {
15190                         tag: 'label',
15191                         cls : 'control-label',
15192                         html : this.fieldLabel,
15193                         cn : [
15194                             {
15195                                tag : 'i',
15196                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15197                                tooltip : 'This field is required'
15198                             }
15199                         ]
15200                     },
15201                     {
15202                         cls : '', 
15203                         cn: [
15204                             combobox
15205                         ]
15206                     }
15207                 ];
15208             }
15209         } else {
15210             cfg.cn = combobox;    
15211         }
15212         
15213         
15214         var settings = this;
15215         
15216         ['xs','sm','md','lg'].map(function(size){
15217             if (settings[size]) {
15218                 cfg.cls += ' col-' + size + '-' + settings[size];
15219             }
15220         });
15221         
15222         return cfg;
15223     },
15224     
15225     initTouchView : function()
15226     {
15227         this.renderTouchView();
15228         
15229         this.touchViewEl.on('scroll', function(){
15230             this.el.dom.scrollTop = 0;
15231         }, this);
15232         
15233         this.originalValue = this.getValue();
15234         
15235         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15236         
15237         this.inputEl().on("click", this.showTouchView, this);
15238         if (this.triggerEl) {
15239             this.triggerEl.on("click", this.showTouchView, this);
15240         }
15241         
15242         
15243         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15244         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15245         
15246         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15247         
15248         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15249         this.store.on('load', this.onTouchViewLoad, this);
15250         this.store.on('loadexception', this.onTouchViewLoadException, this);
15251         
15252         if(this.hiddenName){
15253             
15254             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15255             
15256             this.hiddenField.dom.value =
15257                 this.hiddenValue !== undefined ? this.hiddenValue :
15258                 this.value !== undefined ? this.value : '';
15259         
15260             this.el.dom.removeAttribute('name');
15261             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15262         }
15263         
15264         if(this.multiple){
15265             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15266             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15267         }
15268         
15269         if(this.removable && !this.multiple){
15270             var close = this.closeTriggerEl();
15271             if(close){
15272                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15273                 close.on('click', this.removeBtnClick, this, close);
15274             }
15275         }
15276         /*
15277          * fix the bug in Safari iOS8
15278          */
15279         this.inputEl().on("focus", function(e){
15280             document.activeElement.blur();
15281         }, this);
15282         
15283         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15284         
15285         return;
15286         
15287         
15288     },
15289     
15290     renderTouchView : function()
15291     {
15292         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15293         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15294         
15295         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15296         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15297         
15298         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15299         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15300         this.touchViewBodyEl.setStyle('overflow', 'auto');
15301         
15302         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15303         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15304         
15305         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15306         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15307         
15308     },
15309     
15310     showTouchView : function()
15311     {
15312         if(this.disabled){
15313             return;
15314         }
15315         
15316         this.touchViewHeaderEl.hide();
15317
15318         if(this.modalTitle.length){
15319             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15320             this.touchViewHeaderEl.show();
15321         }
15322
15323         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15324         this.touchViewEl.show();
15325
15326         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15327         
15328         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15329         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15330
15331         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15332
15333         if(this.modalTitle.length){
15334             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15335         }
15336         
15337         this.touchViewBodyEl.setHeight(bodyHeight);
15338
15339         if(this.animate){
15340             var _this = this;
15341             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15342         }else{
15343             this.touchViewEl.addClass('in');
15344         }
15345         
15346         if(this._touchViewMask){
15347             Roo.get(document.body).addClass("x-body-masked");
15348             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15349             this._touchViewMask.setStyle('z-index', 10000);
15350             this._touchViewMask.addClass('show');
15351         }
15352         
15353         this.doTouchViewQuery();
15354         
15355     },
15356     
15357     hideTouchView : function()
15358     {
15359         this.touchViewEl.removeClass('in');
15360
15361         if(this.animate){
15362             var _this = this;
15363             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15364         }else{
15365             this.touchViewEl.setStyle('display', 'none');
15366         }
15367         
15368         if(this._touchViewMask){
15369             this._touchViewMask.removeClass('show');
15370             Roo.get(document.body).removeClass("x-body-masked");
15371         }
15372     },
15373     
15374     setTouchViewValue : function()
15375     {
15376         if(this.multiple){
15377             this.clearItem();
15378         
15379             var _this = this;
15380
15381             Roo.each(this.tickItems, function(o){
15382                 this.addItem(o);
15383             }, this);
15384         }
15385         
15386         this.hideTouchView();
15387     },
15388     
15389     doTouchViewQuery : function()
15390     {
15391         var qe = {
15392             query: '',
15393             forceAll: true,
15394             combo: this,
15395             cancel:false
15396         };
15397         
15398         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15399             return false;
15400         }
15401         
15402         if(!this.alwaysQuery || this.mode == 'local'){
15403             this.onTouchViewLoad();
15404             return;
15405         }
15406         
15407         this.store.load();
15408     },
15409     
15410     onTouchViewBeforeLoad : function(combo,opts)
15411     {
15412         return;
15413     },
15414
15415     // private
15416     onTouchViewLoad : function()
15417     {
15418         if(this.store.getCount() < 1){
15419             this.onTouchViewEmptyResults();
15420             return;
15421         }
15422         
15423         this.clearTouchView();
15424         
15425         var rawValue = this.getRawValue();
15426         
15427         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15428         
15429         this.tickItems = [];
15430         
15431         this.store.data.each(function(d, rowIndex){
15432             var row = this.touchViewListGroup.createChild(template);
15433             
15434             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15435                 row.addClass(d.data.cls);
15436             }
15437             
15438             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15439                 var cfg = {
15440                     data : d.data,
15441                     html : d.data[this.displayField]
15442                 };
15443                 
15444                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15445                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15446                 }
15447             }
15448             row.removeClass('selected');
15449             if(!this.multiple && this.valueField &&
15450                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15451             {
15452                 // radio buttons..
15453                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15454                 row.addClass('selected');
15455             }
15456             
15457             if(this.multiple && this.valueField &&
15458                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15459             {
15460                 
15461                 // checkboxes...
15462                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15463                 this.tickItems.push(d.data);
15464             }
15465             
15466             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15467             
15468         }, this);
15469         
15470         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15471         
15472         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15473
15474         if(this.modalTitle.length){
15475             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15476         }
15477
15478         var listHeight = this.touchViewListGroup.getHeight();
15479         
15480         if(this.mobile_restrict_height && listHeight < bodyHeight){
15481             this.touchViewBodyEl.setHeight(listHeight);
15482         }
15483         
15484         var _this = this;
15485         
15486         if(firstChecked && listHeight > bodyHeight){
15487             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15488         }
15489         
15490     },
15491     
15492     onTouchViewLoadException : function()
15493     {
15494         this.hideTouchView();
15495     },
15496     
15497     onTouchViewEmptyResults : function()
15498     {
15499         this.clearTouchView();
15500         
15501         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15502         
15503         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15504         
15505     },
15506     
15507     clearTouchView : function()
15508     {
15509         this.touchViewListGroup.dom.innerHTML = '';
15510     },
15511     
15512     onTouchViewClick : function(e, el, o)
15513     {
15514         e.preventDefault();
15515         
15516         var row = o.row;
15517         var rowIndex = o.rowIndex;
15518         
15519         var r = this.store.getAt(rowIndex);
15520         
15521         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15522             
15523             if(!this.multiple){
15524                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15525                     c.dom.removeAttribute('checked');
15526                 }, this);
15527
15528                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15529
15530                 this.setFromData(r.data);
15531
15532                 var close = this.closeTriggerEl();
15533
15534                 if(close){
15535                     close.show();
15536                 }
15537
15538                 this.hideTouchView();
15539
15540                 this.fireEvent('select', this, r, rowIndex);
15541
15542                 return;
15543             }
15544
15545             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15546                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15547                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15548                 return;
15549             }
15550
15551             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15552             this.addItem(r.data);
15553             this.tickItems.push(r.data);
15554         }
15555     },
15556     
15557     getAutoCreateNativeIOS : function()
15558     {
15559         var cfg = {
15560             cls: 'form-group' //input-group,
15561         };
15562         
15563         var combobox =  {
15564             tag: 'select',
15565             cls : 'roo-ios-select'
15566         };
15567         
15568         if (this.name) {
15569             combobox.name = this.name;
15570         }
15571         
15572         if (this.disabled) {
15573             combobox.disabled = true;
15574         }
15575         
15576         var settings = this;
15577         
15578         ['xs','sm','md','lg'].map(function(size){
15579             if (settings[size]) {
15580                 cfg.cls += ' col-' + size + '-' + settings[size];
15581             }
15582         });
15583         
15584         cfg.cn = combobox;
15585         
15586         return cfg;
15587         
15588     },
15589     
15590     initIOSView : function()
15591     {
15592         this.store.on('load', this.onIOSViewLoad, this);
15593         
15594         return;
15595     },
15596     
15597     onIOSViewLoad : function()
15598     {
15599         if(this.store.getCount() < 1){
15600             return;
15601         }
15602         
15603         this.clearIOSView();
15604         
15605         if(this.allowBlank) {
15606             
15607             var default_text = '-- SELECT --';
15608             
15609             if(this.placeholder.length){
15610                 default_text = this.placeholder;
15611             }
15612             
15613             if(this.emptyTitle.length){
15614                 default_text += ' - ' + this.emptyTitle + ' -';
15615             }
15616             
15617             var opt = this.inputEl().createChild({
15618                 tag: 'option',
15619                 value : 0,
15620                 html : default_text
15621             });
15622             
15623             var o = {};
15624             o[this.valueField] = 0;
15625             o[this.displayField] = default_text;
15626             
15627             this.ios_options.push({
15628                 data : o,
15629                 el : opt
15630             });
15631             
15632         }
15633         
15634         this.store.data.each(function(d, rowIndex){
15635             
15636             var html = '';
15637             
15638             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15639                 html = d.data[this.displayField];
15640             }
15641             
15642             var value = '';
15643             
15644             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15645                 value = d.data[this.valueField];
15646             }
15647             
15648             var option = {
15649                 tag: 'option',
15650                 value : value,
15651                 html : html
15652             };
15653             
15654             if(this.value == d.data[this.valueField]){
15655                 option['selected'] = true;
15656             }
15657             
15658             var opt = this.inputEl().createChild(option);
15659             
15660             this.ios_options.push({
15661                 data : d.data,
15662                 el : opt
15663             });
15664             
15665         }, this);
15666         
15667         this.inputEl().on('change', function(){
15668            this.fireEvent('select', this);
15669         }, this);
15670         
15671     },
15672     
15673     clearIOSView: function()
15674     {
15675         this.inputEl().dom.innerHTML = '';
15676         
15677         this.ios_options = [];
15678     },
15679     
15680     setIOSValue: function(v)
15681     {
15682         this.value = v;
15683         
15684         if(!this.ios_options){
15685             return;
15686         }
15687         
15688         Roo.each(this.ios_options, function(opts){
15689            
15690            opts.el.dom.removeAttribute('selected');
15691            
15692            if(opts.data[this.valueField] != v){
15693                return;
15694            }
15695            
15696            opts.el.dom.setAttribute('selected', true);
15697            
15698         }, this);
15699     }
15700
15701     /** 
15702     * @cfg {Boolean} grow 
15703     * @hide 
15704     */
15705     /** 
15706     * @cfg {Number} growMin 
15707     * @hide 
15708     */
15709     /** 
15710     * @cfg {Number} growMax 
15711     * @hide 
15712     */
15713     /**
15714      * @hide
15715      * @method autoSize
15716      */
15717 });
15718
15719 Roo.apply(Roo.bootstrap.ComboBox,  {
15720     
15721     header : {
15722         tag: 'div',
15723         cls: 'modal-header',
15724         cn: [
15725             {
15726                 tag: 'h4',
15727                 cls: 'modal-title'
15728             }
15729         ]
15730     },
15731     
15732     body : {
15733         tag: 'div',
15734         cls: 'modal-body',
15735         cn: [
15736             {
15737                 tag: 'ul',
15738                 cls: 'list-group'
15739             }
15740         ]
15741     },
15742     
15743     listItemRadio : {
15744         tag: 'li',
15745         cls: 'list-group-item',
15746         cn: [
15747             {
15748                 tag: 'span',
15749                 cls: 'roo-combobox-list-group-item-value'
15750             },
15751             {
15752                 tag: 'div',
15753                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15754                 cn: [
15755                     {
15756                         tag: 'input',
15757                         type: 'radio'
15758                     },
15759                     {
15760                         tag: 'label'
15761                     }
15762                 ]
15763             }
15764         ]
15765     },
15766     
15767     listItemCheckbox : {
15768         tag: 'li',
15769         cls: 'list-group-item',
15770         cn: [
15771             {
15772                 tag: 'span',
15773                 cls: 'roo-combobox-list-group-item-value'
15774             },
15775             {
15776                 tag: 'div',
15777                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15778                 cn: [
15779                     {
15780                         tag: 'input',
15781                         type: 'checkbox'
15782                     },
15783                     {
15784                         tag: 'label'
15785                     }
15786                 ]
15787             }
15788         ]
15789     },
15790     
15791     emptyResult : {
15792         tag: 'div',
15793         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15794     },
15795     
15796     footer : {
15797         tag: 'div',
15798         cls: 'modal-footer',
15799         cn: [
15800             {
15801                 tag: 'div',
15802                 cls: 'row',
15803                 cn: [
15804                     {
15805                         tag: 'div',
15806                         cls: 'col-xs-6 text-left',
15807                         cn: {
15808                             tag: 'button',
15809                             cls: 'btn btn-danger roo-touch-view-cancel',
15810                             html: 'Cancel'
15811                         }
15812                     },
15813                     {
15814                         tag: 'div',
15815                         cls: 'col-xs-6 text-right',
15816                         cn: {
15817                             tag: 'button',
15818                             cls: 'btn btn-success roo-touch-view-ok',
15819                             html: 'OK'
15820                         }
15821                     }
15822                 ]
15823             }
15824         ]
15825         
15826     }
15827 });
15828
15829 Roo.apply(Roo.bootstrap.ComboBox,  {
15830     
15831     touchViewTemplate : {
15832         tag: 'div',
15833         cls: 'modal fade roo-combobox-touch-view',
15834         cn: [
15835             {
15836                 tag: 'div',
15837                 cls: 'modal-dialog',
15838                 style : 'position:fixed', // we have to fix position....
15839                 cn: [
15840                     {
15841                         tag: 'div',
15842                         cls: 'modal-content',
15843                         cn: [
15844                             Roo.bootstrap.ComboBox.header,
15845                             Roo.bootstrap.ComboBox.body,
15846                             Roo.bootstrap.ComboBox.footer
15847                         ]
15848                     }
15849                 ]
15850             }
15851         ]
15852     }
15853 });/*
15854  * Based on:
15855  * Ext JS Library 1.1.1
15856  * Copyright(c) 2006-2007, Ext JS, LLC.
15857  *
15858  * Originally Released Under LGPL - original licence link has changed is not relivant.
15859  *
15860  * Fork - LGPL
15861  * <script type="text/javascript">
15862  */
15863
15864 /**
15865  * @class Roo.View
15866  * @extends Roo.util.Observable
15867  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15868  * This class also supports single and multi selection modes. <br>
15869  * Create a data model bound view:
15870  <pre><code>
15871  var store = new Roo.data.Store(...);
15872
15873  var view = new Roo.View({
15874     el : "my-element",
15875     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15876  
15877     singleSelect: true,
15878     selectedClass: "ydataview-selected",
15879     store: store
15880  });
15881
15882  // listen for node click?
15883  view.on("click", function(vw, index, node, e){
15884  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15885  });
15886
15887  // load XML data
15888  dataModel.load("foobar.xml");
15889  </code></pre>
15890  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15891  * <br><br>
15892  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15893  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15894  * 
15895  * Note: old style constructor is still suported (container, template, config)
15896  * 
15897  * @constructor
15898  * Create a new View
15899  * @param {Object} config The config object
15900  * 
15901  */
15902 Roo.View = function(config, depreciated_tpl, depreciated_config){
15903     
15904     this.parent = false;
15905     
15906     if (typeof(depreciated_tpl) == 'undefined') {
15907         // new way.. - universal constructor.
15908         Roo.apply(this, config);
15909         this.el  = Roo.get(this.el);
15910     } else {
15911         // old format..
15912         this.el  = Roo.get(config);
15913         this.tpl = depreciated_tpl;
15914         Roo.apply(this, depreciated_config);
15915     }
15916     this.wrapEl  = this.el.wrap().wrap();
15917     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15918     
15919     
15920     if(typeof(this.tpl) == "string"){
15921         this.tpl = new Roo.Template(this.tpl);
15922     } else {
15923         // support xtype ctors..
15924         this.tpl = new Roo.factory(this.tpl, Roo);
15925     }
15926     
15927     
15928     this.tpl.compile();
15929     
15930     /** @private */
15931     this.addEvents({
15932         /**
15933          * @event beforeclick
15934          * Fires before a click is processed. Returns false to cancel the default action.
15935          * @param {Roo.View} this
15936          * @param {Number} index The index of the target node
15937          * @param {HTMLElement} node The target node
15938          * @param {Roo.EventObject} e The raw event object
15939          */
15940             "beforeclick" : true,
15941         /**
15942          * @event click
15943          * Fires when a template node is clicked.
15944          * @param {Roo.View} this
15945          * @param {Number} index The index of the target node
15946          * @param {HTMLElement} node The target node
15947          * @param {Roo.EventObject} e The raw event object
15948          */
15949             "click" : true,
15950         /**
15951          * @event dblclick
15952          * Fires when a template node is double clicked.
15953          * @param {Roo.View} this
15954          * @param {Number} index The index of the target node
15955          * @param {HTMLElement} node The target node
15956          * @param {Roo.EventObject} e The raw event object
15957          */
15958             "dblclick" : true,
15959         /**
15960          * @event contextmenu
15961          * Fires when a template node is right clicked.
15962          * @param {Roo.View} this
15963          * @param {Number} index The index of the target node
15964          * @param {HTMLElement} node The target node
15965          * @param {Roo.EventObject} e The raw event object
15966          */
15967             "contextmenu" : true,
15968         /**
15969          * @event selectionchange
15970          * Fires when the selected nodes change.
15971          * @param {Roo.View} this
15972          * @param {Array} selections Array of the selected nodes
15973          */
15974             "selectionchange" : true,
15975     
15976         /**
15977          * @event beforeselect
15978          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15979          * @param {Roo.View} this
15980          * @param {HTMLElement} node The node to be selected
15981          * @param {Array} selections Array of currently selected nodes
15982          */
15983             "beforeselect" : true,
15984         /**
15985          * @event preparedata
15986          * Fires on every row to render, to allow you to change the data.
15987          * @param {Roo.View} this
15988          * @param {Object} data to be rendered (change this)
15989          */
15990           "preparedata" : true
15991           
15992           
15993         });
15994
15995
15996
15997     this.el.on({
15998         "click": this.onClick,
15999         "dblclick": this.onDblClick,
16000         "contextmenu": this.onContextMenu,
16001         scope:this
16002     });
16003
16004     this.selections = [];
16005     this.nodes = [];
16006     this.cmp = new Roo.CompositeElementLite([]);
16007     if(this.store){
16008         this.store = Roo.factory(this.store, Roo.data);
16009         this.setStore(this.store, true);
16010     }
16011     
16012     if ( this.footer && this.footer.xtype) {
16013            
16014          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16015         
16016         this.footer.dataSource = this.store;
16017         this.footer.container = fctr;
16018         this.footer = Roo.factory(this.footer, Roo);
16019         fctr.insertFirst(this.el);
16020         
16021         // this is a bit insane - as the paging toolbar seems to detach the el..
16022 //        dom.parentNode.parentNode.parentNode
16023          // they get detached?
16024     }
16025     
16026     
16027     Roo.View.superclass.constructor.call(this);
16028     
16029     
16030 };
16031
16032 Roo.extend(Roo.View, Roo.util.Observable, {
16033     
16034      /**
16035      * @cfg {Roo.data.Store} store Data store to load data from.
16036      */
16037     store : false,
16038     
16039     /**
16040      * @cfg {String|Roo.Element} el The container element.
16041      */
16042     el : '',
16043     
16044     /**
16045      * @cfg {String|Roo.Template} tpl The template used by this View 
16046      */
16047     tpl : false,
16048     /**
16049      * @cfg {String} dataName the named area of the template to use as the data area
16050      *                          Works with domtemplates roo-name="name"
16051      */
16052     dataName: false,
16053     /**
16054      * @cfg {String} selectedClass The css class to add to selected nodes
16055      */
16056     selectedClass : "x-view-selected",
16057      /**
16058      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16059      */
16060     emptyText : "",
16061     
16062     /**
16063      * @cfg {String} text to display on mask (default Loading)
16064      */
16065     mask : false,
16066     /**
16067      * @cfg {Boolean} multiSelect Allow multiple selection
16068      */
16069     multiSelect : false,
16070     /**
16071      * @cfg {Boolean} singleSelect Allow single selection
16072      */
16073     singleSelect:  false,
16074     
16075     /**
16076      * @cfg {Boolean} toggleSelect - selecting 
16077      */
16078     toggleSelect : false,
16079     
16080     /**
16081      * @cfg {Boolean} tickable - selecting 
16082      */
16083     tickable : false,
16084     
16085     /**
16086      * Returns the element this view is bound to.
16087      * @return {Roo.Element}
16088      */
16089     getEl : function(){
16090         return this.wrapEl;
16091     },
16092     
16093     
16094
16095     /**
16096      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16097      */
16098     refresh : function(){
16099         //Roo.log('refresh');
16100         var t = this.tpl;
16101         
16102         // if we are using something like 'domtemplate', then
16103         // the what gets used is:
16104         // t.applySubtemplate(NAME, data, wrapping data..)
16105         // the outer template then get' applied with
16106         //     the store 'extra data'
16107         // and the body get's added to the
16108         //      roo-name="data" node?
16109         //      <span class='roo-tpl-{name}'></span> ?????
16110         
16111         
16112         
16113         this.clearSelections();
16114         this.el.update("");
16115         var html = [];
16116         var records = this.store.getRange();
16117         if(records.length < 1) {
16118             
16119             // is this valid??  = should it render a template??
16120             
16121             this.el.update(this.emptyText);
16122             return;
16123         }
16124         var el = this.el;
16125         if (this.dataName) {
16126             this.el.update(t.apply(this.store.meta)); //????
16127             el = this.el.child('.roo-tpl-' + this.dataName);
16128         }
16129         
16130         for(var i = 0, len = records.length; i < len; i++){
16131             var data = this.prepareData(records[i].data, i, records[i]);
16132             this.fireEvent("preparedata", this, data, i, records[i]);
16133             
16134             var d = Roo.apply({}, data);
16135             
16136             if(this.tickable){
16137                 Roo.apply(d, {'roo-id' : Roo.id()});
16138                 
16139                 var _this = this;
16140             
16141                 Roo.each(this.parent.item, function(item){
16142                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16143                         return;
16144                     }
16145                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16146                 });
16147             }
16148             
16149             html[html.length] = Roo.util.Format.trim(
16150                 this.dataName ?
16151                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16152                     t.apply(d)
16153             );
16154         }
16155         
16156         
16157         
16158         el.update(html.join(""));
16159         this.nodes = el.dom.childNodes;
16160         this.updateIndexes(0);
16161     },
16162     
16163
16164     /**
16165      * Function to override to reformat the data that is sent to
16166      * the template for each node.
16167      * DEPRICATED - use the preparedata event handler.
16168      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16169      * a JSON object for an UpdateManager bound view).
16170      */
16171     prepareData : function(data, index, record)
16172     {
16173         this.fireEvent("preparedata", this, data, index, record);
16174         return data;
16175     },
16176
16177     onUpdate : function(ds, record){
16178         // Roo.log('on update');   
16179         this.clearSelections();
16180         var index = this.store.indexOf(record);
16181         var n = this.nodes[index];
16182         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16183         n.parentNode.removeChild(n);
16184         this.updateIndexes(index, index);
16185     },
16186
16187     
16188     
16189 // --------- FIXME     
16190     onAdd : function(ds, records, index)
16191     {
16192         //Roo.log(['on Add', ds, records, index] );        
16193         this.clearSelections();
16194         if(this.nodes.length == 0){
16195             this.refresh();
16196             return;
16197         }
16198         var n = this.nodes[index];
16199         for(var i = 0, len = records.length; i < len; i++){
16200             var d = this.prepareData(records[i].data, i, records[i]);
16201             if(n){
16202                 this.tpl.insertBefore(n, d);
16203             }else{
16204                 
16205                 this.tpl.append(this.el, d);
16206             }
16207         }
16208         this.updateIndexes(index);
16209     },
16210
16211     onRemove : function(ds, record, index){
16212        // Roo.log('onRemove');
16213         this.clearSelections();
16214         var el = this.dataName  ?
16215             this.el.child('.roo-tpl-' + this.dataName) :
16216             this.el; 
16217         
16218         el.dom.removeChild(this.nodes[index]);
16219         this.updateIndexes(index);
16220     },
16221
16222     /**
16223      * Refresh an individual node.
16224      * @param {Number} index
16225      */
16226     refreshNode : function(index){
16227         this.onUpdate(this.store, this.store.getAt(index));
16228     },
16229
16230     updateIndexes : function(startIndex, endIndex){
16231         var ns = this.nodes;
16232         startIndex = startIndex || 0;
16233         endIndex = endIndex || ns.length - 1;
16234         for(var i = startIndex; i <= endIndex; i++){
16235             ns[i].nodeIndex = i;
16236         }
16237     },
16238
16239     /**
16240      * Changes the data store this view uses and refresh the view.
16241      * @param {Store} store
16242      */
16243     setStore : function(store, initial){
16244         if(!initial && this.store){
16245             this.store.un("datachanged", this.refresh);
16246             this.store.un("add", this.onAdd);
16247             this.store.un("remove", this.onRemove);
16248             this.store.un("update", this.onUpdate);
16249             this.store.un("clear", this.refresh);
16250             this.store.un("beforeload", this.onBeforeLoad);
16251             this.store.un("load", this.onLoad);
16252             this.store.un("loadexception", this.onLoad);
16253         }
16254         if(store){
16255           
16256             store.on("datachanged", this.refresh, this);
16257             store.on("add", this.onAdd, this);
16258             store.on("remove", this.onRemove, this);
16259             store.on("update", this.onUpdate, this);
16260             store.on("clear", this.refresh, this);
16261             store.on("beforeload", this.onBeforeLoad, this);
16262             store.on("load", this.onLoad, this);
16263             store.on("loadexception", this.onLoad, this);
16264         }
16265         
16266         if(store){
16267             this.refresh();
16268         }
16269     },
16270     /**
16271      * onbeforeLoad - masks the loading area.
16272      *
16273      */
16274     onBeforeLoad : function(store,opts)
16275     {
16276          //Roo.log('onBeforeLoad');   
16277         if (!opts.add) {
16278             this.el.update("");
16279         }
16280         this.el.mask(this.mask ? this.mask : "Loading" ); 
16281     },
16282     onLoad : function ()
16283     {
16284         this.el.unmask();
16285     },
16286     
16287
16288     /**
16289      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16290      * @param {HTMLElement} node
16291      * @return {HTMLElement} The template node
16292      */
16293     findItemFromChild : function(node){
16294         var el = this.dataName  ?
16295             this.el.child('.roo-tpl-' + this.dataName,true) :
16296             this.el.dom; 
16297         
16298         if(!node || node.parentNode == el){
16299                     return node;
16300             }
16301             var p = node.parentNode;
16302             while(p && p != el){
16303             if(p.parentNode == el){
16304                 return p;
16305             }
16306             p = p.parentNode;
16307         }
16308             return null;
16309     },
16310
16311     /** @ignore */
16312     onClick : function(e){
16313         var item = this.findItemFromChild(e.getTarget());
16314         if(item){
16315             var index = this.indexOf(item);
16316             if(this.onItemClick(item, index, e) !== false){
16317                 this.fireEvent("click", this, index, item, e);
16318             }
16319         }else{
16320             this.clearSelections();
16321         }
16322     },
16323
16324     /** @ignore */
16325     onContextMenu : function(e){
16326         var item = this.findItemFromChild(e.getTarget());
16327         if(item){
16328             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16329         }
16330     },
16331
16332     /** @ignore */
16333     onDblClick : function(e){
16334         var item = this.findItemFromChild(e.getTarget());
16335         if(item){
16336             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16337         }
16338     },
16339
16340     onItemClick : function(item, index, e)
16341     {
16342         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16343             return false;
16344         }
16345         if (this.toggleSelect) {
16346             var m = this.isSelected(item) ? 'unselect' : 'select';
16347             //Roo.log(m);
16348             var _t = this;
16349             _t[m](item, true, false);
16350             return true;
16351         }
16352         if(this.multiSelect || this.singleSelect){
16353             if(this.multiSelect && e.shiftKey && this.lastSelection){
16354                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16355             }else{
16356                 this.select(item, this.multiSelect && e.ctrlKey);
16357                 this.lastSelection = item;
16358             }
16359             
16360             if(!this.tickable){
16361                 e.preventDefault();
16362             }
16363             
16364         }
16365         return true;
16366     },
16367
16368     /**
16369      * Get the number of selected nodes.
16370      * @return {Number}
16371      */
16372     getSelectionCount : function(){
16373         return this.selections.length;
16374     },
16375
16376     /**
16377      * Get the currently selected nodes.
16378      * @return {Array} An array of HTMLElements
16379      */
16380     getSelectedNodes : function(){
16381         return this.selections;
16382     },
16383
16384     /**
16385      * Get the indexes of the selected nodes.
16386      * @return {Array}
16387      */
16388     getSelectedIndexes : function(){
16389         var indexes = [], s = this.selections;
16390         for(var i = 0, len = s.length; i < len; i++){
16391             indexes.push(s[i].nodeIndex);
16392         }
16393         return indexes;
16394     },
16395
16396     /**
16397      * Clear all selections
16398      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16399      */
16400     clearSelections : function(suppressEvent){
16401         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16402             this.cmp.elements = this.selections;
16403             this.cmp.removeClass(this.selectedClass);
16404             this.selections = [];
16405             if(!suppressEvent){
16406                 this.fireEvent("selectionchange", this, this.selections);
16407             }
16408         }
16409     },
16410
16411     /**
16412      * Returns true if the passed node is selected
16413      * @param {HTMLElement/Number} node The node or node index
16414      * @return {Boolean}
16415      */
16416     isSelected : function(node){
16417         var s = this.selections;
16418         if(s.length < 1){
16419             return false;
16420         }
16421         node = this.getNode(node);
16422         return s.indexOf(node) !== -1;
16423     },
16424
16425     /**
16426      * Selects nodes.
16427      * @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
16428      * @param {Boolean} keepExisting (optional) true to keep existing selections
16429      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16430      */
16431     select : function(nodeInfo, keepExisting, suppressEvent){
16432         if(nodeInfo instanceof Array){
16433             if(!keepExisting){
16434                 this.clearSelections(true);
16435             }
16436             for(var i = 0, len = nodeInfo.length; i < len; i++){
16437                 this.select(nodeInfo[i], true, true);
16438             }
16439             return;
16440         } 
16441         var node = this.getNode(nodeInfo);
16442         if(!node || this.isSelected(node)){
16443             return; // already selected.
16444         }
16445         if(!keepExisting){
16446             this.clearSelections(true);
16447         }
16448         
16449         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16450             Roo.fly(node).addClass(this.selectedClass);
16451             this.selections.push(node);
16452             if(!suppressEvent){
16453                 this.fireEvent("selectionchange", this, this.selections);
16454             }
16455         }
16456         
16457         
16458     },
16459       /**
16460      * Unselects nodes.
16461      * @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
16462      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16463      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16464      */
16465     unselect : function(nodeInfo, keepExisting, suppressEvent)
16466     {
16467         if(nodeInfo instanceof Array){
16468             Roo.each(this.selections, function(s) {
16469                 this.unselect(s, nodeInfo);
16470             }, this);
16471             return;
16472         }
16473         var node = this.getNode(nodeInfo);
16474         if(!node || !this.isSelected(node)){
16475             //Roo.log("not selected");
16476             return; // not selected.
16477         }
16478         // fireevent???
16479         var ns = [];
16480         Roo.each(this.selections, function(s) {
16481             if (s == node ) {
16482                 Roo.fly(node).removeClass(this.selectedClass);
16483
16484                 return;
16485             }
16486             ns.push(s);
16487         },this);
16488         
16489         this.selections= ns;
16490         this.fireEvent("selectionchange", this, this.selections);
16491     },
16492
16493     /**
16494      * Gets a template node.
16495      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16496      * @return {HTMLElement} The node or null if it wasn't found
16497      */
16498     getNode : function(nodeInfo){
16499         if(typeof nodeInfo == "string"){
16500             return document.getElementById(nodeInfo);
16501         }else if(typeof nodeInfo == "number"){
16502             return this.nodes[nodeInfo];
16503         }
16504         return nodeInfo;
16505     },
16506
16507     /**
16508      * Gets a range template nodes.
16509      * @param {Number} startIndex
16510      * @param {Number} endIndex
16511      * @return {Array} An array of nodes
16512      */
16513     getNodes : function(start, end){
16514         var ns = this.nodes;
16515         start = start || 0;
16516         end = typeof end == "undefined" ? ns.length - 1 : end;
16517         var nodes = [];
16518         if(start <= end){
16519             for(var i = start; i <= end; i++){
16520                 nodes.push(ns[i]);
16521             }
16522         } else{
16523             for(var i = start; i >= end; i--){
16524                 nodes.push(ns[i]);
16525             }
16526         }
16527         return nodes;
16528     },
16529
16530     /**
16531      * Finds the index of the passed node
16532      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16533      * @return {Number} The index of the node or -1
16534      */
16535     indexOf : function(node){
16536         node = this.getNode(node);
16537         if(typeof node.nodeIndex == "number"){
16538             return node.nodeIndex;
16539         }
16540         var ns = this.nodes;
16541         for(var i = 0, len = ns.length; i < len; i++){
16542             if(ns[i] == node){
16543                 return i;
16544             }
16545         }
16546         return -1;
16547     }
16548 });
16549 /*
16550  * - LGPL
16551  *
16552  * based on jquery fullcalendar
16553  * 
16554  */
16555
16556 Roo.bootstrap = Roo.bootstrap || {};
16557 /**
16558  * @class Roo.bootstrap.Calendar
16559  * @extends Roo.bootstrap.Component
16560  * Bootstrap Calendar class
16561  * @cfg {Boolean} loadMask (true|false) default false
16562  * @cfg {Object} header generate the user specific header of the calendar, default false
16563
16564  * @constructor
16565  * Create a new Container
16566  * @param {Object} config The config object
16567  */
16568
16569
16570
16571 Roo.bootstrap.Calendar = function(config){
16572     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16573      this.addEvents({
16574         /**
16575              * @event select
16576              * Fires when a date is selected
16577              * @param {DatePicker} this
16578              * @param {Date} date The selected date
16579              */
16580         'select': true,
16581         /**
16582              * @event monthchange
16583              * Fires when the displayed month changes 
16584              * @param {DatePicker} this
16585              * @param {Date} date The selected month
16586              */
16587         'monthchange': true,
16588         /**
16589              * @event evententer
16590              * Fires when mouse over an event
16591              * @param {Calendar} this
16592              * @param {event} Event
16593              */
16594         'evententer': true,
16595         /**
16596              * @event eventleave
16597              * Fires when the mouse leaves an
16598              * @param {Calendar} this
16599              * @param {event}
16600              */
16601         'eventleave': true,
16602         /**
16603              * @event eventclick
16604              * Fires when the mouse click an
16605              * @param {Calendar} this
16606              * @param {event}
16607              */
16608         'eventclick': true
16609         
16610     });
16611
16612 };
16613
16614 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16615     
16616      /**
16617      * @cfg {Number} startDay
16618      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16619      */
16620     startDay : 0,
16621     
16622     loadMask : false,
16623     
16624     header : false,
16625       
16626     getAutoCreate : function(){
16627         
16628         
16629         var fc_button = function(name, corner, style, content ) {
16630             return Roo.apply({},{
16631                 tag : 'span',
16632                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16633                          (corner.length ?
16634                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16635                             ''
16636                         ),
16637                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16638                 unselectable: 'on'
16639             });
16640         };
16641         
16642         var header = {};
16643         
16644         if(!this.header){
16645             header = {
16646                 tag : 'table',
16647                 cls : 'fc-header',
16648                 style : 'width:100%',
16649                 cn : [
16650                     {
16651                         tag: 'tr',
16652                         cn : [
16653                             {
16654                                 tag : 'td',
16655                                 cls : 'fc-header-left',
16656                                 cn : [
16657                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16658                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16659                                     { tag: 'span', cls: 'fc-header-space' },
16660                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16661
16662
16663                                 ]
16664                             },
16665
16666                             {
16667                                 tag : 'td',
16668                                 cls : 'fc-header-center',
16669                                 cn : [
16670                                     {
16671                                         tag: 'span',
16672                                         cls: 'fc-header-title',
16673                                         cn : {
16674                                             tag: 'H2',
16675                                             html : 'month / year'
16676                                         }
16677                                     }
16678
16679                                 ]
16680                             },
16681                             {
16682                                 tag : 'td',
16683                                 cls : 'fc-header-right',
16684                                 cn : [
16685                               /*      fc_button('month', 'left', '', 'month' ),
16686                                     fc_button('week', '', '', 'week' ),
16687                                     fc_button('day', 'right', '', 'day' )
16688                                 */    
16689
16690                                 ]
16691                             }
16692
16693                         ]
16694                     }
16695                 ]
16696             };
16697         }
16698         
16699         header = this.header;
16700         
16701        
16702         var cal_heads = function() {
16703             var ret = [];
16704             // fixme - handle this.
16705             
16706             for (var i =0; i < Date.dayNames.length; i++) {
16707                 var d = Date.dayNames[i];
16708                 ret.push({
16709                     tag: 'th',
16710                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16711                     html : d.substring(0,3)
16712                 });
16713                 
16714             }
16715             ret[0].cls += ' fc-first';
16716             ret[6].cls += ' fc-last';
16717             return ret;
16718         };
16719         var cal_cell = function(n) {
16720             return  {
16721                 tag: 'td',
16722                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16723                 cn : [
16724                     {
16725                         cn : [
16726                             {
16727                                 cls: 'fc-day-number',
16728                                 html: 'D'
16729                             },
16730                             {
16731                                 cls: 'fc-day-content',
16732                              
16733                                 cn : [
16734                                      {
16735                                         style: 'position: relative;' // height: 17px;
16736                                     }
16737                                 ]
16738                             }
16739                             
16740                             
16741                         ]
16742                     }
16743                 ]
16744                 
16745             }
16746         };
16747         var cal_rows = function() {
16748             
16749             var ret = [];
16750             for (var r = 0; r < 6; r++) {
16751                 var row= {
16752                     tag : 'tr',
16753                     cls : 'fc-week',
16754                     cn : []
16755                 };
16756                 
16757                 for (var i =0; i < Date.dayNames.length; i++) {
16758                     var d = Date.dayNames[i];
16759                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16760
16761                 }
16762                 row.cn[0].cls+=' fc-first';
16763                 row.cn[0].cn[0].style = 'min-height:90px';
16764                 row.cn[6].cls+=' fc-last';
16765                 ret.push(row);
16766                 
16767             }
16768             ret[0].cls += ' fc-first';
16769             ret[4].cls += ' fc-prev-last';
16770             ret[5].cls += ' fc-last';
16771             return ret;
16772             
16773         };
16774         
16775         var cal_table = {
16776             tag: 'table',
16777             cls: 'fc-border-separate',
16778             style : 'width:100%',
16779             cellspacing  : 0,
16780             cn : [
16781                 { 
16782                     tag: 'thead',
16783                     cn : [
16784                         { 
16785                             tag: 'tr',
16786                             cls : 'fc-first fc-last',
16787                             cn : cal_heads()
16788                         }
16789                     ]
16790                 },
16791                 { 
16792                     tag: 'tbody',
16793                     cn : cal_rows()
16794                 }
16795                   
16796             ]
16797         };
16798          
16799          var cfg = {
16800             cls : 'fc fc-ltr',
16801             cn : [
16802                 header,
16803                 {
16804                     cls : 'fc-content',
16805                     style : "position: relative;",
16806                     cn : [
16807                         {
16808                             cls : 'fc-view fc-view-month fc-grid',
16809                             style : 'position: relative',
16810                             unselectable : 'on',
16811                             cn : [
16812                                 {
16813                                     cls : 'fc-event-container',
16814                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16815                                 },
16816                                 cal_table
16817                             ]
16818                         }
16819                     ]
16820     
16821                 }
16822            ] 
16823             
16824         };
16825         
16826          
16827         
16828         return cfg;
16829     },
16830     
16831     
16832     initEvents : function()
16833     {
16834         if(!this.store){
16835             throw "can not find store for calendar";
16836         }
16837         
16838         var mark = {
16839             tag: "div",
16840             cls:"x-dlg-mask",
16841             style: "text-align:center",
16842             cn: [
16843                 {
16844                     tag: "div",
16845                     style: "background-color:white;width:50%;margin:250 auto",
16846                     cn: [
16847                         {
16848                             tag: "img",
16849                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16850                         },
16851                         {
16852                             tag: "span",
16853                             html: "Loading"
16854                         }
16855                         
16856                     ]
16857                 }
16858             ]
16859         };
16860         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16861         
16862         var size = this.el.select('.fc-content', true).first().getSize();
16863         this.maskEl.setSize(size.width, size.height);
16864         this.maskEl.enableDisplayMode("block");
16865         if(!this.loadMask){
16866             this.maskEl.hide();
16867         }
16868         
16869         this.store = Roo.factory(this.store, Roo.data);
16870         this.store.on('load', this.onLoad, this);
16871         this.store.on('beforeload', this.onBeforeLoad, this);
16872         
16873         this.resize();
16874         
16875         this.cells = this.el.select('.fc-day',true);
16876         //Roo.log(this.cells);
16877         this.textNodes = this.el.query('.fc-day-number');
16878         this.cells.addClassOnOver('fc-state-hover');
16879         
16880         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16881         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16882         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16883         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16884         
16885         this.on('monthchange', this.onMonthChange, this);
16886         
16887         this.update(new Date().clearTime());
16888     },
16889     
16890     resize : function() {
16891         var sz  = this.el.getSize();
16892         
16893         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16894         this.el.select('.fc-day-content div',true).setHeight(34);
16895     },
16896     
16897     
16898     // private
16899     showPrevMonth : function(e){
16900         this.update(this.activeDate.add("mo", -1));
16901     },
16902     showToday : function(e){
16903         this.update(new Date().clearTime());
16904     },
16905     // private
16906     showNextMonth : function(e){
16907         this.update(this.activeDate.add("mo", 1));
16908     },
16909
16910     // private
16911     showPrevYear : function(){
16912         this.update(this.activeDate.add("y", -1));
16913     },
16914
16915     // private
16916     showNextYear : function(){
16917         this.update(this.activeDate.add("y", 1));
16918     },
16919
16920     
16921    // private
16922     update : function(date)
16923     {
16924         var vd = this.activeDate;
16925         this.activeDate = date;
16926 //        if(vd && this.el){
16927 //            var t = date.getTime();
16928 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16929 //                Roo.log('using add remove');
16930 //                
16931 //                this.fireEvent('monthchange', this, date);
16932 //                
16933 //                this.cells.removeClass("fc-state-highlight");
16934 //                this.cells.each(function(c){
16935 //                   if(c.dateValue == t){
16936 //                       c.addClass("fc-state-highlight");
16937 //                       setTimeout(function(){
16938 //                            try{c.dom.firstChild.focus();}catch(e){}
16939 //                       }, 50);
16940 //                       return false;
16941 //                   }
16942 //                   return true;
16943 //                });
16944 //                return;
16945 //            }
16946 //        }
16947         
16948         var days = date.getDaysInMonth();
16949         
16950         var firstOfMonth = date.getFirstDateOfMonth();
16951         var startingPos = firstOfMonth.getDay()-this.startDay;
16952         
16953         if(startingPos < this.startDay){
16954             startingPos += 7;
16955         }
16956         
16957         var pm = date.add(Date.MONTH, -1);
16958         var prevStart = pm.getDaysInMonth()-startingPos;
16959 //        
16960         this.cells = this.el.select('.fc-day',true);
16961         this.textNodes = this.el.query('.fc-day-number');
16962         this.cells.addClassOnOver('fc-state-hover');
16963         
16964         var cells = this.cells.elements;
16965         var textEls = this.textNodes;
16966         
16967         Roo.each(cells, function(cell){
16968             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16969         });
16970         
16971         days += startingPos;
16972
16973         // convert everything to numbers so it's fast
16974         var day = 86400000;
16975         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16976         //Roo.log(d);
16977         //Roo.log(pm);
16978         //Roo.log(prevStart);
16979         
16980         var today = new Date().clearTime().getTime();
16981         var sel = date.clearTime().getTime();
16982         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16983         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16984         var ddMatch = this.disabledDatesRE;
16985         var ddText = this.disabledDatesText;
16986         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16987         var ddaysText = this.disabledDaysText;
16988         var format = this.format;
16989         
16990         var setCellClass = function(cal, cell){
16991             cell.row = 0;
16992             cell.events = [];
16993             cell.more = [];
16994             //Roo.log('set Cell Class');
16995             cell.title = "";
16996             var t = d.getTime();
16997             
16998             //Roo.log(d);
16999             
17000             cell.dateValue = t;
17001             if(t == today){
17002                 cell.className += " fc-today";
17003                 cell.className += " fc-state-highlight";
17004                 cell.title = cal.todayText;
17005             }
17006             if(t == sel){
17007                 // disable highlight in other month..
17008                 //cell.className += " fc-state-highlight";
17009                 
17010             }
17011             // disabling
17012             if(t < min) {
17013                 cell.className = " fc-state-disabled";
17014                 cell.title = cal.minText;
17015                 return;
17016             }
17017             if(t > max) {
17018                 cell.className = " fc-state-disabled";
17019                 cell.title = cal.maxText;
17020                 return;
17021             }
17022             if(ddays){
17023                 if(ddays.indexOf(d.getDay()) != -1){
17024                     cell.title = ddaysText;
17025                     cell.className = " fc-state-disabled";
17026                 }
17027             }
17028             if(ddMatch && format){
17029                 var fvalue = d.dateFormat(format);
17030                 if(ddMatch.test(fvalue)){
17031                     cell.title = ddText.replace("%0", fvalue);
17032                     cell.className = " fc-state-disabled";
17033                 }
17034             }
17035             
17036             if (!cell.initialClassName) {
17037                 cell.initialClassName = cell.dom.className;
17038             }
17039             
17040             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17041         };
17042
17043         var i = 0;
17044         
17045         for(; i < startingPos; i++) {
17046             textEls[i].innerHTML = (++prevStart);
17047             d.setDate(d.getDate()+1);
17048             
17049             cells[i].className = "fc-past fc-other-month";
17050             setCellClass(this, cells[i]);
17051         }
17052         
17053         var intDay = 0;
17054         
17055         for(; i < days; i++){
17056             intDay = i - startingPos + 1;
17057             textEls[i].innerHTML = (intDay);
17058             d.setDate(d.getDate()+1);
17059             
17060             cells[i].className = ''; // "x-date-active";
17061             setCellClass(this, cells[i]);
17062         }
17063         var extraDays = 0;
17064         
17065         for(; i < 42; i++) {
17066             textEls[i].innerHTML = (++extraDays);
17067             d.setDate(d.getDate()+1);
17068             
17069             cells[i].className = "fc-future fc-other-month";
17070             setCellClass(this, cells[i]);
17071         }
17072         
17073         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17074         
17075         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17076         
17077         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17078         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17079         
17080         if(totalRows != 6){
17081             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17082             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17083         }
17084         
17085         this.fireEvent('monthchange', this, date);
17086         
17087         
17088         /*
17089         if(!this.internalRender){
17090             var main = this.el.dom.firstChild;
17091             var w = main.offsetWidth;
17092             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17093             Roo.fly(main).setWidth(w);
17094             this.internalRender = true;
17095             // opera does not respect the auto grow header center column
17096             // then, after it gets a width opera refuses to recalculate
17097             // without a second pass
17098             if(Roo.isOpera && !this.secondPass){
17099                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17100                 this.secondPass = true;
17101                 this.update.defer(10, this, [date]);
17102             }
17103         }
17104         */
17105         
17106     },
17107     
17108     findCell : function(dt) {
17109         dt = dt.clearTime().getTime();
17110         var ret = false;
17111         this.cells.each(function(c){
17112             //Roo.log("check " +c.dateValue + '?=' + dt);
17113             if(c.dateValue == dt){
17114                 ret = c;
17115                 return false;
17116             }
17117             return true;
17118         });
17119         
17120         return ret;
17121     },
17122     
17123     findCells : function(ev) {
17124         var s = ev.start.clone().clearTime().getTime();
17125        // Roo.log(s);
17126         var e= ev.end.clone().clearTime().getTime();
17127        // Roo.log(e);
17128         var ret = [];
17129         this.cells.each(function(c){
17130              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17131             
17132             if(c.dateValue > e){
17133                 return ;
17134             }
17135             if(c.dateValue < s){
17136                 return ;
17137             }
17138             ret.push(c);
17139         });
17140         
17141         return ret;    
17142     },
17143     
17144 //    findBestRow: function(cells)
17145 //    {
17146 //        var ret = 0;
17147 //        
17148 //        for (var i =0 ; i < cells.length;i++) {
17149 //            ret  = Math.max(cells[i].rows || 0,ret);
17150 //        }
17151 //        return ret;
17152 //        
17153 //    },
17154     
17155     
17156     addItem : function(ev)
17157     {
17158         // look for vertical location slot in
17159         var cells = this.findCells(ev);
17160         
17161 //        ev.row = this.findBestRow(cells);
17162         
17163         // work out the location.
17164         
17165         var crow = false;
17166         var rows = [];
17167         for(var i =0; i < cells.length; i++) {
17168             
17169             cells[i].row = cells[0].row;
17170             
17171             if(i == 0){
17172                 cells[i].row = cells[i].row + 1;
17173             }
17174             
17175             if (!crow) {
17176                 crow = {
17177                     start : cells[i],
17178                     end :  cells[i]
17179                 };
17180                 continue;
17181             }
17182             if (crow.start.getY() == cells[i].getY()) {
17183                 // on same row.
17184                 crow.end = cells[i];
17185                 continue;
17186             }
17187             // different row.
17188             rows.push(crow);
17189             crow = {
17190                 start: cells[i],
17191                 end : cells[i]
17192             };
17193             
17194         }
17195         
17196         rows.push(crow);
17197         ev.els = [];
17198         ev.rows = rows;
17199         ev.cells = cells;
17200         
17201         cells[0].events.push(ev);
17202         
17203         this.calevents.push(ev);
17204     },
17205     
17206     clearEvents: function() {
17207         
17208         if(!this.calevents){
17209             return;
17210         }
17211         
17212         Roo.each(this.cells.elements, function(c){
17213             c.row = 0;
17214             c.events = [];
17215             c.more = [];
17216         });
17217         
17218         Roo.each(this.calevents, function(e) {
17219             Roo.each(e.els, function(el) {
17220                 el.un('mouseenter' ,this.onEventEnter, this);
17221                 el.un('mouseleave' ,this.onEventLeave, this);
17222                 el.remove();
17223             },this);
17224         },this);
17225         
17226         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17227             e.remove();
17228         });
17229         
17230     },
17231     
17232     renderEvents: function()
17233     {   
17234         var _this = this;
17235         
17236         this.cells.each(function(c) {
17237             
17238             if(c.row < 5){
17239                 return;
17240             }
17241             
17242             var ev = c.events;
17243             
17244             var r = 4;
17245             if(c.row != c.events.length){
17246                 r = 4 - (4 - (c.row - c.events.length));
17247             }
17248             
17249             c.events = ev.slice(0, r);
17250             c.more = ev.slice(r);
17251             
17252             if(c.more.length && c.more.length == 1){
17253                 c.events.push(c.more.pop());
17254             }
17255             
17256             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17257             
17258         });
17259             
17260         this.cells.each(function(c) {
17261             
17262             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17263             
17264             
17265             for (var e = 0; e < c.events.length; e++){
17266                 var ev = c.events[e];
17267                 var rows = ev.rows;
17268                 
17269                 for(var i = 0; i < rows.length; i++) {
17270                 
17271                     // how many rows should it span..
17272
17273                     var  cfg = {
17274                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17275                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17276
17277                         unselectable : "on",
17278                         cn : [
17279                             {
17280                                 cls: 'fc-event-inner',
17281                                 cn : [
17282     //                                {
17283     //                                  tag:'span',
17284     //                                  cls: 'fc-event-time',
17285     //                                  html : cells.length > 1 ? '' : ev.time
17286     //                                },
17287                                     {
17288                                       tag:'span',
17289                                       cls: 'fc-event-title',
17290                                       html : String.format('{0}', ev.title)
17291                                     }
17292
17293
17294                                 ]
17295                             },
17296                             {
17297                                 cls: 'ui-resizable-handle ui-resizable-e',
17298                                 html : '&nbsp;&nbsp;&nbsp'
17299                             }
17300
17301                         ]
17302                     };
17303
17304                     if (i == 0) {
17305                         cfg.cls += ' fc-event-start';
17306                     }
17307                     if ((i+1) == rows.length) {
17308                         cfg.cls += ' fc-event-end';
17309                     }
17310
17311                     var ctr = _this.el.select('.fc-event-container',true).first();
17312                     var cg = ctr.createChild(cfg);
17313
17314                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17315                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17316
17317                     var r = (c.more.length) ? 1 : 0;
17318                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17319                     cg.setWidth(ebox.right - sbox.x -2);
17320
17321                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17322                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17323                     cg.on('click', _this.onEventClick, _this, ev);
17324
17325                     ev.els.push(cg);
17326                     
17327                 }
17328                 
17329             }
17330             
17331             
17332             if(c.more.length){
17333                 var  cfg = {
17334                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17335                     style : 'position: absolute',
17336                     unselectable : "on",
17337                     cn : [
17338                         {
17339                             cls: 'fc-event-inner',
17340                             cn : [
17341                                 {
17342                                   tag:'span',
17343                                   cls: 'fc-event-title',
17344                                   html : 'More'
17345                                 }
17346
17347
17348                             ]
17349                         },
17350                         {
17351                             cls: 'ui-resizable-handle ui-resizable-e',
17352                             html : '&nbsp;&nbsp;&nbsp'
17353                         }
17354
17355                     ]
17356                 };
17357
17358                 var ctr = _this.el.select('.fc-event-container',true).first();
17359                 var cg = ctr.createChild(cfg);
17360
17361                 var sbox = c.select('.fc-day-content',true).first().getBox();
17362                 var ebox = c.select('.fc-day-content',true).first().getBox();
17363                 //Roo.log(cg);
17364                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17365                 cg.setWidth(ebox.right - sbox.x -2);
17366
17367                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17368                 
17369             }
17370             
17371         });
17372         
17373         
17374         
17375     },
17376     
17377     onEventEnter: function (e, el,event,d) {
17378         this.fireEvent('evententer', this, el, event);
17379     },
17380     
17381     onEventLeave: function (e, el,event,d) {
17382         this.fireEvent('eventleave', this, el, event);
17383     },
17384     
17385     onEventClick: function (e, el,event,d) {
17386         this.fireEvent('eventclick', this, el, event);
17387     },
17388     
17389     onMonthChange: function () {
17390         this.store.load();
17391     },
17392     
17393     onMoreEventClick: function(e, el, more)
17394     {
17395         var _this = this;
17396         
17397         this.calpopover.placement = 'right';
17398         this.calpopover.setTitle('More');
17399         
17400         this.calpopover.setContent('');
17401         
17402         var ctr = this.calpopover.el.select('.popover-content', true).first();
17403         
17404         Roo.each(more, function(m){
17405             var cfg = {
17406                 cls : 'fc-event-hori fc-event-draggable',
17407                 html : m.title
17408             };
17409             var cg = ctr.createChild(cfg);
17410             
17411             cg.on('click', _this.onEventClick, _this, m);
17412         });
17413         
17414         this.calpopover.show(el);
17415         
17416         
17417     },
17418     
17419     onLoad: function () 
17420     {   
17421         this.calevents = [];
17422         var cal = this;
17423         
17424         if(this.store.getCount() > 0){
17425             this.store.data.each(function(d){
17426                cal.addItem({
17427                     id : d.data.id,
17428                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17429                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17430                     time : d.data.start_time,
17431                     title : d.data.title,
17432                     description : d.data.description,
17433                     venue : d.data.venue
17434                 });
17435             });
17436         }
17437         
17438         this.renderEvents();
17439         
17440         if(this.calevents.length && this.loadMask){
17441             this.maskEl.hide();
17442         }
17443     },
17444     
17445     onBeforeLoad: function()
17446     {
17447         this.clearEvents();
17448         if(this.loadMask){
17449             this.maskEl.show();
17450         }
17451     }
17452 });
17453
17454  
17455  /*
17456  * - LGPL
17457  *
17458  * element
17459  * 
17460  */
17461
17462 /**
17463  * @class Roo.bootstrap.Popover
17464  * @extends Roo.bootstrap.Component
17465  * Bootstrap Popover class
17466  * @cfg {String} html contents of the popover   (or false to use children..)
17467  * @cfg {String} title of popover (or false to hide)
17468  * @cfg {String} placement how it is placed
17469  * @cfg {String} trigger click || hover (or false to trigger manually)
17470  * @cfg {String} over what (parent or false to trigger manually.)
17471  * @cfg {Number} delay - delay before showing
17472  
17473  * @constructor
17474  * Create a new Popover
17475  * @param {Object} config The config object
17476  */
17477
17478 Roo.bootstrap.Popover = function(config){
17479     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17480     
17481     this.addEvents({
17482         // raw events
17483          /**
17484          * @event show
17485          * After the popover show
17486          * 
17487          * @param {Roo.bootstrap.Popover} this
17488          */
17489         "show" : true,
17490         /**
17491          * @event hide
17492          * After the popover hide
17493          * 
17494          * @param {Roo.bootstrap.Popover} this
17495          */
17496         "hide" : true
17497     });
17498 };
17499
17500 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17501     
17502     title: 'Fill in a title',
17503     html: false,
17504     
17505     placement : 'right',
17506     trigger : 'hover', // hover
17507     
17508     delay : 0,
17509     
17510     over: 'parent',
17511     
17512     can_build_overlaid : false,
17513     
17514     getChildContainer : function()
17515     {
17516         return this.el.select('.popover-content',true).first();
17517     },
17518     
17519     getAutoCreate : function(){
17520          
17521         var cfg = {
17522            cls : 'popover roo-dynamic',
17523            style: 'display:block',
17524            cn : [
17525                 {
17526                     cls : 'arrow'
17527                 },
17528                 {
17529                     cls : 'popover-inner',
17530                     cn : [
17531                         {
17532                             tag: 'h3',
17533                             cls: 'popover-title',
17534                             html : this.title
17535                         },
17536                         {
17537                             cls : 'popover-content',
17538                             html : this.html
17539                         }
17540                     ]
17541                     
17542                 }
17543            ]
17544         };
17545         
17546         return cfg;
17547     },
17548     setTitle: function(str)
17549     {
17550         this.title = str;
17551         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17552     },
17553     setContent: function(str)
17554     {
17555         this.html = str;
17556         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17557     },
17558     // as it get's added to the bottom of the page.
17559     onRender : function(ct, position)
17560     {
17561         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17562         if(!this.el){
17563             var cfg = Roo.apply({},  this.getAutoCreate());
17564             cfg.id = Roo.id();
17565             
17566             if (this.cls) {
17567                 cfg.cls += ' ' + this.cls;
17568             }
17569             if (this.style) {
17570                 cfg.style = this.style;
17571             }
17572             //Roo.log("adding to ");
17573             this.el = Roo.get(document.body).createChild(cfg, position);
17574 //            Roo.log(this.el);
17575         }
17576         this.initEvents();
17577     },
17578     
17579     initEvents : function()
17580     {
17581         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17582         this.el.enableDisplayMode('block');
17583         this.el.hide();
17584         if (this.over === false) {
17585             return; 
17586         }
17587         if (this.triggers === false) {
17588             return;
17589         }
17590         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17591         var triggers = this.trigger ? this.trigger.split(' ') : [];
17592         Roo.each(triggers, function(trigger) {
17593         
17594             if (trigger == 'click') {
17595                 on_el.on('click', this.toggle, this);
17596             } else if (trigger != 'manual') {
17597                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17598                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17599       
17600                 on_el.on(eventIn  ,this.enter, this);
17601                 on_el.on(eventOut, this.leave, this);
17602             }
17603         }, this);
17604         
17605     },
17606     
17607     
17608     // private
17609     timeout : null,
17610     hoverState : null,
17611     
17612     toggle : function () {
17613         this.hoverState == 'in' ? this.leave() : this.enter();
17614     },
17615     
17616     enter : function () {
17617         
17618         clearTimeout(this.timeout);
17619     
17620         this.hoverState = 'in';
17621     
17622         if (!this.delay || !this.delay.show) {
17623             this.show();
17624             return;
17625         }
17626         var _t = this;
17627         this.timeout = setTimeout(function () {
17628             if (_t.hoverState == 'in') {
17629                 _t.show();
17630             }
17631         }, this.delay.show)
17632     },
17633     
17634     leave : function() {
17635         clearTimeout(this.timeout);
17636     
17637         this.hoverState = 'out';
17638     
17639         if (!this.delay || !this.delay.hide) {
17640             this.hide();
17641             return;
17642         }
17643         var _t = this;
17644         this.timeout = setTimeout(function () {
17645             if (_t.hoverState == 'out') {
17646                 _t.hide();
17647             }
17648         }, this.delay.hide)
17649     },
17650     
17651     show : function (on_el)
17652     {
17653         if (!on_el) {
17654             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17655         }
17656         
17657         // set content.
17658         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17659         if (this.html !== false) {
17660             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17661         }
17662         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17663         if (!this.title.length) {
17664             this.el.select('.popover-title',true).hide();
17665         }
17666         
17667         var placement = typeof this.placement == 'function' ?
17668             this.placement.call(this, this.el, on_el) :
17669             this.placement;
17670             
17671         var autoToken = /\s?auto?\s?/i;
17672         var autoPlace = autoToken.test(placement);
17673         if (autoPlace) {
17674             placement = placement.replace(autoToken, '') || 'top';
17675         }
17676         
17677         //this.el.detach()
17678         //this.el.setXY([0,0]);
17679         this.el.show();
17680         this.el.dom.style.display='block';
17681         this.el.addClass(placement);
17682         
17683         //this.el.appendTo(on_el);
17684         
17685         var p = this.getPosition();
17686         var box = this.el.getBox();
17687         
17688         if (autoPlace) {
17689             // fixme..
17690         }
17691         var align = Roo.bootstrap.Popover.alignment[placement];
17692         
17693 //        Roo.log(align);
17694         this.el.alignTo(on_el, align[0],align[1]);
17695         //var arrow = this.el.select('.arrow',true).first();
17696         //arrow.set(align[2], 
17697         
17698         this.el.addClass('in');
17699         
17700         
17701         if (this.el.hasClass('fade')) {
17702             // fade it?
17703         }
17704         
17705         this.hoverState = 'in';
17706         
17707         this.fireEvent('show', this);
17708         
17709     },
17710     hide : function()
17711     {
17712         this.el.setXY([0,0]);
17713         this.el.removeClass('in');
17714         this.el.hide();
17715         this.hoverState = null;
17716         
17717         this.fireEvent('hide', this);
17718     }
17719     
17720 });
17721
17722 Roo.bootstrap.Popover.alignment = {
17723     'left' : ['r-l', [-10,0], 'right'],
17724     'right' : ['l-r', [10,0], 'left'],
17725     'bottom' : ['t-b', [0,10], 'top'],
17726     'top' : [ 'b-t', [0,-10], 'bottom']
17727 };
17728
17729  /*
17730  * - LGPL
17731  *
17732  * Progress
17733  * 
17734  */
17735
17736 /**
17737  * @class Roo.bootstrap.Progress
17738  * @extends Roo.bootstrap.Component
17739  * Bootstrap Progress class
17740  * @cfg {Boolean} striped striped of the progress bar
17741  * @cfg {Boolean} active animated of the progress bar
17742  * 
17743  * 
17744  * @constructor
17745  * Create a new Progress
17746  * @param {Object} config The config object
17747  */
17748
17749 Roo.bootstrap.Progress = function(config){
17750     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17751 };
17752
17753 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17754     
17755     striped : false,
17756     active: false,
17757     
17758     getAutoCreate : function(){
17759         var cfg = {
17760             tag: 'div',
17761             cls: 'progress'
17762         };
17763         
17764         
17765         if(this.striped){
17766             cfg.cls += ' progress-striped';
17767         }
17768       
17769         if(this.active){
17770             cfg.cls += ' active';
17771         }
17772         
17773         
17774         return cfg;
17775     }
17776    
17777 });
17778
17779  
17780
17781  /*
17782  * - LGPL
17783  *
17784  * ProgressBar
17785  * 
17786  */
17787
17788 /**
17789  * @class Roo.bootstrap.ProgressBar
17790  * @extends Roo.bootstrap.Component
17791  * Bootstrap ProgressBar class
17792  * @cfg {Number} aria_valuenow aria-value now
17793  * @cfg {Number} aria_valuemin aria-value min
17794  * @cfg {Number} aria_valuemax aria-value max
17795  * @cfg {String} label label for the progress bar
17796  * @cfg {String} panel (success | info | warning | danger )
17797  * @cfg {String} role role of the progress bar
17798  * @cfg {String} sr_only text
17799  * 
17800  * 
17801  * @constructor
17802  * Create a new ProgressBar
17803  * @param {Object} config The config object
17804  */
17805
17806 Roo.bootstrap.ProgressBar = function(config){
17807     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17808 };
17809
17810 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17811     
17812     aria_valuenow : 0,
17813     aria_valuemin : 0,
17814     aria_valuemax : 100,
17815     label : false,
17816     panel : false,
17817     role : false,
17818     sr_only: false,
17819     
17820     getAutoCreate : function()
17821     {
17822         
17823         var cfg = {
17824             tag: 'div',
17825             cls: 'progress-bar',
17826             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17827         };
17828         
17829         if(this.sr_only){
17830             cfg.cn = {
17831                 tag: 'span',
17832                 cls: 'sr-only',
17833                 html: this.sr_only
17834             }
17835         }
17836         
17837         if(this.role){
17838             cfg.role = this.role;
17839         }
17840         
17841         if(this.aria_valuenow){
17842             cfg['aria-valuenow'] = this.aria_valuenow;
17843         }
17844         
17845         if(this.aria_valuemin){
17846             cfg['aria-valuemin'] = this.aria_valuemin;
17847         }
17848         
17849         if(this.aria_valuemax){
17850             cfg['aria-valuemax'] = this.aria_valuemax;
17851         }
17852         
17853         if(this.label && !this.sr_only){
17854             cfg.html = this.label;
17855         }
17856         
17857         if(this.panel){
17858             cfg.cls += ' progress-bar-' + this.panel;
17859         }
17860         
17861         return cfg;
17862     },
17863     
17864     update : function(aria_valuenow)
17865     {
17866         this.aria_valuenow = aria_valuenow;
17867         
17868         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17869     }
17870    
17871 });
17872
17873  
17874
17875  /*
17876  * - LGPL
17877  *
17878  * column
17879  * 
17880  */
17881
17882 /**
17883  * @class Roo.bootstrap.TabGroup
17884  * @extends Roo.bootstrap.Column
17885  * Bootstrap Column class
17886  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17887  * @cfg {Boolean} carousel true to make the group behave like a carousel
17888  * @cfg {Boolean} bullets show bullets for the panels
17889  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17890  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17891  * @cfg {Boolean} showarrow (true|false) show arrow default true
17892  * 
17893  * @constructor
17894  * Create a new TabGroup
17895  * @param {Object} config The config object
17896  */
17897
17898 Roo.bootstrap.TabGroup = function(config){
17899     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17900     if (!this.navId) {
17901         this.navId = Roo.id();
17902     }
17903     this.tabs = [];
17904     Roo.bootstrap.TabGroup.register(this);
17905     
17906 };
17907
17908 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17909     
17910     carousel : false,
17911     transition : false,
17912     bullets : 0,
17913     timer : 0,
17914     autoslide : false,
17915     slideFn : false,
17916     slideOnTouch : false,
17917     showarrow : true,
17918     
17919     getAutoCreate : function()
17920     {
17921         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17922         
17923         cfg.cls += ' tab-content';
17924         
17925         if (this.carousel) {
17926             cfg.cls += ' carousel slide';
17927             
17928             cfg.cn = [{
17929                cls : 'carousel-inner',
17930                cn : []
17931             }];
17932         
17933             if(this.bullets  && !Roo.isTouch){
17934                 
17935                 var bullets = {
17936                     cls : 'carousel-bullets',
17937                     cn : []
17938                 };
17939                
17940                 if(this.bullets_cls){
17941                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17942                 }
17943                 
17944                 bullets.cn.push({
17945                     cls : 'clear'
17946                 });
17947                 
17948                 cfg.cn[0].cn.push(bullets);
17949             }
17950             
17951             if(this.showarrow){
17952                 cfg.cn[0].cn.push({
17953                     tag : 'div',
17954                     class : 'carousel-arrow',
17955                     cn : [
17956                         {
17957                             tag : 'div',
17958                             class : 'carousel-prev',
17959                             cn : [
17960                                 {
17961                                     tag : 'i',
17962                                     class : 'fa fa-chevron-left'
17963                                 }
17964                             ]
17965                         },
17966                         {
17967                             tag : 'div',
17968                             class : 'carousel-next',
17969                             cn : [
17970                                 {
17971                                     tag : 'i',
17972                                     class : 'fa fa-chevron-right'
17973                                 }
17974                             ]
17975                         }
17976                     ]
17977                 });
17978             }
17979             
17980         }
17981         
17982         return cfg;
17983     },
17984     
17985     initEvents:  function()
17986     {
17987 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17988 //            this.el.on("touchstart", this.onTouchStart, this);
17989 //        }
17990         
17991         if(this.autoslide){
17992             var _this = this;
17993             
17994             this.slideFn = window.setInterval(function() {
17995                 _this.showPanelNext();
17996             }, this.timer);
17997         }
17998         
17999         if(this.showarrow){
18000             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18001             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18002         }
18003         
18004         
18005     },
18006     
18007 //    onTouchStart : function(e, el, o)
18008 //    {
18009 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18010 //            return;
18011 //        }
18012 //        
18013 //        this.showPanelNext();
18014 //    },
18015     
18016     
18017     getChildContainer : function()
18018     {
18019         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18020     },
18021     
18022     /**
18023     * register a Navigation item
18024     * @param {Roo.bootstrap.NavItem} the navitem to add
18025     */
18026     register : function(item)
18027     {
18028         this.tabs.push( item);
18029         item.navId = this.navId; // not really needed..
18030         this.addBullet();
18031     
18032     },
18033     
18034     getActivePanel : function()
18035     {
18036         var r = false;
18037         Roo.each(this.tabs, function(t) {
18038             if (t.active) {
18039                 r = t;
18040                 return false;
18041             }
18042             return null;
18043         });
18044         return r;
18045         
18046     },
18047     getPanelByName : function(n)
18048     {
18049         var r = false;
18050         Roo.each(this.tabs, function(t) {
18051             if (t.tabId == n) {
18052                 r = t;
18053                 return false;
18054             }
18055             return null;
18056         });
18057         return r;
18058     },
18059     indexOfPanel : function(p)
18060     {
18061         var r = false;
18062         Roo.each(this.tabs, function(t,i) {
18063             if (t.tabId == p.tabId) {
18064                 r = i;
18065                 return false;
18066             }
18067             return null;
18068         });
18069         return r;
18070     },
18071     /**
18072      * show a specific panel
18073      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18074      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18075      */
18076     showPanel : function (pan)
18077     {
18078         if(this.transition || typeof(pan) == 'undefined'){
18079             Roo.log("waiting for the transitionend");
18080             return;
18081         }
18082         
18083         if (typeof(pan) == 'number') {
18084             pan = this.tabs[pan];
18085         }
18086         
18087         if (typeof(pan) == 'string') {
18088             pan = this.getPanelByName(pan);
18089         }
18090         
18091         var cur = this.getActivePanel();
18092         
18093         if(!pan || !cur){
18094             Roo.log('pan or acitve pan is undefined');
18095             return false;
18096         }
18097         
18098         if (pan.tabId == this.getActivePanel().tabId) {
18099             return true;
18100         }
18101         
18102         if (false === cur.fireEvent('beforedeactivate')) {
18103             return false;
18104         }
18105         
18106         if(this.bullets > 0 && !Roo.isTouch){
18107             this.setActiveBullet(this.indexOfPanel(pan));
18108         }
18109         
18110         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18111             
18112             this.transition = true;
18113             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18114             var lr = dir == 'next' ? 'left' : 'right';
18115             pan.el.addClass(dir); // or prev
18116             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18117             cur.el.addClass(lr); // or right
18118             pan.el.addClass(lr);
18119             
18120             var _this = this;
18121             cur.el.on('transitionend', function() {
18122                 Roo.log("trans end?");
18123                 
18124                 pan.el.removeClass([lr,dir]);
18125                 pan.setActive(true);
18126                 
18127                 cur.el.removeClass([lr]);
18128                 cur.setActive(false);
18129                 
18130                 _this.transition = false;
18131                 
18132             }, this, { single:  true } );
18133             
18134             return true;
18135         }
18136         
18137         cur.setActive(false);
18138         pan.setActive(true);
18139         
18140         return true;
18141         
18142     },
18143     showPanelNext : function()
18144     {
18145         var i = this.indexOfPanel(this.getActivePanel());
18146         
18147         if (i >= this.tabs.length - 1 && !this.autoslide) {
18148             return;
18149         }
18150         
18151         if (i >= this.tabs.length - 1 && this.autoslide) {
18152             i = -1;
18153         }
18154         
18155         this.showPanel(this.tabs[i+1]);
18156     },
18157     
18158     showPanelPrev : function()
18159     {
18160         var i = this.indexOfPanel(this.getActivePanel());
18161         
18162         if (i  < 1 && !this.autoslide) {
18163             return;
18164         }
18165         
18166         if (i < 1 && this.autoslide) {
18167             i = this.tabs.length;
18168         }
18169         
18170         this.showPanel(this.tabs[i-1]);
18171     },
18172     
18173     
18174     addBullet: function()
18175     {
18176         if(!this.bullets || Roo.isTouch){
18177             return;
18178         }
18179         var ctr = this.el.select('.carousel-bullets',true).first();
18180         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18181         var bullet = ctr.createChild({
18182             cls : 'bullet bullet-' + i
18183         },ctr.dom.lastChild);
18184         
18185         
18186         var _this = this;
18187         
18188         bullet.on('click', (function(e, el, o, ii, t){
18189
18190             e.preventDefault();
18191
18192             this.showPanel(ii);
18193
18194             if(this.autoslide && this.slideFn){
18195                 clearInterval(this.slideFn);
18196                 this.slideFn = window.setInterval(function() {
18197                     _this.showPanelNext();
18198                 }, this.timer);
18199             }
18200
18201         }).createDelegate(this, [i, bullet], true));
18202                 
18203         
18204     },
18205      
18206     setActiveBullet : function(i)
18207     {
18208         if(Roo.isTouch){
18209             return;
18210         }
18211         
18212         Roo.each(this.el.select('.bullet', true).elements, function(el){
18213             el.removeClass('selected');
18214         });
18215
18216         var bullet = this.el.select('.bullet-' + i, true).first();
18217         
18218         if(!bullet){
18219             return;
18220         }
18221         
18222         bullet.addClass('selected');
18223     }
18224     
18225     
18226   
18227 });
18228
18229  
18230
18231  
18232  
18233 Roo.apply(Roo.bootstrap.TabGroup, {
18234     
18235     groups: {},
18236      /**
18237     * register a Navigation Group
18238     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18239     */
18240     register : function(navgrp)
18241     {
18242         this.groups[navgrp.navId] = navgrp;
18243         
18244     },
18245     /**
18246     * fetch a Navigation Group based on the navigation ID
18247     * if one does not exist , it will get created.
18248     * @param {string} the navgroup to add
18249     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18250     */
18251     get: function(navId) {
18252         if (typeof(this.groups[navId]) == 'undefined') {
18253             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18254         }
18255         return this.groups[navId] ;
18256     }
18257     
18258     
18259     
18260 });
18261
18262  /*
18263  * - LGPL
18264  *
18265  * TabPanel
18266  * 
18267  */
18268
18269 /**
18270  * @class Roo.bootstrap.TabPanel
18271  * @extends Roo.bootstrap.Component
18272  * Bootstrap TabPanel class
18273  * @cfg {Boolean} active panel active
18274  * @cfg {String} html panel content
18275  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18276  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18277  * @cfg {String} href click to link..
18278  * 
18279  * 
18280  * @constructor
18281  * Create a new TabPanel
18282  * @param {Object} config The config object
18283  */
18284
18285 Roo.bootstrap.TabPanel = function(config){
18286     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18287     this.addEvents({
18288         /**
18289              * @event changed
18290              * Fires when the active status changes
18291              * @param {Roo.bootstrap.TabPanel} this
18292              * @param {Boolean} state the new state
18293             
18294          */
18295         'changed': true,
18296         /**
18297              * @event beforedeactivate
18298              * Fires before a tab is de-activated - can be used to do validation on a form.
18299              * @param {Roo.bootstrap.TabPanel} this
18300              * @return {Boolean} false if there is an error
18301             
18302          */
18303         'beforedeactivate': true
18304      });
18305     
18306     this.tabId = this.tabId || Roo.id();
18307   
18308 };
18309
18310 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18311     
18312     active: false,
18313     html: false,
18314     tabId: false,
18315     navId : false,
18316     href : '',
18317     
18318     getAutoCreate : function(){
18319         var cfg = {
18320             tag: 'div',
18321             // item is needed for carousel - not sure if it has any effect otherwise
18322             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18323             html: this.html || ''
18324         };
18325         
18326         if(this.active){
18327             cfg.cls += ' active';
18328         }
18329         
18330         if(this.tabId){
18331             cfg.tabId = this.tabId;
18332         }
18333         
18334         
18335         return cfg;
18336     },
18337     
18338     initEvents:  function()
18339     {
18340         var p = this.parent();
18341         
18342         this.navId = this.navId || p.navId;
18343         
18344         if (typeof(this.navId) != 'undefined') {
18345             // not really needed.. but just in case.. parent should be a NavGroup.
18346             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18347             
18348             tg.register(this);
18349             
18350             var i = tg.tabs.length - 1;
18351             
18352             if(this.active && tg.bullets > 0 && i < tg.bullets){
18353                 tg.setActiveBullet(i);
18354             }
18355         }
18356         
18357         this.el.on('click', this.onClick, this);
18358         
18359         if(Roo.isTouch){
18360             this.el.on("touchstart", this.onTouchStart, this);
18361             this.el.on("touchmove", this.onTouchMove, this);
18362             this.el.on("touchend", this.onTouchEnd, this);
18363         }
18364         
18365     },
18366     
18367     onRender : function(ct, position)
18368     {
18369         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18370     },
18371     
18372     setActive : function(state)
18373     {
18374         Roo.log("panel - set active " + this.tabId + "=" + state);
18375         
18376         this.active = state;
18377         if (!state) {
18378             this.el.removeClass('active');
18379             
18380         } else  if (!this.el.hasClass('active')) {
18381             this.el.addClass('active');
18382         }
18383         
18384         this.fireEvent('changed', this, state);
18385     },
18386     
18387     onClick : function(e)
18388     {
18389         e.preventDefault();
18390         
18391         if(!this.href.length){
18392             return;
18393         }
18394         
18395         window.location.href = this.href;
18396     },
18397     
18398     startX : 0,
18399     startY : 0,
18400     endX : 0,
18401     endY : 0,
18402     swiping : false,
18403     
18404     onTouchStart : function(e)
18405     {
18406         this.swiping = false;
18407         
18408         this.startX = e.browserEvent.touches[0].clientX;
18409         this.startY = e.browserEvent.touches[0].clientY;
18410     },
18411     
18412     onTouchMove : function(e)
18413     {
18414         this.swiping = true;
18415         
18416         this.endX = e.browserEvent.touches[0].clientX;
18417         this.endY = e.browserEvent.touches[0].clientY;
18418     },
18419     
18420     onTouchEnd : function(e)
18421     {
18422         if(!this.swiping){
18423             this.onClick(e);
18424             return;
18425         }
18426         
18427         var tabGroup = this.parent();
18428         
18429         if(this.endX > this.startX){ // swiping right
18430             tabGroup.showPanelPrev();
18431             return;
18432         }
18433         
18434         if(this.startX > this.endX){ // swiping left
18435             tabGroup.showPanelNext();
18436             return;
18437         }
18438     }
18439     
18440     
18441 });
18442  
18443
18444  
18445
18446  /*
18447  * - LGPL
18448  *
18449  * DateField
18450  * 
18451  */
18452
18453 /**
18454  * @class Roo.bootstrap.DateField
18455  * @extends Roo.bootstrap.Input
18456  * Bootstrap DateField class
18457  * @cfg {Number} weekStart default 0
18458  * @cfg {String} viewMode default empty, (months|years)
18459  * @cfg {String} minViewMode default empty, (months|years)
18460  * @cfg {Number} startDate default -Infinity
18461  * @cfg {Number} endDate default Infinity
18462  * @cfg {Boolean} todayHighlight default false
18463  * @cfg {Boolean} todayBtn default false
18464  * @cfg {Boolean} calendarWeeks default false
18465  * @cfg {Object} daysOfWeekDisabled default empty
18466  * @cfg {Boolean} singleMode default false (true | false)
18467  * 
18468  * @cfg {Boolean} keyboardNavigation default true
18469  * @cfg {String} language default en
18470  * 
18471  * @constructor
18472  * Create a new DateField
18473  * @param {Object} config The config object
18474  */
18475
18476 Roo.bootstrap.DateField = function(config){
18477     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18478      this.addEvents({
18479             /**
18480              * @event show
18481              * Fires when this field show.
18482              * @param {Roo.bootstrap.DateField} this
18483              * @param {Mixed} date The date value
18484              */
18485             show : true,
18486             /**
18487              * @event show
18488              * Fires when this field hide.
18489              * @param {Roo.bootstrap.DateField} this
18490              * @param {Mixed} date The date value
18491              */
18492             hide : true,
18493             /**
18494              * @event select
18495              * Fires when select a date.
18496              * @param {Roo.bootstrap.DateField} this
18497              * @param {Mixed} date The date value
18498              */
18499             select : true,
18500             /**
18501              * @event beforeselect
18502              * Fires when before select a date.
18503              * @param {Roo.bootstrap.DateField} this
18504              * @param {Mixed} date The date value
18505              */
18506             beforeselect : true
18507         });
18508 };
18509
18510 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18511     
18512     /**
18513      * @cfg {String} format
18514      * The default date format string which can be overriden for localization support.  The format must be
18515      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18516      */
18517     format : "m/d/y",
18518     /**
18519      * @cfg {String} altFormats
18520      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18521      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18522      */
18523     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18524     
18525     weekStart : 0,
18526     
18527     viewMode : '',
18528     
18529     minViewMode : '',
18530     
18531     todayHighlight : false,
18532     
18533     todayBtn: false,
18534     
18535     language: 'en',
18536     
18537     keyboardNavigation: true,
18538     
18539     calendarWeeks: false,
18540     
18541     startDate: -Infinity,
18542     
18543     endDate: Infinity,
18544     
18545     daysOfWeekDisabled: [],
18546     
18547     _events: [],
18548     
18549     singleMode : false,
18550     
18551     UTCDate: function()
18552     {
18553         return new Date(Date.UTC.apply(Date, arguments));
18554     },
18555     
18556     UTCToday: function()
18557     {
18558         var today = new Date();
18559         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18560     },
18561     
18562     getDate: function() {
18563             var d = this.getUTCDate();
18564             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18565     },
18566     
18567     getUTCDate: function() {
18568             return this.date;
18569     },
18570     
18571     setDate: function(d) {
18572             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18573     },
18574     
18575     setUTCDate: function(d) {
18576             this.date = d;
18577             this.setValue(this.formatDate(this.date));
18578     },
18579         
18580     onRender: function(ct, position)
18581     {
18582         
18583         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18584         
18585         this.language = this.language || 'en';
18586         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18587         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18588         
18589         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18590         this.format = this.format || 'm/d/y';
18591         this.isInline = false;
18592         this.isInput = true;
18593         this.component = this.el.select('.add-on', true).first() || false;
18594         this.component = (this.component && this.component.length === 0) ? false : this.component;
18595         this.hasInput = this.component && this.inputEl().length;
18596         
18597         if (typeof(this.minViewMode === 'string')) {
18598             switch (this.minViewMode) {
18599                 case 'months':
18600                     this.minViewMode = 1;
18601                     break;
18602                 case 'years':
18603                     this.minViewMode = 2;
18604                     break;
18605                 default:
18606                     this.minViewMode = 0;
18607                     break;
18608             }
18609         }
18610         
18611         if (typeof(this.viewMode === 'string')) {
18612             switch (this.viewMode) {
18613                 case 'months':
18614                     this.viewMode = 1;
18615                     break;
18616                 case 'years':
18617                     this.viewMode = 2;
18618                     break;
18619                 default:
18620                     this.viewMode = 0;
18621                     break;
18622             }
18623         }
18624                 
18625         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18626         
18627 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18628         
18629         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18630         
18631         this.picker().on('mousedown', this.onMousedown, this);
18632         this.picker().on('click', this.onClick, this);
18633         
18634         this.picker().addClass('datepicker-dropdown');
18635         
18636         this.startViewMode = this.viewMode;
18637         
18638         if(this.singleMode){
18639             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18640                 v.setVisibilityMode(Roo.Element.DISPLAY);
18641                 v.hide();
18642             });
18643             
18644             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18645                 v.setStyle('width', '189px');
18646             });
18647         }
18648         
18649         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18650             if(!this.calendarWeeks){
18651                 v.remove();
18652                 return;
18653             }
18654             
18655             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18656             v.attr('colspan', function(i, val){
18657                 return parseInt(val) + 1;
18658             });
18659         });
18660                         
18661         
18662         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18663         
18664         this.setStartDate(this.startDate);
18665         this.setEndDate(this.endDate);
18666         
18667         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18668         
18669         this.fillDow();
18670         this.fillMonths();
18671         this.update();
18672         this.showMode();
18673         
18674         if(this.isInline) {
18675             this.showPopup();
18676         }
18677     },
18678     
18679     picker : function()
18680     {
18681         return this.pickerEl;
18682 //        return this.el.select('.datepicker', true).first();
18683     },
18684     
18685     fillDow: function()
18686     {
18687         var dowCnt = this.weekStart;
18688         
18689         var dow = {
18690             tag: 'tr',
18691             cn: [
18692                 
18693             ]
18694         };
18695         
18696         if(this.calendarWeeks){
18697             dow.cn.push({
18698                 tag: 'th',
18699                 cls: 'cw',
18700                 html: '&nbsp;'
18701             })
18702         }
18703         
18704         while (dowCnt < this.weekStart + 7) {
18705             dow.cn.push({
18706                 tag: 'th',
18707                 cls: 'dow',
18708                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18709             });
18710         }
18711         
18712         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18713     },
18714     
18715     fillMonths: function()
18716     {    
18717         var i = 0;
18718         var months = this.picker().select('>.datepicker-months td', true).first();
18719         
18720         months.dom.innerHTML = '';
18721         
18722         while (i < 12) {
18723             var month = {
18724                 tag: 'span',
18725                 cls: 'month',
18726                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18727             };
18728             
18729             months.createChild(month);
18730         }
18731         
18732     },
18733     
18734     update: function()
18735     {
18736         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;
18737         
18738         if (this.date < this.startDate) {
18739             this.viewDate = new Date(this.startDate);
18740         } else if (this.date > this.endDate) {
18741             this.viewDate = new Date(this.endDate);
18742         } else {
18743             this.viewDate = new Date(this.date);
18744         }
18745         
18746         this.fill();
18747     },
18748     
18749     fill: function() 
18750     {
18751         var d = new Date(this.viewDate),
18752                 year = d.getUTCFullYear(),
18753                 month = d.getUTCMonth(),
18754                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18755                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18756                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18757                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18758                 currentDate = this.date && this.date.valueOf(),
18759                 today = this.UTCToday();
18760         
18761         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18762         
18763 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18764         
18765 //        this.picker.select('>tfoot th.today').
18766 //                                              .text(dates[this.language].today)
18767 //                                              .toggle(this.todayBtn !== false);
18768     
18769         this.updateNavArrows();
18770         this.fillMonths();
18771                                                 
18772         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18773         
18774         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18775          
18776         prevMonth.setUTCDate(day);
18777         
18778         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18779         
18780         var nextMonth = new Date(prevMonth);
18781         
18782         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18783         
18784         nextMonth = nextMonth.valueOf();
18785         
18786         var fillMonths = false;
18787         
18788         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18789         
18790         while(prevMonth.valueOf() <= nextMonth) {
18791             var clsName = '';
18792             
18793             if (prevMonth.getUTCDay() === this.weekStart) {
18794                 if(fillMonths){
18795                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18796                 }
18797                     
18798                 fillMonths = {
18799                     tag: 'tr',
18800                     cn: []
18801                 };
18802                 
18803                 if(this.calendarWeeks){
18804                     // ISO 8601: First week contains first thursday.
18805                     // ISO also states week starts on Monday, but we can be more abstract here.
18806                     var
18807                     // Start of current week: based on weekstart/current date
18808                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18809                     // Thursday of this week
18810                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18811                     // First Thursday of year, year from thursday
18812                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18813                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18814                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18815                     
18816                     fillMonths.cn.push({
18817                         tag: 'td',
18818                         cls: 'cw',
18819                         html: calWeek
18820                     });
18821                 }
18822             }
18823             
18824             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18825                 clsName += ' old';
18826             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18827                 clsName += ' new';
18828             }
18829             if (this.todayHighlight &&
18830                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18831                 prevMonth.getUTCMonth() == today.getMonth() &&
18832                 prevMonth.getUTCDate() == today.getDate()) {
18833                 clsName += ' today';
18834             }
18835             
18836             if (currentDate && prevMonth.valueOf() === currentDate) {
18837                 clsName += ' active';
18838             }
18839             
18840             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18841                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18842                     clsName += ' disabled';
18843             }
18844             
18845             fillMonths.cn.push({
18846                 tag: 'td',
18847                 cls: 'day ' + clsName,
18848                 html: prevMonth.getDate()
18849             });
18850             
18851             prevMonth.setDate(prevMonth.getDate()+1);
18852         }
18853           
18854         var currentYear = this.date && this.date.getUTCFullYear();
18855         var currentMonth = this.date && this.date.getUTCMonth();
18856         
18857         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18858         
18859         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18860             v.removeClass('active');
18861             
18862             if(currentYear === year && k === currentMonth){
18863                 v.addClass('active');
18864             }
18865             
18866             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18867                 v.addClass('disabled');
18868             }
18869             
18870         });
18871         
18872         
18873         year = parseInt(year/10, 10) * 10;
18874         
18875         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18876         
18877         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18878         
18879         year -= 1;
18880         for (var i = -1; i < 11; i++) {
18881             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18882                 tag: 'span',
18883                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18884                 html: year
18885             });
18886             
18887             year += 1;
18888         }
18889     },
18890     
18891     showMode: function(dir) 
18892     {
18893         if (dir) {
18894             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18895         }
18896         
18897         Roo.each(this.picker().select('>div',true).elements, function(v){
18898             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18899             v.hide();
18900         });
18901         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18902     },
18903     
18904     place: function()
18905     {
18906         if(this.isInline) {
18907             return;
18908         }
18909         
18910         this.picker().removeClass(['bottom', 'top']);
18911         
18912         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18913             /*
18914              * place to the top of element!
18915              *
18916              */
18917             
18918             this.picker().addClass('top');
18919             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18920             
18921             return;
18922         }
18923         
18924         this.picker().addClass('bottom');
18925         
18926         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18927     },
18928     
18929     parseDate : function(value)
18930     {
18931         if(!value || value instanceof Date){
18932             return value;
18933         }
18934         var v = Date.parseDate(value, this.format);
18935         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18936             v = Date.parseDate(value, 'Y-m-d');
18937         }
18938         if(!v && this.altFormats){
18939             if(!this.altFormatsArray){
18940                 this.altFormatsArray = this.altFormats.split("|");
18941             }
18942             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18943                 v = Date.parseDate(value, this.altFormatsArray[i]);
18944             }
18945         }
18946         return v;
18947     },
18948     
18949     formatDate : function(date, fmt)
18950     {   
18951         return (!date || !(date instanceof Date)) ?
18952         date : date.dateFormat(fmt || this.format);
18953     },
18954     
18955     onFocus : function()
18956     {
18957         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18958         this.showPopup();
18959     },
18960     
18961     onBlur : function()
18962     {
18963         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18964         
18965         var d = this.inputEl().getValue();
18966         
18967         this.setValue(d);
18968                 
18969         this.hidePopup();
18970     },
18971     
18972     showPopup : function()
18973     {
18974         this.picker().show();
18975         this.update();
18976         this.place();
18977         
18978         this.fireEvent('showpopup', this, this.date);
18979     },
18980     
18981     hidePopup : function()
18982     {
18983         if(this.isInline) {
18984             return;
18985         }
18986         this.picker().hide();
18987         this.viewMode = this.startViewMode;
18988         this.showMode();
18989         
18990         this.fireEvent('hidepopup', this, this.date);
18991         
18992     },
18993     
18994     onMousedown: function(e)
18995     {
18996         e.stopPropagation();
18997         e.preventDefault();
18998     },
18999     
19000     keyup: function(e)
19001     {
19002         Roo.bootstrap.DateField.superclass.keyup.call(this);
19003         this.update();
19004     },
19005
19006     setValue: function(v)
19007     {
19008         if(this.fireEvent('beforeselect', this, v) !== false){
19009             var d = new Date(this.parseDate(v) ).clearTime();
19010         
19011             if(isNaN(d.getTime())){
19012                 this.date = this.viewDate = '';
19013                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19014                 return;
19015             }
19016
19017             v = this.formatDate(d);
19018
19019             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19020
19021             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19022
19023             this.update();
19024
19025             this.fireEvent('select', this, this.date);
19026         }
19027     },
19028     
19029     getValue: function()
19030     {
19031         return this.formatDate(this.date);
19032     },
19033     
19034     fireKey: function(e)
19035     {
19036         if (!this.picker().isVisible()){
19037             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19038                 this.showPopup();
19039             }
19040             return;
19041         }
19042         
19043         var dateChanged = false,
19044         dir, day, month,
19045         newDate, newViewDate;
19046         
19047         switch(e.keyCode){
19048             case 27: // escape
19049                 this.hidePopup();
19050                 e.preventDefault();
19051                 break;
19052             case 37: // left
19053             case 39: // right
19054                 if (!this.keyboardNavigation) {
19055                     break;
19056                 }
19057                 dir = e.keyCode == 37 ? -1 : 1;
19058                 
19059                 if (e.ctrlKey){
19060                     newDate = this.moveYear(this.date, dir);
19061                     newViewDate = this.moveYear(this.viewDate, dir);
19062                 } else if (e.shiftKey){
19063                     newDate = this.moveMonth(this.date, dir);
19064                     newViewDate = this.moveMonth(this.viewDate, dir);
19065                 } else {
19066                     newDate = new Date(this.date);
19067                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19068                     newViewDate = new Date(this.viewDate);
19069                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19070                 }
19071                 if (this.dateWithinRange(newDate)){
19072                     this.date = newDate;
19073                     this.viewDate = newViewDate;
19074                     this.setValue(this.formatDate(this.date));
19075 //                    this.update();
19076                     e.preventDefault();
19077                     dateChanged = true;
19078                 }
19079                 break;
19080             case 38: // up
19081             case 40: // down
19082                 if (!this.keyboardNavigation) {
19083                     break;
19084                 }
19085                 dir = e.keyCode == 38 ? -1 : 1;
19086                 if (e.ctrlKey){
19087                     newDate = this.moveYear(this.date, dir);
19088                     newViewDate = this.moveYear(this.viewDate, dir);
19089                 } else if (e.shiftKey){
19090                     newDate = this.moveMonth(this.date, dir);
19091                     newViewDate = this.moveMonth(this.viewDate, dir);
19092                 } else {
19093                     newDate = new Date(this.date);
19094                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19095                     newViewDate = new Date(this.viewDate);
19096                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19097                 }
19098                 if (this.dateWithinRange(newDate)){
19099                     this.date = newDate;
19100                     this.viewDate = newViewDate;
19101                     this.setValue(this.formatDate(this.date));
19102 //                    this.update();
19103                     e.preventDefault();
19104                     dateChanged = true;
19105                 }
19106                 break;
19107             case 13: // enter
19108                 this.setValue(this.formatDate(this.date));
19109                 this.hidePopup();
19110                 e.preventDefault();
19111                 break;
19112             case 9: // tab
19113                 this.setValue(this.formatDate(this.date));
19114                 this.hidePopup();
19115                 break;
19116             case 16: // shift
19117             case 17: // ctrl
19118             case 18: // alt
19119                 break;
19120             default :
19121                 this.hide();
19122                 
19123         }
19124     },
19125     
19126     
19127     onClick: function(e) 
19128     {
19129         e.stopPropagation();
19130         e.preventDefault();
19131         
19132         var target = e.getTarget();
19133         
19134         if(target.nodeName.toLowerCase() === 'i'){
19135             target = Roo.get(target).dom.parentNode;
19136         }
19137         
19138         var nodeName = target.nodeName;
19139         var className = target.className;
19140         var html = target.innerHTML;
19141         //Roo.log(nodeName);
19142         
19143         switch(nodeName.toLowerCase()) {
19144             case 'th':
19145                 switch(className) {
19146                     case 'switch':
19147                         this.showMode(1);
19148                         break;
19149                     case 'prev':
19150                     case 'next':
19151                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19152                         switch(this.viewMode){
19153                                 case 0:
19154                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19155                                         break;
19156                                 case 1:
19157                                 case 2:
19158                                         this.viewDate = this.moveYear(this.viewDate, dir);
19159                                         break;
19160                         }
19161                         this.fill();
19162                         break;
19163                     case 'today':
19164                         var date = new Date();
19165                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19166 //                        this.fill()
19167                         this.setValue(this.formatDate(this.date));
19168                         
19169                         this.hidePopup();
19170                         break;
19171                 }
19172                 break;
19173             case 'span':
19174                 if (className.indexOf('disabled') < 0) {
19175                     this.viewDate.setUTCDate(1);
19176                     if (className.indexOf('month') > -1) {
19177                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19178                     } else {
19179                         var year = parseInt(html, 10) || 0;
19180                         this.viewDate.setUTCFullYear(year);
19181                         
19182                     }
19183                     
19184                     if(this.singleMode){
19185                         this.setValue(this.formatDate(this.viewDate));
19186                         this.hidePopup();
19187                         return;
19188                     }
19189                     
19190                     this.showMode(-1);
19191                     this.fill();
19192                 }
19193                 break;
19194                 
19195             case 'td':
19196                 //Roo.log(className);
19197                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19198                     var day = parseInt(html, 10) || 1;
19199                     var year = this.viewDate.getUTCFullYear(),
19200                         month = this.viewDate.getUTCMonth();
19201
19202                     if (className.indexOf('old') > -1) {
19203                         if(month === 0 ){
19204                             month = 11;
19205                             year -= 1;
19206                         }else{
19207                             month -= 1;
19208                         }
19209                     } else if (className.indexOf('new') > -1) {
19210                         if (month == 11) {
19211                             month = 0;
19212                             year += 1;
19213                         } else {
19214                             month += 1;
19215                         }
19216                     }
19217                     //Roo.log([year,month,day]);
19218                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19219                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19220 //                    this.fill();
19221                     //Roo.log(this.formatDate(this.date));
19222                     this.setValue(this.formatDate(this.date));
19223                     this.hidePopup();
19224                 }
19225                 break;
19226         }
19227     },
19228     
19229     setStartDate: function(startDate)
19230     {
19231         this.startDate = startDate || -Infinity;
19232         if (this.startDate !== -Infinity) {
19233             this.startDate = this.parseDate(this.startDate);
19234         }
19235         this.update();
19236         this.updateNavArrows();
19237     },
19238
19239     setEndDate: function(endDate)
19240     {
19241         this.endDate = endDate || Infinity;
19242         if (this.endDate !== Infinity) {
19243             this.endDate = this.parseDate(this.endDate);
19244         }
19245         this.update();
19246         this.updateNavArrows();
19247     },
19248     
19249     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19250     {
19251         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19252         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19253             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19254         }
19255         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19256             return parseInt(d, 10);
19257         });
19258         this.update();
19259         this.updateNavArrows();
19260     },
19261     
19262     updateNavArrows: function() 
19263     {
19264         if(this.singleMode){
19265             return;
19266         }
19267         
19268         var d = new Date(this.viewDate),
19269         year = d.getUTCFullYear(),
19270         month = d.getUTCMonth();
19271         
19272         Roo.each(this.picker().select('.prev', true).elements, function(v){
19273             v.show();
19274             switch (this.viewMode) {
19275                 case 0:
19276
19277                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19278                         v.hide();
19279                     }
19280                     break;
19281                 case 1:
19282                 case 2:
19283                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19284                         v.hide();
19285                     }
19286                     break;
19287             }
19288         });
19289         
19290         Roo.each(this.picker().select('.next', true).elements, function(v){
19291             v.show();
19292             switch (this.viewMode) {
19293                 case 0:
19294
19295                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19296                         v.hide();
19297                     }
19298                     break;
19299                 case 1:
19300                 case 2:
19301                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19302                         v.hide();
19303                     }
19304                     break;
19305             }
19306         })
19307     },
19308     
19309     moveMonth: function(date, dir)
19310     {
19311         if (!dir) {
19312             return date;
19313         }
19314         var new_date = new Date(date.valueOf()),
19315         day = new_date.getUTCDate(),
19316         month = new_date.getUTCMonth(),
19317         mag = Math.abs(dir),
19318         new_month, test;
19319         dir = dir > 0 ? 1 : -1;
19320         if (mag == 1){
19321             test = dir == -1
19322             // If going back one month, make sure month is not current month
19323             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19324             ? function(){
19325                 return new_date.getUTCMonth() == month;
19326             }
19327             // If going forward one month, make sure month is as expected
19328             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19329             : function(){
19330                 return new_date.getUTCMonth() != new_month;
19331             };
19332             new_month = month + dir;
19333             new_date.setUTCMonth(new_month);
19334             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19335             if (new_month < 0 || new_month > 11) {
19336                 new_month = (new_month + 12) % 12;
19337             }
19338         } else {
19339             // For magnitudes >1, move one month at a time...
19340             for (var i=0; i<mag; i++) {
19341                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19342                 new_date = this.moveMonth(new_date, dir);
19343             }
19344             // ...then reset the day, keeping it in the new month
19345             new_month = new_date.getUTCMonth();
19346             new_date.setUTCDate(day);
19347             test = function(){
19348                 return new_month != new_date.getUTCMonth();
19349             };
19350         }
19351         // Common date-resetting loop -- if date is beyond end of month, make it
19352         // end of month
19353         while (test()){
19354             new_date.setUTCDate(--day);
19355             new_date.setUTCMonth(new_month);
19356         }
19357         return new_date;
19358     },
19359
19360     moveYear: function(date, dir)
19361     {
19362         return this.moveMonth(date, dir*12);
19363     },
19364
19365     dateWithinRange: function(date)
19366     {
19367         return date >= this.startDate && date <= this.endDate;
19368     },
19369
19370     
19371     remove: function() 
19372     {
19373         this.picker().remove();
19374     },
19375     
19376     validateValue : function(value)
19377     {
19378         if(this.getVisibilityEl().hasClass('hidden')){
19379             return true;
19380         }
19381         
19382         if(value.length < 1)  {
19383             if(this.allowBlank){
19384                 return true;
19385             }
19386             return false;
19387         }
19388         
19389         if(value.length < this.minLength){
19390             return false;
19391         }
19392         if(value.length > this.maxLength){
19393             return false;
19394         }
19395         if(this.vtype){
19396             var vt = Roo.form.VTypes;
19397             if(!vt[this.vtype](value, this)){
19398                 return false;
19399             }
19400         }
19401         if(typeof this.validator == "function"){
19402             var msg = this.validator(value);
19403             if(msg !== true){
19404                 return false;
19405             }
19406         }
19407         
19408         if(this.regex && !this.regex.test(value)){
19409             return false;
19410         }
19411         
19412         if(typeof(this.parseDate(value)) == 'undefined'){
19413             return false;
19414         }
19415         
19416         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19417             return false;
19418         }      
19419         
19420         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19421             return false;
19422         } 
19423         
19424         
19425         return true;
19426     },
19427     
19428     reset : function()
19429     {
19430         this.date = this.viewDate = '';
19431         
19432         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19433     }
19434    
19435 });
19436
19437 Roo.apply(Roo.bootstrap.DateField,  {
19438     
19439     head : {
19440         tag: 'thead',
19441         cn: [
19442         {
19443             tag: 'tr',
19444             cn: [
19445             {
19446                 tag: 'th',
19447                 cls: 'prev',
19448                 html: '<i class="fa fa-arrow-left"/>'
19449             },
19450             {
19451                 tag: 'th',
19452                 cls: 'switch',
19453                 colspan: '5'
19454             },
19455             {
19456                 tag: 'th',
19457                 cls: 'next',
19458                 html: '<i class="fa fa-arrow-right"/>'
19459             }
19460
19461             ]
19462         }
19463         ]
19464     },
19465     
19466     content : {
19467         tag: 'tbody',
19468         cn: [
19469         {
19470             tag: 'tr',
19471             cn: [
19472             {
19473                 tag: 'td',
19474                 colspan: '7'
19475             }
19476             ]
19477         }
19478         ]
19479     },
19480     
19481     footer : {
19482         tag: 'tfoot',
19483         cn: [
19484         {
19485             tag: 'tr',
19486             cn: [
19487             {
19488                 tag: 'th',
19489                 colspan: '7',
19490                 cls: 'today'
19491             }
19492                     
19493             ]
19494         }
19495         ]
19496     },
19497     
19498     dates:{
19499         en: {
19500             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19501             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19502             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19503             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19504             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19505             today: "Today"
19506         }
19507     },
19508     
19509     modes: [
19510     {
19511         clsName: 'days',
19512         navFnc: 'Month',
19513         navStep: 1
19514     },
19515     {
19516         clsName: 'months',
19517         navFnc: 'FullYear',
19518         navStep: 1
19519     },
19520     {
19521         clsName: 'years',
19522         navFnc: 'FullYear',
19523         navStep: 10
19524     }]
19525 });
19526
19527 Roo.apply(Roo.bootstrap.DateField,  {
19528   
19529     template : {
19530         tag: 'div',
19531         cls: 'datepicker dropdown-menu roo-dynamic',
19532         cn: [
19533         {
19534             tag: 'div',
19535             cls: 'datepicker-days',
19536             cn: [
19537             {
19538                 tag: 'table',
19539                 cls: 'table-condensed',
19540                 cn:[
19541                 Roo.bootstrap.DateField.head,
19542                 {
19543                     tag: 'tbody'
19544                 },
19545                 Roo.bootstrap.DateField.footer
19546                 ]
19547             }
19548             ]
19549         },
19550         {
19551             tag: 'div',
19552             cls: 'datepicker-months',
19553             cn: [
19554             {
19555                 tag: 'table',
19556                 cls: 'table-condensed',
19557                 cn:[
19558                 Roo.bootstrap.DateField.head,
19559                 Roo.bootstrap.DateField.content,
19560                 Roo.bootstrap.DateField.footer
19561                 ]
19562             }
19563             ]
19564         },
19565         {
19566             tag: 'div',
19567             cls: 'datepicker-years',
19568             cn: [
19569             {
19570                 tag: 'table',
19571                 cls: 'table-condensed',
19572                 cn:[
19573                 Roo.bootstrap.DateField.head,
19574                 Roo.bootstrap.DateField.content,
19575                 Roo.bootstrap.DateField.footer
19576                 ]
19577             }
19578             ]
19579         }
19580         ]
19581     }
19582 });
19583
19584  
19585
19586  /*
19587  * - LGPL
19588  *
19589  * TimeField
19590  * 
19591  */
19592
19593 /**
19594  * @class Roo.bootstrap.TimeField
19595  * @extends Roo.bootstrap.Input
19596  * Bootstrap DateField class
19597  * 
19598  * 
19599  * @constructor
19600  * Create a new TimeField
19601  * @param {Object} config The config object
19602  */
19603
19604 Roo.bootstrap.TimeField = function(config){
19605     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19606     this.addEvents({
19607             /**
19608              * @event show
19609              * Fires when this field show.
19610              * @param {Roo.bootstrap.DateField} thisthis
19611              * @param {Mixed} date The date value
19612              */
19613             show : true,
19614             /**
19615              * @event show
19616              * Fires when this field hide.
19617              * @param {Roo.bootstrap.DateField} this
19618              * @param {Mixed} date The date value
19619              */
19620             hide : true,
19621             /**
19622              * @event select
19623              * Fires when select a date.
19624              * @param {Roo.bootstrap.DateField} this
19625              * @param {Mixed} date The date value
19626              */
19627             select : true
19628         });
19629 };
19630
19631 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19632     
19633     /**
19634      * @cfg {String} format
19635      * The default time format string which can be overriden for localization support.  The format must be
19636      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19637      */
19638     format : "H:i",
19639        
19640     onRender: function(ct, position)
19641     {
19642         
19643         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19644                 
19645         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19646         
19647         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19648         
19649         this.pop = this.picker().select('>.datepicker-time',true).first();
19650         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19651         
19652         this.picker().on('mousedown', this.onMousedown, this);
19653         this.picker().on('click', this.onClick, this);
19654         
19655         this.picker().addClass('datepicker-dropdown');
19656     
19657         this.fillTime();
19658         this.update();
19659             
19660         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19661         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19662         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19663         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19664         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19665         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19666
19667     },
19668     
19669     fireKey: function(e){
19670         if (!this.picker().isVisible()){
19671             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19672                 this.show();
19673             }
19674             return;
19675         }
19676
19677         e.preventDefault();
19678         
19679         switch(e.keyCode){
19680             case 27: // escape
19681                 this.hide();
19682                 break;
19683             case 37: // left
19684             case 39: // right
19685                 this.onTogglePeriod();
19686                 break;
19687             case 38: // up
19688                 this.onIncrementMinutes();
19689                 break;
19690             case 40: // down
19691                 this.onDecrementMinutes();
19692                 break;
19693             case 13: // enter
19694             case 9: // tab
19695                 this.setTime();
19696                 break;
19697         }
19698     },
19699     
19700     onClick: function(e) {
19701         e.stopPropagation();
19702         e.preventDefault();
19703     },
19704     
19705     picker : function()
19706     {
19707         return this.el.select('.datepicker', true).first();
19708     },
19709     
19710     fillTime: function()
19711     {    
19712         var time = this.pop.select('tbody', true).first();
19713         
19714         time.dom.innerHTML = '';
19715         
19716         time.createChild({
19717             tag: 'tr',
19718             cn: [
19719                 {
19720                     tag: 'td',
19721                     cn: [
19722                         {
19723                             tag: 'a',
19724                             href: '#',
19725                             cls: 'btn',
19726                             cn: [
19727                                 {
19728                                     tag: 'span',
19729                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19730                                 }
19731                             ]
19732                         } 
19733                     ]
19734                 },
19735                 {
19736                     tag: 'td',
19737                     cls: 'separator'
19738                 },
19739                 {
19740                     tag: 'td',
19741                     cn: [
19742                         {
19743                             tag: 'a',
19744                             href: '#',
19745                             cls: 'btn',
19746                             cn: [
19747                                 {
19748                                     tag: 'span',
19749                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19750                                 }
19751                             ]
19752                         }
19753                     ]
19754                 },
19755                 {
19756                     tag: 'td',
19757                     cls: 'separator'
19758                 }
19759             ]
19760         });
19761         
19762         time.createChild({
19763             tag: 'tr',
19764             cn: [
19765                 {
19766                     tag: 'td',
19767                     cn: [
19768                         {
19769                             tag: 'span',
19770                             cls: 'timepicker-hour',
19771                             html: '00'
19772                         }  
19773                     ]
19774                 },
19775                 {
19776                     tag: 'td',
19777                     cls: 'separator',
19778                     html: ':'
19779                 },
19780                 {
19781                     tag: 'td',
19782                     cn: [
19783                         {
19784                             tag: 'span',
19785                             cls: 'timepicker-minute',
19786                             html: '00'
19787                         }  
19788                     ]
19789                 },
19790                 {
19791                     tag: 'td',
19792                     cls: 'separator'
19793                 },
19794                 {
19795                     tag: 'td',
19796                     cn: [
19797                         {
19798                             tag: 'button',
19799                             type: 'button',
19800                             cls: 'btn btn-primary period',
19801                             html: 'AM'
19802                             
19803                         }
19804                     ]
19805                 }
19806             ]
19807         });
19808         
19809         time.createChild({
19810             tag: 'tr',
19811             cn: [
19812                 {
19813                     tag: 'td',
19814                     cn: [
19815                         {
19816                             tag: 'a',
19817                             href: '#',
19818                             cls: 'btn',
19819                             cn: [
19820                                 {
19821                                     tag: 'span',
19822                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19823                                 }
19824                             ]
19825                         }
19826                     ]
19827                 },
19828                 {
19829                     tag: 'td',
19830                     cls: 'separator'
19831                 },
19832                 {
19833                     tag: 'td',
19834                     cn: [
19835                         {
19836                             tag: 'a',
19837                             href: '#',
19838                             cls: 'btn',
19839                             cn: [
19840                                 {
19841                                     tag: 'span',
19842                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19843                                 }
19844                             ]
19845                         }
19846                     ]
19847                 },
19848                 {
19849                     tag: 'td',
19850                     cls: 'separator'
19851                 }
19852             ]
19853         });
19854         
19855     },
19856     
19857     update: function()
19858     {
19859         
19860         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19861         
19862         this.fill();
19863     },
19864     
19865     fill: function() 
19866     {
19867         var hours = this.time.getHours();
19868         var minutes = this.time.getMinutes();
19869         var period = 'AM';
19870         
19871         if(hours > 11){
19872             period = 'PM';
19873         }
19874         
19875         if(hours == 0){
19876             hours = 12;
19877         }
19878         
19879         
19880         if(hours > 12){
19881             hours = hours - 12;
19882         }
19883         
19884         if(hours < 10){
19885             hours = '0' + hours;
19886         }
19887         
19888         if(minutes < 10){
19889             minutes = '0' + minutes;
19890         }
19891         
19892         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19893         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19894         this.pop.select('button', true).first().dom.innerHTML = period;
19895         
19896     },
19897     
19898     place: function()
19899     {   
19900         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19901         
19902         var cls = ['bottom'];
19903         
19904         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19905             cls.pop();
19906             cls.push('top');
19907         }
19908         
19909         cls.push('right');
19910         
19911         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19912             cls.pop();
19913             cls.push('left');
19914         }
19915         
19916         this.picker().addClass(cls.join('-'));
19917         
19918         var _this = this;
19919         
19920         Roo.each(cls, function(c){
19921             if(c == 'bottom'){
19922                 _this.picker().setTop(_this.inputEl().getHeight());
19923                 return;
19924             }
19925             if(c == 'top'){
19926                 _this.picker().setTop(0 - _this.picker().getHeight());
19927                 return;
19928             }
19929             
19930             if(c == 'left'){
19931                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19932                 return;
19933             }
19934             if(c == 'right'){
19935                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19936                 return;
19937             }
19938         });
19939         
19940     },
19941   
19942     onFocus : function()
19943     {
19944         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19945         this.show();
19946     },
19947     
19948     onBlur : function()
19949     {
19950         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19951         this.hide();
19952     },
19953     
19954     show : function()
19955     {
19956         this.picker().show();
19957         this.pop.show();
19958         this.update();
19959         this.place();
19960         
19961         this.fireEvent('show', this, this.date);
19962     },
19963     
19964     hide : function()
19965     {
19966         this.picker().hide();
19967         this.pop.hide();
19968         
19969         this.fireEvent('hide', this, this.date);
19970     },
19971     
19972     setTime : function()
19973     {
19974         this.hide();
19975         this.setValue(this.time.format(this.format));
19976         
19977         this.fireEvent('select', this, this.date);
19978         
19979         
19980     },
19981     
19982     onMousedown: function(e){
19983         e.stopPropagation();
19984         e.preventDefault();
19985     },
19986     
19987     onIncrementHours: function()
19988     {
19989         Roo.log('onIncrementHours');
19990         this.time = this.time.add(Date.HOUR, 1);
19991         this.update();
19992         
19993     },
19994     
19995     onDecrementHours: function()
19996     {
19997         Roo.log('onDecrementHours');
19998         this.time = this.time.add(Date.HOUR, -1);
19999         this.update();
20000     },
20001     
20002     onIncrementMinutes: function()
20003     {
20004         Roo.log('onIncrementMinutes');
20005         this.time = this.time.add(Date.MINUTE, 1);
20006         this.update();
20007     },
20008     
20009     onDecrementMinutes: function()
20010     {
20011         Roo.log('onDecrementMinutes');
20012         this.time = this.time.add(Date.MINUTE, -1);
20013         this.update();
20014     },
20015     
20016     onTogglePeriod: function()
20017     {
20018         Roo.log('onTogglePeriod');
20019         this.time = this.time.add(Date.HOUR, 12);
20020         this.update();
20021     }
20022     
20023    
20024 });
20025
20026 Roo.apply(Roo.bootstrap.TimeField,  {
20027     
20028     content : {
20029         tag: 'tbody',
20030         cn: [
20031             {
20032                 tag: 'tr',
20033                 cn: [
20034                 {
20035                     tag: 'td',
20036                     colspan: '7'
20037                 }
20038                 ]
20039             }
20040         ]
20041     },
20042     
20043     footer : {
20044         tag: 'tfoot',
20045         cn: [
20046             {
20047                 tag: 'tr',
20048                 cn: [
20049                 {
20050                     tag: 'th',
20051                     colspan: '7',
20052                     cls: '',
20053                     cn: [
20054                         {
20055                             tag: 'button',
20056                             cls: 'btn btn-info ok',
20057                             html: 'OK'
20058                         }
20059                     ]
20060                 }
20061
20062                 ]
20063             }
20064         ]
20065     }
20066 });
20067
20068 Roo.apply(Roo.bootstrap.TimeField,  {
20069   
20070     template : {
20071         tag: 'div',
20072         cls: 'datepicker dropdown-menu',
20073         cn: [
20074             {
20075                 tag: 'div',
20076                 cls: 'datepicker-time',
20077                 cn: [
20078                 {
20079                     tag: 'table',
20080                     cls: 'table-condensed',
20081                     cn:[
20082                     Roo.bootstrap.TimeField.content,
20083                     Roo.bootstrap.TimeField.footer
20084                     ]
20085                 }
20086                 ]
20087             }
20088         ]
20089     }
20090 });
20091
20092  
20093
20094  /*
20095  * - LGPL
20096  *
20097  * MonthField
20098  * 
20099  */
20100
20101 /**
20102  * @class Roo.bootstrap.MonthField
20103  * @extends Roo.bootstrap.Input
20104  * Bootstrap MonthField class
20105  * 
20106  * @cfg {String} language default en
20107  * 
20108  * @constructor
20109  * Create a new MonthField
20110  * @param {Object} config The config object
20111  */
20112
20113 Roo.bootstrap.MonthField = function(config){
20114     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20115     
20116     this.addEvents({
20117         /**
20118          * @event show
20119          * Fires when this field show.
20120          * @param {Roo.bootstrap.MonthField} this
20121          * @param {Mixed} date The date value
20122          */
20123         show : true,
20124         /**
20125          * @event show
20126          * Fires when this field hide.
20127          * @param {Roo.bootstrap.MonthField} this
20128          * @param {Mixed} date The date value
20129          */
20130         hide : true,
20131         /**
20132          * @event select
20133          * Fires when select a date.
20134          * @param {Roo.bootstrap.MonthField} this
20135          * @param {String} oldvalue The old value
20136          * @param {String} newvalue The new value
20137          */
20138         select : true
20139     });
20140 };
20141
20142 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20143     
20144     onRender: function(ct, position)
20145     {
20146         
20147         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20148         
20149         this.language = this.language || 'en';
20150         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20151         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20152         
20153         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20154         this.isInline = false;
20155         this.isInput = true;
20156         this.component = this.el.select('.add-on', true).first() || false;
20157         this.component = (this.component && this.component.length === 0) ? false : this.component;
20158         this.hasInput = this.component && this.inputEL().length;
20159         
20160         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20161         
20162         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20163         
20164         this.picker().on('mousedown', this.onMousedown, this);
20165         this.picker().on('click', this.onClick, this);
20166         
20167         this.picker().addClass('datepicker-dropdown');
20168         
20169         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20170             v.setStyle('width', '189px');
20171         });
20172         
20173         this.fillMonths();
20174         
20175         this.update();
20176         
20177         if(this.isInline) {
20178             this.show();
20179         }
20180         
20181     },
20182     
20183     setValue: function(v, suppressEvent)
20184     {   
20185         var o = this.getValue();
20186         
20187         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20188         
20189         this.update();
20190
20191         if(suppressEvent !== true){
20192             this.fireEvent('select', this, o, v);
20193         }
20194         
20195     },
20196     
20197     getValue: function()
20198     {
20199         return this.value;
20200     },
20201     
20202     onClick: function(e) 
20203     {
20204         e.stopPropagation();
20205         e.preventDefault();
20206         
20207         var target = e.getTarget();
20208         
20209         if(target.nodeName.toLowerCase() === 'i'){
20210             target = Roo.get(target).dom.parentNode;
20211         }
20212         
20213         var nodeName = target.nodeName;
20214         var className = target.className;
20215         var html = target.innerHTML;
20216         
20217         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20218             return;
20219         }
20220         
20221         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20222         
20223         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20224         
20225         this.hide();
20226                         
20227     },
20228     
20229     picker : function()
20230     {
20231         return this.pickerEl;
20232     },
20233     
20234     fillMonths: function()
20235     {    
20236         var i = 0;
20237         var months = this.picker().select('>.datepicker-months td', true).first();
20238         
20239         months.dom.innerHTML = '';
20240         
20241         while (i < 12) {
20242             var month = {
20243                 tag: 'span',
20244                 cls: 'month',
20245                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20246             };
20247             
20248             months.createChild(month);
20249         }
20250         
20251     },
20252     
20253     update: function()
20254     {
20255         var _this = this;
20256         
20257         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20258             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20259         }
20260         
20261         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20262             e.removeClass('active');
20263             
20264             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20265                 e.addClass('active');
20266             }
20267         })
20268     },
20269     
20270     place: function()
20271     {
20272         if(this.isInline) {
20273             return;
20274         }
20275         
20276         this.picker().removeClass(['bottom', 'top']);
20277         
20278         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20279             /*
20280              * place to the top of element!
20281              *
20282              */
20283             
20284             this.picker().addClass('top');
20285             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20286             
20287             return;
20288         }
20289         
20290         this.picker().addClass('bottom');
20291         
20292         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20293     },
20294     
20295     onFocus : function()
20296     {
20297         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20298         this.show();
20299     },
20300     
20301     onBlur : function()
20302     {
20303         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20304         
20305         var d = this.inputEl().getValue();
20306         
20307         this.setValue(d);
20308                 
20309         this.hide();
20310     },
20311     
20312     show : function()
20313     {
20314         this.picker().show();
20315         this.picker().select('>.datepicker-months', true).first().show();
20316         this.update();
20317         this.place();
20318         
20319         this.fireEvent('show', this, this.date);
20320     },
20321     
20322     hide : function()
20323     {
20324         if(this.isInline) {
20325             return;
20326         }
20327         this.picker().hide();
20328         this.fireEvent('hide', this, this.date);
20329         
20330     },
20331     
20332     onMousedown: function(e)
20333     {
20334         e.stopPropagation();
20335         e.preventDefault();
20336     },
20337     
20338     keyup: function(e)
20339     {
20340         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20341         this.update();
20342     },
20343
20344     fireKey: function(e)
20345     {
20346         if (!this.picker().isVisible()){
20347             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20348                 this.show();
20349             }
20350             return;
20351         }
20352         
20353         var dir;
20354         
20355         switch(e.keyCode){
20356             case 27: // escape
20357                 this.hide();
20358                 e.preventDefault();
20359                 break;
20360             case 37: // left
20361             case 39: // right
20362                 dir = e.keyCode == 37 ? -1 : 1;
20363                 
20364                 this.vIndex = this.vIndex + dir;
20365                 
20366                 if(this.vIndex < 0){
20367                     this.vIndex = 0;
20368                 }
20369                 
20370                 if(this.vIndex > 11){
20371                     this.vIndex = 11;
20372                 }
20373                 
20374                 if(isNaN(this.vIndex)){
20375                     this.vIndex = 0;
20376                 }
20377                 
20378                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20379                 
20380                 break;
20381             case 38: // up
20382             case 40: // down
20383                 
20384                 dir = e.keyCode == 38 ? -1 : 1;
20385                 
20386                 this.vIndex = this.vIndex + dir * 4;
20387                 
20388                 if(this.vIndex < 0){
20389                     this.vIndex = 0;
20390                 }
20391                 
20392                 if(this.vIndex > 11){
20393                     this.vIndex = 11;
20394                 }
20395                 
20396                 if(isNaN(this.vIndex)){
20397                     this.vIndex = 0;
20398                 }
20399                 
20400                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20401                 break;
20402                 
20403             case 13: // enter
20404                 
20405                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20406                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20407                 }
20408                 
20409                 this.hide();
20410                 e.preventDefault();
20411                 break;
20412             case 9: // tab
20413                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20414                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20415                 }
20416                 this.hide();
20417                 break;
20418             case 16: // shift
20419             case 17: // ctrl
20420             case 18: // alt
20421                 break;
20422             default :
20423                 this.hide();
20424                 
20425         }
20426     },
20427     
20428     remove: function() 
20429     {
20430         this.picker().remove();
20431     }
20432    
20433 });
20434
20435 Roo.apply(Roo.bootstrap.MonthField,  {
20436     
20437     content : {
20438         tag: 'tbody',
20439         cn: [
20440         {
20441             tag: 'tr',
20442             cn: [
20443             {
20444                 tag: 'td',
20445                 colspan: '7'
20446             }
20447             ]
20448         }
20449         ]
20450     },
20451     
20452     dates:{
20453         en: {
20454             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20455             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20456         }
20457     }
20458 });
20459
20460 Roo.apply(Roo.bootstrap.MonthField,  {
20461   
20462     template : {
20463         tag: 'div',
20464         cls: 'datepicker dropdown-menu roo-dynamic',
20465         cn: [
20466             {
20467                 tag: 'div',
20468                 cls: 'datepicker-months',
20469                 cn: [
20470                 {
20471                     tag: 'table',
20472                     cls: 'table-condensed',
20473                     cn:[
20474                         Roo.bootstrap.DateField.content
20475                     ]
20476                 }
20477                 ]
20478             }
20479         ]
20480     }
20481 });
20482
20483  
20484
20485  
20486  /*
20487  * - LGPL
20488  *
20489  * CheckBox
20490  * 
20491  */
20492
20493 /**
20494  * @class Roo.bootstrap.CheckBox
20495  * @extends Roo.bootstrap.Input
20496  * Bootstrap CheckBox class
20497  * 
20498  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20499  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20500  * @cfg {String} boxLabel The text that appears beside the checkbox
20501  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20502  * @cfg {Boolean} checked initnal the element
20503  * @cfg {Boolean} inline inline the element (default false)
20504  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20505  * @cfg {String} tooltip label tooltip
20506  * 
20507  * @constructor
20508  * Create a new CheckBox
20509  * @param {Object} config The config object
20510  */
20511
20512 Roo.bootstrap.CheckBox = function(config){
20513     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20514    
20515     this.addEvents({
20516         /**
20517         * @event check
20518         * Fires when the element is checked or unchecked.
20519         * @param {Roo.bootstrap.CheckBox} this This input
20520         * @param {Boolean} checked The new checked value
20521         */
20522        check : true,
20523        /**
20524         * @event click
20525         * Fires when the element is click.
20526         * @param {Roo.bootstrap.CheckBox} this This input
20527         */
20528        click : true
20529     });
20530     
20531 };
20532
20533 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20534   
20535     inputType: 'checkbox',
20536     inputValue: 1,
20537     valueOff: 0,
20538     boxLabel: false,
20539     checked: false,
20540     weight : false,
20541     inline: false,
20542     tooltip : '',
20543     
20544     getAutoCreate : function()
20545     {
20546         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20547         
20548         var id = Roo.id();
20549         
20550         var cfg = {};
20551         
20552         cfg.cls = 'form-group ' + this.inputType; //input-group
20553         
20554         if(this.inline){
20555             cfg.cls += ' ' + this.inputType + '-inline';
20556         }
20557         
20558         var input =  {
20559             tag: 'input',
20560             id : id,
20561             type : this.inputType,
20562             value : this.inputValue,
20563             cls : 'roo-' + this.inputType, //'form-box',
20564             placeholder : this.placeholder || ''
20565             
20566         };
20567         
20568         if(this.inputType != 'radio'){
20569             var hidden =  {
20570                 tag: 'input',
20571                 type : 'hidden',
20572                 cls : 'roo-hidden-value',
20573                 value : this.checked ? this.inputValue : this.valueOff
20574             };
20575         }
20576         
20577             
20578         if (this.weight) { // Validity check?
20579             cfg.cls += " " + this.inputType + "-" + this.weight;
20580         }
20581         
20582         if (this.disabled) {
20583             input.disabled=true;
20584         }
20585         
20586         if(this.checked){
20587             input.checked = this.checked;
20588         }
20589         
20590         if (this.name) {
20591             
20592             input.name = this.name;
20593             
20594             if(this.inputType != 'radio'){
20595                 hidden.name = this.name;
20596                 input.name = '_hidden_' + this.name;
20597             }
20598         }
20599         
20600         if (this.size) {
20601             input.cls += ' input-' + this.size;
20602         }
20603         
20604         var settings=this;
20605         
20606         ['xs','sm','md','lg'].map(function(size){
20607             if (settings[size]) {
20608                 cfg.cls += ' col-' + size + '-' + settings[size];
20609             }
20610         });
20611         
20612         var inputblock = input;
20613          
20614         if (this.before || this.after) {
20615             
20616             inputblock = {
20617                 cls : 'input-group',
20618                 cn :  [] 
20619             };
20620             
20621             if (this.before) {
20622                 inputblock.cn.push({
20623                     tag :'span',
20624                     cls : 'input-group-addon',
20625                     html : this.before
20626                 });
20627             }
20628             
20629             inputblock.cn.push(input);
20630             
20631             if(this.inputType != 'radio'){
20632                 inputblock.cn.push(hidden);
20633             }
20634             
20635             if (this.after) {
20636                 inputblock.cn.push({
20637                     tag :'span',
20638                     cls : 'input-group-addon',
20639                     html : this.after
20640                 });
20641             }
20642             
20643         }
20644         
20645         if (align ==='left' && this.fieldLabel.length) {
20646 //                Roo.log("left and has label");
20647             cfg.cn = [
20648                 {
20649                     tag: 'label',
20650                     'for' :  id,
20651                     cls : 'control-label',
20652                     html : this.fieldLabel
20653                 },
20654                 {
20655                     cls : "", 
20656                     cn: [
20657                         inputblock
20658                     ]
20659                 }
20660             ];
20661             
20662             if(this.labelWidth > 12){
20663                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20664             }
20665             
20666             if(this.labelWidth < 13 && this.labelmd == 0){
20667                 this.labelmd = this.labelWidth;
20668             }
20669             
20670             if(this.labellg > 0){
20671                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20672                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20673             }
20674             
20675             if(this.labelmd > 0){
20676                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20677                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20678             }
20679             
20680             if(this.labelsm > 0){
20681                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20682                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20683             }
20684             
20685             if(this.labelxs > 0){
20686                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20687                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20688             }
20689             
20690         } else if ( this.fieldLabel.length) {
20691 //                Roo.log(" label");
20692                 cfg.cn = [
20693                    
20694                     {
20695                         tag: this.boxLabel ? 'span' : 'label',
20696                         'for': id,
20697                         cls: 'control-label box-input-label',
20698                         //cls : 'input-group-addon',
20699                         html : this.fieldLabel
20700                     },
20701                     
20702                     inputblock
20703                     
20704                 ];
20705
20706         } else {
20707             
20708 //                Roo.log(" no label && no align");
20709                 cfg.cn = [  inputblock ] ;
20710                 
20711                 
20712         }
20713         
20714         if(this.boxLabel){
20715              var boxLabelCfg = {
20716                 tag: 'label',
20717                 //'for': id, // box label is handled by onclick - so no for...
20718                 cls: 'box-label',
20719                 html: this.boxLabel
20720             };
20721             
20722             if(this.tooltip){
20723                 boxLabelCfg.tooltip = this.tooltip;
20724             }
20725              
20726             cfg.cn.push(boxLabelCfg);
20727         }
20728         
20729         if(this.inputType != 'radio'){
20730             cfg.cn.push(hidden);
20731         }
20732         
20733         return cfg;
20734         
20735     },
20736     
20737     /**
20738      * return the real input element.
20739      */
20740     inputEl: function ()
20741     {
20742         return this.el.select('input.roo-' + this.inputType,true).first();
20743     },
20744     hiddenEl: function ()
20745     {
20746         return this.el.select('input.roo-hidden-value',true).first();
20747     },
20748     
20749     labelEl: function()
20750     {
20751         return this.el.select('label.control-label',true).first();
20752     },
20753     /* depricated... */
20754     
20755     label: function()
20756     {
20757         return this.labelEl();
20758     },
20759     
20760     boxLabelEl: function()
20761     {
20762         return this.el.select('label.box-label',true).first();
20763     },
20764     
20765     initEvents : function()
20766     {
20767 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20768         
20769         this.inputEl().on('click', this.onClick,  this);
20770         
20771         if (this.boxLabel) { 
20772             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20773         }
20774         
20775         this.startValue = this.getValue();
20776         
20777         if(this.groupId){
20778             Roo.bootstrap.CheckBox.register(this);
20779         }
20780     },
20781     
20782     onClick : function(e)
20783     {   
20784         if(this.fireEvent('click', this, e) !== false){
20785             this.setChecked(!this.checked);
20786         }
20787         
20788     },
20789     
20790     setChecked : function(state,suppressEvent)
20791     {
20792         this.startValue = this.getValue();
20793
20794         if(this.inputType == 'radio'){
20795             
20796             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20797                 e.dom.checked = false;
20798             });
20799             
20800             this.inputEl().dom.checked = true;
20801             
20802             this.inputEl().dom.value = this.inputValue;
20803             
20804             if(suppressEvent !== true){
20805                 this.fireEvent('check', this, true);
20806             }
20807             
20808             this.validate();
20809             
20810             return;
20811         }
20812         
20813         this.checked = state;
20814         
20815         this.inputEl().dom.checked = state;
20816         
20817         
20818         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20819         
20820         if(suppressEvent !== true){
20821             this.fireEvent('check', this, state);
20822         }
20823         
20824         this.validate();
20825     },
20826     
20827     getValue : function()
20828     {
20829         if(this.inputType == 'radio'){
20830             return this.getGroupValue();
20831         }
20832         
20833         return this.hiddenEl().dom.value;
20834         
20835     },
20836     
20837     getGroupValue : function()
20838     {
20839         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20840             return '';
20841         }
20842         
20843         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20844     },
20845     
20846     setValue : function(v,suppressEvent)
20847     {
20848         if(this.inputType == 'radio'){
20849             this.setGroupValue(v, suppressEvent);
20850             return;
20851         }
20852         
20853         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20854         
20855         this.validate();
20856     },
20857     
20858     setGroupValue : function(v, suppressEvent)
20859     {
20860         this.startValue = this.getValue();
20861         
20862         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20863             e.dom.checked = false;
20864             
20865             if(e.dom.value == v){
20866                 e.dom.checked = true;
20867             }
20868         });
20869         
20870         if(suppressEvent !== true){
20871             this.fireEvent('check', this, true);
20872         }
20873
20874         this.validate();
20875         
20876         return;
20877     },
20878     
20879     validate : function()
20880     {
20881         if(this.getVisibilityEl().hasClass('hidden')){
20882             return true;
20883         }
20884         
20885         if(
20886                 this.disabled || 
20887                 (this.inputType == 'radio' && this.validateRadio()) ||
20888                 (this.inputType == 'checkbox' && this.validateCheckbox())
20889         ){
20890             this.markValid();
20891             return true;
20892         }
20893         
20894         this.markInvalid();
20895         return false;
20896     },
20897     
20898     validateRadio : function()
20899     {
20900         if(this.getVisibilityEl().hasClass('hidden')){
20901             return true;
20902         }
20903         
20904         if(this.allowBlank){
20905             return true;
20906         }
20907         
20908         var valid = false;
20909         
20910         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20911             if(!e.dom.checked){
20912                 return;
20913             }
20914             
20915             valid = true;
20916             
20917             return false;
20918         });
20919         
20920         return valid;
20921     },
20922     
20923     validateCheckbox : function()
20924     {
20925         if(!this.groupId){
20926             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20927             //return (this.getValue() == this.inputValue) ? true : false;
20928         }
20929         
20930         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20931         
20932         if(!group){
20933             return false;
20934         }
20935         
20936         var r = false;
20937         
20938         for(var i in group){
20939             if(group[i].el.isVisible(true)){
20940                 r = false;
20941                 break;
20942             }
20943             
20944             r = true;
20945         }
20946         
20947         for(var i in group){
20948             if(r){
20949                 break;
20950             }
20951             
20952             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20953         }
20954         
20955         return r;
20956     },
20957     
20958     /**
20959      * Mark this field as valid
20960      */
20961     markValid : function()
20962     {
20963         var _this = this;
20964         
20965         this.fireEvent('valid', this);
20966         
20967         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20968         
20969         if(this.groupId){
20970             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20971         }
20972         
20973         if(label){
20974             label.markValid();
20975         }
20976
20977         if(this.inputType == 'radio'){
20978             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20979                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20980                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20981             });
20982             
20983             return;
20984         }
20985
20986         if(!this.groupId){
20987             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20988             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20989             return;
20990         }
20991         
20992         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20993         
20994         if(!group){
20995             return;
20996         }
20997         
20998         for(var i in group){
20999             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21000             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21001         }
21002     },
21003     
21004      /**
21005      * Mark this field as invalid
21006      * @param {String} msg The validation message
21007      */
21008     markInvalid : function(msg)
21009     {
21010         if(this.allowBlank){
21011             return;
21012         }
21013         
21014         var _this = this;
21015         
21016         this.fireEvent('invalid', this, msg);
21017         
21018         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21019         
21020         if(this.groupId){
21021             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21022         }
21023         
21024         if(label){
21025             label.markInvalid();
21026         }
21027             
21028         if(this.inputType == 'radio'){
21029             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21030                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21031                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21032             });
21033             
21034             return;
21035         }
21036         
21037         if(!this.groupId){
21038             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21039             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21040             return;
21041         }
21042         
21043         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21044         
21045         if(!group){
21046             return;
21047         }
21048         
21049         for(var i in group){
21050             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21051             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21052         }
21053         
21054     },
21055     
21056     clearInvalid : function()
21057     {
21058         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21059         
21060         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21061         
21062         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21063         
21064         if (label && label.iconEl) {
21065             label.iconEl.removeClass(label.validClass);
21066             label.iconEl.removeClass(label.invalidClass);
21067         }
21068     },
21069     
21070     disable : function()
21071     {
21072         if(this.inputType != 'radio'){
21073             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21074             return;
21075         }
21076         
21077         var _this = this;
21078         
21079         if(this.rendered){
21080             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21081                 _this.getActionEl().addClass(this.disabledClass);
21082                 e.dom.disabled = true;
21083             });
21084         }
21085         
21086         this.disabled = true;
21087         this.fireEvent("disable", this);
21088         return this;
21089     },
21090
21091     enable : function()
21092     {
21093         if(this.inputType != 'radio'){
21094             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21095             return;
21096         }
21097         
21098         var _this = this;
21099         
21100         if(this.rendered){
21101             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21102                 _this.getActionEl().removeClass(this.disabledClass);
21103                 e.dom.disabled = false;
21104             });
21105         }
21106         
21107         this.disabled = false;
21108         this.fireEvent("enable", this);
21109         return this;
21110     },
21111     
21112     setBoxLabel : function(v)
21113     {
21114         this.boxLabel = v;
21115         
21116         if(this.rendered){
21117             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21118         }
21119     }
21120
21121 });
21122
21123 Roo.apply(Roo.bootstrap.CheckBox, {
21124     
21125     groups: {},
21126     
21127      /**
21128     * register a CheckBox Group
21129     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21130     */
21131     register : function(checkbox)
21132     {
21133         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21134             this.groups[checkbox.groupId] = {};
21135         }
21136         
21137         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21138             return;
21139         }
21140         
21141         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21142         
21143     },
21144     /**
21145     * fetch a CheckBox Group based on the group ID
21146     * @param {string} the group ID
21147     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21148     */
21149     get: function(groupId) {
21150         if (typeof(this.groups[groupId]) == 'undefined') {
21151             return false;
21152         }
21153         
21154         return this.groups[groupId] ;
21155     }
21156     
21157     
21158 });
21159 /*
21160  * - LGPL
21161  *
21162  * RadioItem
21163  * 
21164  */
21165
21166 /**
21167  * @class Roo.bootstrap.Radio
21168  * @extends Roo.bootstrap.Component
21169  * Bootstrap Radio class
21170  * @cfg {String} boxLabel - the label associated
21171  * @cfg {String} value - the value of radio
21172  * 
21173  * @constructor
21174  * Create a new Radio
21175  * @param {Object} config The config object
21176  */
21177 Roo.bootstrap.Radio = function(config){
21178     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21179     
21180 };
21181
21182 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21183     
21184     boxLabel : '',
21185     
21186     value : '',
21187     
21188     getAutoCreate : function()
21189     {
21190         var cfg = {
21191             tag : 'div',
21192             cls : 'form-group radio',
21193             cn : [
21194                 {
21195                     tag : 'label',
21196                     cls : 'box-label',
21197                     html : this.boxLabel
21198                 }
21199             ]
21200         };
21201         
21202         return cfg;
21203     },
21204     
21205     initEvents : function() 
21206     {
21207         this.parent().register(this);
21208         
21209         this.el.on('click', this.onClick, this);
21210         
21211     },
21212     
21213     onClick : function(e)
21214     {
21215         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21216             this.setChecked(true);
21217         }
21218     },
21219     
21220     setChecked : function(state, suppressEvent)
21221     {
21222         this.parent().setValue(this.value, suppressEvent);
21223         
21224     },
21225     
21226     setBoxLabel : function(v)
21227     {
21228         this.boxLabel = v;
21229         
21230         if(this.rendered){
21231             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21232         }
21233     }
21234     
21235 });
21236  
21237
21238  /*
21239  * - LGPL
21240  *
21241  * Input
21242  * 
21243  */
21244
21245 /**
21246  * @class Roo.bootstrap.SecurePass
21247  * @extends Roo.bootstrap.Input
21248  * Bootstrap SecurePass class
21249  *
21250  * 
21251  * @constructor
21252  * Create a new SecurePass
21253  * @param {Object} config The config object
21254  */
21255  
21256 Roo.bootstrap.SecurePass = function (config) {
21257     // these go here, so the translation tool can replace them..
21258     this.errors = {
21259         PwdEmpty: "Please type a password, and then retype it to confirm.",
21260         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21261         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21262         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21263         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21264         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21265         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21266         TooWeak: "Your password is Too Weak."
21267     },
21268     this.meterLabel = "Password strength:";
21269     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21270     this.meterClass = [
21271         "roo-password-meter-tooweak", 
21272         "roo-password-meter-weak", 
21273         "roo-password-meter-medium", 
21274         "roo-password-meter-strong", 
21275         "roo-password-meter-grey"
21276     ];
21277     
21278     this.errors = {};
21279     
21280     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21281 }
21282
21283 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21284     /**
21285      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21286      * {
21287      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21288      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21289      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21290      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21291      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21292      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21293      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21294      * })
21295      */
21296     // private
21297     
21298     meterWidth: 300,
21299     errorMsg :'',    
21300     errors: false,
21301     imageRoot: '/',
21302     /**
21303      * @cfg {String/Object} Label for the strength meter (defaults to
21304      * 'Password strength:')
21305      */
21306     // private
21307     meterLabel: '',
21308     /**
21309      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21310      * ['Weak', 'Medium', 'Strong'])
21311      */
21312     // private    
21313     pwdStrengths: false,    
21314     // private
21315     strength: 0,
21316     // private
21317     _lastPwd: null,
21318     // private
21319     kCapitalLetter: 0,
21320     kSmallLetter: 1,
21321     kDigit: 2,
21322     kPunctuation: 3,
21323     
21324     insecure: false,
21325     // private
21326     initEvents: function ()
21327     {
21328         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21329
21330         if (this.el.is('input[type=password]') && Roo.isSafari) {
21331             this.el.on('keydown', this.SafariOnKeyDown, this);
21332         }
21333
21334         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21335     },
21336     // private
21337     onRender: function (ct, position)
21338     {
21339         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21340         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21341         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21342
21343         this.trigger.createChild({
21344                    cn: [
21345                     {
21346                     //id: 'PwdMeter',
21347                     tag: 'div',
21348                     cls: 'roo-password-meter-grey col-xs-12',
21349                     style: {
21350                         //width: 0,
21351                         //width: this.meterWidth + 'px'                                                
21352                         }
21353                     },
21354                     {                            
21355                          cls: 'roo-password-meter-text'                          
21356                     }
21357                 ]            
21358         });
21359
21360          
21361         if (this.hideTrigger) {
21362             this.trigger.setDisplayed(false);
21363         }
21364         this.setSize(this.width || '', this.height || '');
21365     },
21366     // private
21367     onDestroy: function ()
21368     {
21369         if (this.trigger) {
21370             this.trigger.removeAllListeners();
21371             this.trigger.remove();
21372         }
21373         if (this.wrap) {
21374             this.wrap.remove();
21375         }
21376         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21377     },
21378     // private
21379     checkStrength: function ()
21380     {
21381         var pwd = this.inputEl().getValue();
21382         if (pwd == this._lastPwd) {
21383             return;
21384         }
21385
21386         var strength;
21387         if (this.ClientSideStrongPassword(pwd)) {
21388             strength = 3;
21389         } else if (this.ClientSideMediumPassword(pwd)) {
21390             strength = 2;
21391         } else if (this.ClientSideWeakPassword(pwd)) {
21392             strength = 1;
21393         } else {
21394             strength = 0;
21395         }
21396         
21397         Roo.log('strength1: ' + strength);
21398         
21399         //var pm = this.trigger.child('div/div/div').dom;
21400         var pm = this.trigger.child('div/div');
21401         pm.removeClass(this.meterClass);
21402         pm.addClass(this.meterClass[strength]);
21403                 
21404         
21405         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21406                 
21407         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21408         
21409         this._lastPwd = pwd;
21410     },
21411     reset: function ()
21412     {
21413         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21414         
21415         this._lastPwd = '';
21416         
21417         var pm = this.trigger.child('div/div');
21418         pm.removeClass(this.meterClass);
21419         pm.addClass('roo-password-meter-grey');        
21420         
21421         
21422         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21423         
21424         pt.innerHTML = '';
21425         this.inputEl().dom.type='password';
21426     },
21427     // private
21428     validateValue: function (value)
21429     {
21430         
21431         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21432             return false;
21433         }
21434         if (value.length == 0) {
21435             if (this.allowBlank) {
21436                 this.clearInvalid();
21437                 return true;
21438             }
21439
21440             this.markInvalid(this.errors.PwdEmpty);
21441             this.errorMsg = this.errors.PwdEmpty;
21442             return false;
21443         }
21444         
21445         if(this.insecure){
21446             return true;
21447         }
21448         
21449         if ('[\x21-\x7e]*'.match(value)) {
21450             this.markInvalid(this.errors.PwdBadChar);
21451             this.errorMsg = this.errors.PwdBadChar;
21452             return false;
21453         }
21454         if (value.length < 6) {
21455             this.markInvalid(this.errors.PwdShort);
21456             this.errorMsg = this.errors.PwdShort;
21457             return false;
21458         }
21459         if (value.length > 16) {
21460             this.markInvalid(this.errors.PwdLong);
21461             this.errorMsg = this.errors.PwdLong;
21462             return false;
21463         }
21464         var strength;
21465         if (this.ClientSideStrongPassword(value)) {
21466             strength = 3;
21467         } else if (this.ClientSideMediumPassword(value)) {
21468             strength = 2;
21469         } else if (this.ClientSideWeakPassword(value)) {
21470             strength = 1;
21471         } else {
21472             strength = 0;
21473         }
21474
21475         
21476         if (strength < 2) {
21477             //this.markInvalid(this.errors.TooWeak);
21478             this.errorMsg = this.errors.TooWeak;
21479             //return false;
21480         }
21481         
21482         
21483         console.log('strength2: ' + strength);
21484         
21485         //var pm = this.trigger.child('div/div/div').dom;
21486         
21487         var pm = this.trigger.child('div/div');
21488         pm.removeClass(this.meterClass);
21489         pm.addClass(this.meterClass[strength]);
21490                 
21491         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21492                 
21493         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21494         
21495         this.errorMsg = ''; 
21496         return true;
21497     },
21498     // private
21499     CharacterSetChecks: function (type)
21500     {
21501         this.type = type;
21502         this.fResult = false;
21503     },
21504     // private
21505     isctype: function (character, type)
21506     {
21507         switch (type) {  
21508             case this.kCapitalLetter:
21509                 if (character >= 'A' && character <= 'Z') {
21510                     return true;
21511                 }
21512                 break;
21513             
21514             case this.kSmallLetter:
21515                 if (character >= 'a' && character <= 'z') {
21516                     return true;
21517                 }
21518                 break;
21519             
21520             case this.kDigit:
21521                 if (character >= '0' && character <= '9') {
21522                     return true;
21523                 }
21524                 break;
21525             
21526             case this.kPunctuation:
21527                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21528                     return true;
21529                 }
21530                 break;
21531             
21532             default:
21533                 return false;
21534         }
21535
21536     },
21537     // private
21538     IsLongEnough: function (pwd, size)
21539     {
21540         return !(pwd == null || isNaN(size) || pwd.length < size);
21541     },
21542     // private
21543     SpansEnoughCharacterSets: function (word, nb)
21544     {
21545         if (!this.IsLongEnough(word, nb))
21546         {
21547             return false;
21548         }
21549
21550         var characterSetChecks = new Array(
21551             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21552             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21553         );
21554         
21555         for (var index = 0; index < word.length; ++index) {
21556             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21557                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21558                     characterSetChecks[nCharSet].fResult = true;
21559                     break;
21560                 }
21561             }
21562         }
21563
21564         var nCharSets = 0;
21565         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21566             if (characterSetChecks[nCharSet].fResult) {
21567                 ++nCharSets;
21568             }
21569         }
21570
21571         if (nCharSets < nb) {
21572             return false;
21573         }
21574         return true;
21575     },
21576     // private
21577     ClientSideStrongPassword: function (pwd)
21578     {
21579         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21580     },
21581     // private
21582     ClientSideMediumPassword: function (pwd)
21583     {
21584         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21585     },
21586     // private
21587     ClientSideWeakPassword: function (pwd)
21588     {
21589         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21590     }
21591           
21592 })//<script type="text/javascript">
21593
21594 /*
21595  * Based  Ext JS Library 1.1.1
21596  * Copyright(c) 2006-2007, Ext JS, LLC.
21597  * LGPL
21598  *
21599  */
21600  
21601 /**
21602  * @class Roo.HtmlEditorCore
21603  * @extends Roo.Component
21604  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21605  *
21606  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21607  */
21608
21609 Roo.HtmlEditorCore = function(config){
21610     
21611     
21612     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21613     
21614     
21615     this.addEvents({
21616         /**
21617          * @event initialize
21618          * Fires when the editor is fully initialized (including the iframe)
21619          * @param {Roo.HtmlEditorCore} this
21620          */
21621         initialize: true,
21622         /**
21623          * @event activate
21624          * Fires when the editor is first receives the focus. Any insertion must wait
21625          * until after this event.
21626          * @param {Roo.HtmlEditorCore} this
21627          */
21628         activate: true,
21629          /**
21630          * @event beforesync
21631          * Fires before the textarea is updated with content from the editor iframe. Return false
21632          * to cancel the sync.
21633          * @param {Roo.HtmlEditorCore} this
21634          * @param {String} html
21635          */
21636         beforesync: true,
21637          /**
21638          * @event beforepush
21639          * Fires before the iframe editor is updated with content from the textarea. Return false
21640          * to cancel the push.
21641          * @param {Roo.HtmlEditorCore} this
21642          * @param {String} html
21643          */
21644         beforepush: true,
21645          /**
21646          * @event sync
21647          * Fires when the textarea is updated with content from the editor iframe.
21648          * @param {Roo.HtmlEditorCore} this
21649          * @param {String} html
21650          */
21651         sync: true,
21652          /**
21653          * @event push
21654          * Fires when the iframe editor is updated with content from the textarea.
21655          * @param {Roo.HtmlEditorCore} this
21656          * @param {String} html
21657          */
21658         push: true,
21659         
21660         /**
21661          * @event editorevent
21662          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21663          * @param {Roo.HtmlEditorCore} this
21664          */
21665         editorevent: true
21666         
21667     });
21668     
21669     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21670     
21671     // defaults : white / black...
21672     this.applyBlacklists();
21673     
21674     
21675     
21676 };
21677
21678
21679 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21680
21681
21682      /**
21683      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21684      */
21685     
21686     owner : false,
21687     
21688      /**
21689      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21690      *                        Roo.resizable.
21691      */
21692     resizable : false,
21693      /**
21694      * @cfg {Number} height (in pixels)
21695      */   
21696     height: 300,
21697    /**
21698      * @cfg {Number} width (in pixels)
21699      */   
21700     width: 500,
21701     
21702     /**
21703      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21704      * 
21705      */
21706     stylesheets: false,
21707     
21708     // id of frame..
21709     frameId: false,
21710     
21711     // private properties
21712     validationEvent : false,
21713     deferHeight: true,
21714     initialized : false,
21715     activated : false,
21716     sourceEditMode : false,
21717     onFocus : Roo.emptyFn,
21718     iframePad:3,
21719     hideMode:'offsets',
21720     
21721     clearUp: true,
21722     
21723     // blacklist + whitelisted elements..
21724     black: false,
21725     white: false,
21726      
21727     bodyCls : '',
21728
21729     /**
21730      * Protected method that will not generally be called directly. It
21731      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21732      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21733      */
21734     getDocMarkup : function(){
21735         // body styles..
21736         var st = '';
21737         
21738         // inherit styels from page...?? 
21739         if (this.stylesheets === false) {
21740             
21741             Roo.get(document.head).select('style').each(function(node) {
21742                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21743             });
21744             
21745             Roo.get(document.head).select('link').each(function(node) { 
21746                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21747             });
21748             
21749         } else if (!this.stylesheets.length) {
21750                 // simple..
21751                 st = '<style type="text/css">' +
21752                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21753                    '</style>';
21754         } else { 
21755             st = '<style type="text/css">' +
21756                     this.stylesheets +
21757                 '</style>';
21758         }
21759         
21760         st +=  '<style type="text/css">' +
21761             'IMG { cursor: pointer } ' +
21762         '</style>';
21763
21764         var cls = 'roo-htmleditor-body';
21765         
21766         if(this.bodyCls.length){
21767             cls += ' ' + this.bodyCls;
21768         }
21769         
21770         return '<html><head>' + st  +
21771             //<style type="text/css">' +
21772             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21773             //'</style>' +
21774             ' </head><body class="' +  cls + '"></body></html>';
21775     },
21776
21777     // private
21778     onRender : function(ct, position)
21779     {
21780         var _t = this;
21781         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21782         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21783         
21784         
21785         this.el.dom.style.border = '0 none';
21786         this.el.dom.setAttribute('tabIndex', -1);
21787         this.el.addClass('x-hidden hide');
21788         
21789         
21790         
21791         if(Roo.isIE){ // fix IE 1px bogus margin
21792             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21793         }
21794        
21795         
21796         this.frameId = Roo.id();
21797         
21798          
21799         
21800         var iframe = this.owner.wrap.createChild({
21801             tag: 'iframe',
21802             cls: 'form-control', // bootstrap..
21803             id: this.frameId,
21804             name: this.frameId,
21805             frameBorder : 'no',
21806             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21807         }, this.el
21808         );
21809         
21810         
21811         this.iframe = iframe.dom;
21812
21813          this.assignDocWin();
21814         
21815         this.doc.designMode = 'on';
21816        
21817         this.doc.open();
21818         this.doc.write(this.getDocMarkup());
21819         this.doc.close();
21820
21821         
21822         var task = { // must defer to wait for browser to be ready
21823             run : function(){
21824                 //console.log("run task?" + this.doc.readyState);
21825                 this.assignDocWin();
21826                 if(this.doc.body || this.doc.readyState == 'complete'){
21827                     try {
21828                         this.doc.designMode="on";
21829                     } catch (e) {
21830                         return;
21831                     }
21832                     Roo.TaskMgr.stop(task);
21833                     this.initEditor.defer(10, this);
21834                 }
21835             },
21836             interval : 10,
21837             duration: 10000,
21838             scope: this
21839         };
21840         Roo.TaskMgr.start(task);
21841
21842     },
21843
21844     // private
21845     onResize : function(w, h)
21846     {
21847          Roo.log('resize: ' +w + ',' + h );
21848         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21849         if(!this.iframe){
21850             return;
21851         }
21852         if(typeof w == 'number'){
21853             
21854             this.iframe.style.width = w + 'px';
21855         }
21856         if(typeof h == 'number'){
21857             
21858             this.iframe.style.height = h + 'px';
21859             if(this.doc){
21860                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21861             }
21862         }
21863         
21864     },
21865
21866     /**
21867      * Toggles the editor between standard and source edit mode.
21868      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21869      */
21870     toggleSourceEdit : function(sourceEditMode){
21871         
21872         this.sourceEditMode = sourceEditMode === true;
21873         
21874         if(this.sourceEditMode){
21875  
21876             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21877             
21878         }else{
21879             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21880             //this.iframe.className = '';
21881             this.deferFocus();
21882         }
21883         //this.setSize(this.owner.wrap.getSize());
21884         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21885     },
21886
21887     
21888   
21889
21890     /**
21891      * Protected method that will not generally be called directly. If you need/want
21892      * custom HTML cleanup, this is the method you should override.
21893      * @param {String} html The HTML to be cleaned
21894      * return {String} The cleaned HTML
21895      */
21896     cleanHtml : function(html){
21897         html = String(html);
21898         if(html.length > 5){
21899             if(Roo.isSafari){ // strip safari nonsense
21900                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21901             }
21902         }
21903         if(html == '&nbsp;'){
21904             html = '';
21905         }
21906         return html;
21907     },
21908
21909     /**
21910      * HTML Editor -> Textarea
21911      * Protected method that will not generally be called directly. Syncs the contents
21912      * of the editor iframe with the textarea.
21913      */
21914     syncValue : function(){
21915         if(this.initialized){
21916             var bd = (this.doc.body || this.doc.documentElement);
21917             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21918             var html = bd.innerHTML;
21919             if(Roo.isSafari){
21920                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21921                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21922                 if(m && m[1]){
21923                     html = '<div style="'+m[0]+'">' + html + '</div>';
21924                 }
21925             }
21926             html = this.cleanHtml(html);
21927             // fix up the special chars.. normaly like back quotes in word...
21928             // however we do not want to do this with chinese..
21929             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21930                 var cc = b.charCodeAt();
21931                 if (
21932                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21933                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21934                     (cc >= 0xf900 && cc < 0xfb00 )
21935                 ) {
21936                         return b;
21937                 }
21938                 return "&#"+cc+";" 
21939             });
21940             if(this.owner.fireEvent('beforesync', this, html) !== false){
21941                 this.el.dom.value = html;
21942                 this.owner.fireEvent('sync', this, html);
21943             }
21944         }
21945     },
21946
21947     /**
21948      * Protected method that will not generally be called directly. Pushes the value of the textarea
21949      * into the iframe editor.
21950      */
21951     pushValue : function(){
21952         if(this.initialized){
21953             var v = this.el.dom.value.trim();
21954             
21955 //            if(v.length < 1){
21956 //                v = '&#160;';
21957 //            }
21958             
21959             if(this.owner.fireEvent('beforepush', this, v) !== false){
21960                 var d = (this.doc.body || this.doc.documentElement);
21961                 d.innerHTML = v;
21962                 this.cleanUpPaste();
21963                 this.el.dom.value = d.innerHTML;
21964                 this.owner.fireEvent('push', this, v);
21965             }
21966         }
21967     },
21968
21969     // private
21970     deferFocus : function(){
21971         this.focus.defer(10, this);
21972     },
21973
21974     // doc'ed in Field
21975     focus : function(){
21976         if(this.win && !this.sourceEditMode){
21977             this.win.focus();
21978         }else{
21979             this.el.focus();
21980         }
21981     },
21982     
21983     assignDocWin: function()
21984     {
21985         var iframe = this.iframe;
21986         
21987          if(Roo.isIE){
21988             this.doc = iframe.contentWindow.document;
21989             this.win = iframe.contentWindow;
21990         } else {
21991 //            if (!Roo.get(this.frameId)) {
21992 //                return;
21993 //            }
21994 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21995 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21996             
21997             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21998                 return;
21999             }
22000             
22001             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22002             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22003         }
22004     },
22005     
22006     // private
22007     initEditor : function(){
22008         //console.log("INIT EDITOR");
22009         this.assignDocWin();
22010         
22011         
22012         
22013         this.doc.designMode="on";
22014         this.doc.open();
22015         this.doc.write(this.getDocMarkup());
22016         this.doc.close();
22017         
22018         var dbody = (this.doc.body || this.doc.documentElement);
22019         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22020         // this copies styles from the containing element into thsi one..
22021         // not sure why we need all of this..
22022         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22023         
22024         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22025         //ss['background-attachment'] = 'fixed'; // w3c
22026         dbody.bgProperties = 'fixed'; // ie
22027         //Roo.DomHelper.applyStyles(dbody, ss);
22028         Roo.EventManager.on(this.doc, {
22029             //'mousedown': this.onEditorEvent,
22030             'mouseup': this.onEditorEvent,
22031             'dblclick': this.onEditorEvent,
22032             'click': this.onEditorEvent,
22033             'keyup': this.onEditorEvent,
22034             buffer:100,
22035             scope: this
22036         });
22037         if(Roo.isGecko){
22038             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22039         }
22040         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22041             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22042         }
22043         this.initialized = true;
22044
22045         this.owner.fireEvent('initialize', this);
22046         this.pushValue();
22047     },
22048
22049     // private
22050     onDestroy : function(){
22051         
22052         
22053         
22054         if(this.rendered){
22055             
22056             //for (var i =0; i < this.toolbars.length;i++) {
22057             //    // fixme - ask toolbars for heights?
22058             //    this.toolbars[i].onDestroy();
22059            // }
22060             
22061             //this.wrap.dom.innerHTML = '';
22062             //this.wrap.remove();
22063         }
22064     },
22065
22066     // private
22067     onFirstFocus : function(){
22068         
22069         this.assignDocWin();
22070         
22071         
22072         this.activated = true;
22073          
22074     
22075         if(Roo.isGecko){ // prevent silly gecko errors
22076             this.win.focus();
22077             var s = this.win.getSelection();
22078             if(!s.focusNode || s.focusNode.nodeType != 3){
22079                 var r = s.getRangeAt(0);
22080                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22081                 r.collapse(true);
22082                 this.deferFocus();
22083             }
22084             try{
22085                 this.execCmd('useCSS', true);
22086                 this.execCmd('styleWithCSS', false);
22087             }catch(e){}
22088         }
22089         this.owner.fireEvent('activate', this);
22090     },
22091
22092     // private
22093     adjustFont: function(btn){
22094         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22095         //if(Roo.isSafari){ // safari
22096         //    adjust *= 2;
22097        // }
22098         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22099         if(Roo.isSafari){ // safari
22100             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22101             v =  (v < 10) ? 10 : v;
22102             v =  (v > 48) ? 48 : v;
22103             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22104             
22105         }
22106         
22107         
22108         v = Math.max(1, v+adjust);
22109         
22110         this.execCmd('FontSize', v  );
22111     },
22112
22113     onEditorEvent : function(e)
22114     {
22115         this.owner.fireEvent('editorevent', this, e);
22116       //  this.updateToolbar();
22117         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22118     },
22119
22120     insertTag : function(tg)
22121     {
22122         // could be a bit smarter... -> wrap the current selected tRoo..
22123         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22124             
22125             range = this.createRange(this.getSelection());
22126             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22127             wrappingNode.appendChild(range.extractContents());
22128             range.insertNode(wrappingNode);
22129
22130             return;
22131             
22132             
22133             
22134         }
22135         this.execCmd("formatblock",   tg);
22136         
22137     },
22138     
22139     insertText : function(txt)
22140     {
22141         
22142         
22143         var range = this.createRange();
22144         range.deleteContents();
22145                //alert(Sender.getAttribute('label'));
22146                
22147         range.insertNode(this.doc.createTextNode(txt));
22148     } ,
22149     
22150      
22151
22152     /**
22153      * Executes a Midas editor command on the editor document and performs necessary focus and
22154      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22155      * @param {String} cmd The Midas command
22156      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22157      */
22158     relayCmd : function(cmd, value){
22159         this.win.focus();
22160         this.execCmd(cmd, value);
22161         this.owner.fireEvent('editorevent', this);
22162         //this.updateToolbar();
22163         this.owner.deferFocus();
22164     },
22165
22166     /**
22167      * Executes a Midas editor command directly on the editor document.
22168      * For visual commands, you should use {@link #relayCmd} instead.
22169      * <b>This should only be called after the editor is initialized.</b>
22170      * @param {String} cmd The Midas command
22171      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22172      */
22173     execCmd : function(cmd, value){
22174         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22175         this.syncValue();
22176     },
22177  
22178  
22179    
22180     /**
22181      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22182      * to insert tRoo.
22183      * @param {String} text | dom node.. 
22184      */
22185     insertAtCursor : function(text)
22186     {
22187         
22188         if(!this.activated){
22189             return;
22190         }
22191         /*
22192         if(Roo.isIE){
22193             this.win.focus();
22194             var r = this.doc.selection.createRange();
22195             if(r){
22196                 r.collapse(true);
22197                 r.pasteHTML(text);
22198                 this.syncValue();
22199                 this.deferFocus();
22200             
22201             }
22202             return;
22203         }
22204         */
22205         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22206             this.win.focus();
22207             
22208             
22209             // from jquery ui (MIT licenced)
22210             var range, node;
22211             var win = this.win;
22212             
22213             if (win.getSelection && win.getSelection().getRangeAt) {
22214                 range = win.getSelection().getRangeAt(0);
22215                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22216                 range.insertNode(node);
22217             } else if (win.document.selection && win.document.selection.createRange) {
22218                 // no firefox support
22219                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22220                 win.document.selection.createRange().pasteHTML(txt);
22221             } else {
22222                 // no firefox support
22223                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22224                 this.execCmd('InsertHTML', txt);
22225             } 
22226             
22227             this.syncValue();
22228             
22229             this.deferFocus();
22230         }
22231     },
22232  // private
22233     mozKeyPress : function(e){
22234         if(e.ctrlKey){
22235             var c = e.getCharCode(), cmd;
22236           
22237             if(c > 0){
22238                 c = String.fromCharCode(c).toLowerCase();
22239                 switch(c){
22240                     case 'b':
22241                         cmd = 'bold';
22242                         break;
22243                     case 'i':
22244                         cmd = 'italic';
22245                         break;
22246                     
22247                     case 'u':
22248                         cmd = 'underline';
22249                         break;
22250                     
22251                     case 'v':
22252                         this.cleanUpPaste.defer(100, this);
22253                         return;
22254                         
22255                 }
22256                 if(cmd){
22257                     this.win.focus();
22258                     this.execCmd(cmd);
22259                     this.deferFocus();
22260                     e.preventDefault();
22261                 }
22262                 
22263             }
22264         }
22265     },
22266
22267     // private
22268     fixKeys : function(){ // load time branching for fastest keydown performance
22269         if(Roo.isIE){
22270             return function(e){
22271                 var k = e.getKey(), r;
22272                 if(k == e.TAB){
22273                     e.stopEvent();
22274                     r = this.doc.selection.createRange();
22275                     if(r){
22276                         r.collapse(true);
22277                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22278                         this.deferFocus();
22279                     }
22280                     return;
22281                 }
22282                 
22283                 if(k == e.ENTER){
22284                     r = this.doc.selection.createRange();
22285                     if(r){
22286                         var target = r.parentElement();
22287                         if(!target || target.tagName.toLowerCase() != 'li'){
22288                             e.stopEvent();
22289                             r.pasteHTML('<br />');
22290                             r.collapse(false);
22291                             r.select();
22292                         }
22293                     }
22294                 }
22295                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22296                     this.cleanUpPaste.defer(100, this);
22297                     return;
22298                 }
22299                 
22300                 
22301             };
22302         }else if(Roo.isOpera){
22303             return function(e){
22304                 var k = e.getKey();
22305                 if(k == e.TAB){
22306                     e.stopEvent();
22307                     this.win.focus();
22308                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22309                     this.deferFocus();
22310                 }
22311                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22312                     this.cleanUpPaste.defer(100, this);
22313                     return;
22314                 }
22315                 
22316             };
22317         }else if(Roo.isSafari){
22318             return function(e){
22319                 var k = e.getKey();
22320                 
22321                 if(k == e.TAB){
22322                     e.stopEvent();
22323                     this.execCmd('InsertText','\t');
22324                     this.deferFocus();
22325                     return;
22326                 }
22327                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22328                     this.cleanUpPaste.defer(100, this);
22329                     return;
22330                 }
22331                 
22332              };
22333         }
22334     }(),
22335     
22336     getAllAncestors: function()
22337     {
22338         var p = this.getSelectedNode();
22339         var a = [];
22340         if (!p) {
22341             a.push(p); // push blank onto stack..
22342             p = this.getParentElement();
22343         }
22344         
22345         
22346         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22347             a.push(p);
22348             p = p.parentNode;
22349         }
22350         a.push(this.doc.body);
22351         return a;
22352     },
22353     lastSel : false,
22354     lastSelNode : false,
22355     
22356     
22357     getSelection : function() 
22358     {
22359         this.assignDocWin();
22360         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22361     },
22362     
22363     getSelectedNode: function() 
22364     {
22365         // this may only work on Gecko!!!
22366         
22367         // should we cache this!!!!
22368         
22369         
22370         
22371          
22372         var range = this.createRange(this.getSelection()).cloneRange();
22373         
22374         if (Roo.isIE) {
22375             var parent = range.parentElement();
22376             while (true) {
22377                 var testRange = range.duplicate();
22378                 testRange.moveToElementText(parent);
22379                 if (testRange.inRange(range)) {
22380                     break;
22381                 }
22382                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22383                     break;
22384                 }
22385                 parent = parent.parentElement;
22386             }
22387             return parent;
22388         }
22389         
22390         // is ancestor a text element.
22391         var ac =  range.commonAncestorContainer;
22392         if (ac.nodeType == 3) {
22393             ac = ac.parentNode;
22394         }
22395         
22396         var ar = ac.childNodes;
22397          
22398         var nodes = [];
22399         var other_nodes = [];
22400         var has_other_nodes = false;
22401         for (var i=0;i<ar.length;i++) {
22402             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22403                 continue;
22404             }
22405             // fullly contained node.
22406             
22407             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22408                 nodes.push(ar[i]);
22409                 continue;
22410             }
22411             
22412             // probably selected..
22413             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22414                 other_nodes.push(ar[i]);
22415                 continue;
22416             }
22417             // outer..
22418             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22419                 continue;
22420             }
22421             
22422             
22423             has_other_nodes = true;
22424         }
22425         if (!nodes.length && other_nodes.length) {
22426             nodes= other_nodes;
22427         }
22428         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22429             return false;
22430         }
22431         
22432         return nodes[0];
22433     },
22434     createRange: function(sel)
22435     {
22436         // this has strange effects when using with 
22437         // top toolbar - not sure if it's a great idea.
22438         //this.editor.contentWindow.focus();
22439         if (typeof sel != "undefined") {
22440             try {
22441                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22442             } catch(e) {
22443                 return this.doc.createRange();
22444             }
22445         } else {
22446             return this.doc.createRange();
22447         }
22448     },
22449     getParentElement: function()
22450     {
22451         
22452         this.assignDocWin();
22453         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22454         
22455         var range = this.createRange(sel);
22456          
22457         try {
22458             var p = range.commonAncestorContainer;
22459             while (p.nodeType == 3) { // text node
22460                 p = p.parentNode;
22461             }
22462             return p;
22463         } catch (e) {
22464             return null;
22465         }
22466     
22467     },
22468     /***
22469      *
22470      * Range intersection.. the hard stuff...
22471      *  '-1' = before
22472      *  '0' = hits..
22473      *  '1' = after.
22474      *         [ -- selected range --- ]
22475      *   [fail]                        [fail]
22476      *
22477      *    basically..
22478      *      if end is before start or  hits it. fail.
22479      *      if start is after end or hits it fail.
22480      *
22481      *   if either hits (but other is outside. - then it's not 
22482      *   
22483      *    
22484      **/
22485     
22486     
22487     // @see http://www.thismuchiknow.co.uk/?p=64.
22488     rangeIntersectsNode : function(range, node)
22489     {
22490         var nodeRange = node.ownerDocument.createRange();
22491         try {
22492             nodeRange.selectNode(node);
22493         } catch (e) {
22494             nodeRange.selectNodeContents(node);
22495         }
22496     
22497         var rangeStartRange = range.cloneRange();
22498         rangeStartRange.collapse(true);
22499     
22500         var rangeEndRange = range.cloneRange();
22501         rangeEndRange.collapse(false);
22502     
22503         var nodeStartRange = nodeRange.cloneRange();
22504         nodeStartRange.collapse(true);
22505     
22506         var nodeEndRange = nodeRange.cloneRange();
22507         nodeEndRange.collapse(false);
22508     
22509         return rangeStartRange.compareBoundaryPoints(
22510                  Range.START_TO_START, nodeEndRange) == -1 &&
22511                rangeEndRange.compareBoundaryPoints(
22512                  Range.START_TO_START, nodeStartRange) == 1;
22513         
22514          
22515     },
22516     rangeCompareNode : function(range, node)
22517     {
22518         var nodeRange = node.ownerDocument.createRange();
22519         try {
22520             nodeRange.selectNode(node);
22521         } catch (e) {
22522             nodeRange.selectNodeContents(node);
22523         }
22524         
22525         
22526         range.collapse(true);
22527     
22528         nodeRange.collapse(true);
22529      
22530         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22531         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22532          
22533         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22534         
22535         var nodeIsBefore   =  ss == 1;
22536         var nodeIsAfter    = ee == -1;
22537         
22538         if (nodeIsBefore && nodeIsAfter) {
22539             return 0; // outer
22540         }
22541         if (!nodeIsBefore && nodeIsAfter) {
22542             return 1; //right trailed.
22543         }
22544         
22545         if (nodeIsBefore && !nodeIsAfter) {
22546             return 2;  // left trailed.
22547         }
22548         // fully contined.
22549         return 3;
22550     },
22551
22552     // private? - in a new class?
22553     cleanUpPaste :  function()
22554     {
22555         // cleans up the whole document..
22556         Roo.log('cleanuppaste');
22557         
22558         this.cleanUpChildren(this.doc.body);
22559         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22560         if (clean != this.doc.body.innerHTML) {
22561             this.doc.body.innerHTML = clean;
22562         }
22563         
22564     },
22565     
22566     cleanWordChars : function(input) {// change the chars to hex code
22567         var he = Roo.HtmlEditorCore;
22568         
22569         var output = input;
22570         Roo.each(he.swapCodes, function(sw) { 
22571             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22572             
22573             output = output.replace(swapper, sw[1]);
22574         });
22575         
22576         return output;
22577     },
22578     
22579     
22580     cleanUpChildren : function (n)
22581     {
22582         if (!n.childNodes.length) {
22583             return;
22584         }
22585         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22586            this.cleanUpChild(n.childNodes[i]);
22587         }
22588     },
22589     
22590     
22591         
22592     
22593     cleanUpChild : function (node)
22594     {
22595         var ed = this;
22596         //console.log(node);
22597         if (node.nodeName == "#text") {
22598             // clean up silly Windows -- stuff?
22599             return; 
22600         }
22601         if (node.nodeName == "#comment") {
22602             node.parentNode.removeChild(node);
22603             // clean up silly Windows -- stuff?
22604             return; 
22605         }
22606         var lcname = node.tagName.toLowerCase();
22607         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22608         // whitelist of tags..
22609         
22610         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22611             // remove node.
22612             node.parentNode.removeChild(node);
22613             return;
22614             
22615         }
22616         
22617         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22618         
22619         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22620         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22621         
22622         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22623         //    remove_keep_children = true;
22624         //}
22625         
22626         if (remove_keep_children) {
22627             this.cleanUpChildren(node);
22628             // inserts everything just before this node...
22629             while (node.childNodes.length) {
22630                 var cn = node.childNodes[0];
22631                 node.removeChild(cn);
22632                 node.parentNode.insertBefore(cn, node);
22633             }
22634             node.parentNode.removeChild(node);
22635             return;
22636         }
22637         
22638         if (!node.attributes || !node.attributes.length) {
22639             this.cleanUpChildren(node);
22640             return;
22641         }
22642         
22643         function cleanAttr(n,v)
22644         {
22645             
22646             if (v.match(/^\./) || v.match(/^\//)) {
22647                 return;
22648             }
22649             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22650                 return;
22651             }
22652             if (v.match(/^#/)) {
22653                 return;
22654             }
22655 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22656             node.removeAttribute(n);
22657             
22658         }
22659         
22660         var cwhite = this.cwhite;
22661         var cblack = this.cblack;
22662             
22663         function cleanStyle(n,v)
22664         {
22665             if (v.match(/expression/)) { //XSS?? should we even bother..
22666                 node.removeAttribute(n);
22667                 return;
22668             }
22669             
22670             var parts = v.split(/;/);
22671             var clean = [];
22672             
22673             Roo.each(parts, function(p) {
22674                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22675                 if (!p.length) {
22676                     return true;
22677                 }
22678                 var l = p.split(':').shift().replace(/\s+/g,'');
22679                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22680                 
22681                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22682 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22683                     //node.removeAttribute(n);
22684                     return true;
22685                 }
22686                 //Roo.log()
22687                 // only allow 'c whitelisted system attributes'
22688                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22689 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22690                     //node.removeAttribute(n);
22691                     return true;
22692                 }
22693                 
22694                 
22695                  
22696                 
22697                 clean.push(p);
22698                 return true;
22699             });
22700             if (clean.length) { 
22701                 node.setAttribute(n, clean.join(';'));
22702             } else {
22703                 node.removeAttribute(n);
22704             }
22705             
22706         }
22707         
22708         
22709         for (var i = node.attributes.length-1; i > -1 ; i--) {
22710             var a = node.attributes[i];
22711             //console.log(a);
22712             
22713             if (a.name.toLowerCase().substr(0,2)=='on')  {
22714                 node.removeAttribute(a.name);
22715                 continue;
22716             }
22717             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22718                 node.removeAttribute(a.name);
22719                 continue;
22720             }
22721             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22722                 cleanAttr(a.name,a.value); // fixme..
22723                 continue;
22724             }
22725             if (a.name == 'style') {
22726                 cleanStyle(a.name,a.value);
22727                 continue;
22728             }
22729             /// clean up MS crap..
22730             // tecnically this should be a list of valid class'es..
22731             
22732             
22733             if (a.name == 'class') {
22734                 if (a.value.match(/^Mso/)) {
22735                     node.className = '';
22736                 }
22737                 
22738                 if (a.value.match(/^body$/)) {
22739                     node.className = '';
22740                 }
22741                 continue;
22742             }
22743             
22744             // style cleanup!?
22745             // class cleanup?
22746             
22747         }
22748         
22749         
22750         this.cleanUpChildren(node);
22751         
22752         
22753     },
22754     
22755     /**
22756      * Clean up MS wordisms...
22757      */
22758     cleanWord : function(node)
22759     {
22760         
22761         
22762         if (!node) {
22763             this.cleanWord(this.doc.body);
22764             return;
22765         }
22766         if (node.nodeName == "#text") {
22767             // clean up silly Windows -- stuff?
22768             return; 
22769         }
22770         if (node.nodeName == "#comment") {
22771             node.parentNode.removeChild(node);
22772             // clean up silly Windows -- stuff?
22773             return; 
22774         }
22775         
22776         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22777             node.parentNode.removeChild(node);
22778             return;
22779         }
22780         
22781         // remove - but keep children..
22782         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22783             while (node.childNodes.length) {
22784                 var cn = node.childNodes[0];
22785                 node.removeChild(cn);
22786                 node.parentNode.insertBefore(cn, node);
22787             }
22788             node.parentNode.removeChild(node);
22789             this.iterateChildren(node, this.cleanWord);
22790             return;
22791         }
22792         // clean styles
22793         if (node.className.length) {
22794             
22795             var cn = node.className.split(/\W+/);
22796             var cna = [];
22797             Roo.each(cn, function(cls) {
22798                 if (cls.match(/Mso[a-zA-Z]+/)) {
22799                     return;
22800                 }
22801                 cna.push(cls);
22802             });
22803             node.className = cna.length ? cna.join(' ') : '';
22804             if (!cna.length) {
22805                 node.removeAttribute("class");
22806             }
22807         }
22808         
22809         if (node.hasAttribute("lang")) {
22810             node.removeAttribute("lang");
22811         }
22812         
22813         if (node.hasAttribute("style")) {
22814             
22815             var styles = node.getAttribute("style").split(";");
22816             var nstyle = [];
22817             Roo.each(styles, function(s) {
22818                 if (!s.match(/:/)) {
22819                     return;
22820                 }
22821                 var kv = s.split(":");
22822                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22823                     return;
22824                 }
22825                 // what ever is left... we allow.
22826                 nstyle.push(s);
22827             });
22828             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22829             if (!nstyle.length) {
22830                 node.removeAttribute('style');
22831             }
22832         }
22833         this.iterateChildren(node, this.cleanWord);
22834         
22835         
22836         
22837     },
22838     /**
22839      * iterateChildren of a Node, calling fn each time, using this as the scole..
22840      * @param {DomNode} node node to iterate children of.
22841      * @param {Function} fn method of this class to call on each item.
22842      */
22843     iterateChildren : function(node, fn)
22844     {
22845         if (!node.childNodes.length) {
22846                 return;
22847         }
22848         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22849            fn.call(this, node.childNodes[i])
22850         }
22851     },
22852     
22853     
22854     /**
22855      * cleanTableWidths.
22856      *
22857      * Quite often pasting from word etc.. results in tables with column and widths.
22858      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22859      *
22860      */
22861     cleanTableWidths : function(node)
22862     {
22863          
22864          
22865         if (!node) {
22866             this.cleanTableWidths(this.doc.body);
22867             return;
22868         }
22869         
22870         // ignore list...
22871         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22872             return; 
22873         }
22874         Roo.log(node.tagName);
22875         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22876             this.iterateChildren(node, this.cleanTableWidths);
22877             return;
22878         }
22879         if (node.hasAttribute('width')) {
22880             node.removeAttribute('width');
22881         }
22882         
22883          
22884         if (node.hasAttribute("style")) {
22885             // pretty basic...
22886             
22887             var styles = node.getAttribute("style").split(";");
22888             var nstyle = [];
22889             Roo.each(styles, function(s) {
22890                 if (!s.match(/:/)) {
22891                     return;
22892                 }
22893                 var kv = s.split(":");
22894                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22895                     return;
22896                 }
22897                 // what ever is left... we allow.
22898                 nstyle.push(s);
22899             });
22900             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22901             if (!nstyle.length) {
22902                 node.removeAttribute('style');
22903             }
22904         }
22905         
22906         this.iterateChildren(node, this.cleanTableWidths);
22907         
22908         
22909     },
22910     
22911     
22912     
22913     
22914     domToHTML : function(currentElement, depth, nopadtext) {
22915         
22916         depth = depth || 0;
22917         nopadtext = nopadtext || false;
22918     
22919         if (!currentElement) {
22920             return this.domToHTML(this.doc.body);
22921         }
22922         
22923         //Roo.log(currentElement);
22924         var j;
22925         var allText = false;
22926         var nodeName = currentElement.nodeName;
22927         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22928         
22929         if  (nodeName == '#text') {
22930             
22931             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22932         }
22933         
22934         
22935         var ret = '';
22936         if (nodeName != 'BODY') {
22937              
22938             var i = 0;
22939             // Prints the node tagName, such as <A>, <IMG>, etc
22940             if (tagName) {
22941                 var attr = [];
22942                 for(i = 0; i < currentElement.attributes.length;i++) {
22943                     // quoting?
22944                     var aname = currentElement.attributes.item(i).name;
22945                     if (!currentElement.attributes.item(i).value.length) {
22946                         continue;
22947                     }
22948                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22949                 }
22950                 
22951                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22952             } 
22953             else {
22954                 
22955                 // eack
22956             }
22957         } else {
22958             tagName = false;
22959         }
22960         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22961             return ret;
22962         }
22963         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22964             nopadtext = true;
22965         }
22966         
22967         
22968         // Traverse the tree
22969         i = 0;
22970         var currentElementChild = currentElement.childNodes.item(i);
22971         var allText = true;
22972         var innerHTML  = '';
22973         lastnode = '';
22974         while (currentElementChild) {
22975             // Formatting code (indent the tree so it looks nice on the screen)
22976             var nopad = nopadtext;
22977             if (lastnode == 'SPAN') {
22978                 nopad  = true;
22979             }
22980             // text
22981             if  (currentElementChild.nodeName == '#text') {
22982                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22983                 toadd = nopadtext ? toadd : toadd.trim();
22984                 if (!nopad && toadd.length > 80) {
22985                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22986                 }
22987                 innerHTML  += toadd;
22988                 
22989                 i++;
22990                 currentElementChild = currentElement.childNodes.item(i);
22991                 lastNode = '';
22992                 continue;
22993             }
22994             allText = false;
22995             
22996             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22997                 
22998             // Recursively traverse the tree structure of the child node
22999             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23000             lastnode = currentElementChild.nodeName;
23001             i++;
23002             currentElementChild=currentElement.childNodes.item(i);
23003         }
23004         
23005         ret += innerHTML;
23006         
23007         if (!allText) {
23008                 // The remaining code is mostly for formatting the tree
23009             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23010         }
23011         
23012         
23013         if (tagName) {
23014             ret+= "</"+tagName+">";
23015         }
23016         return ret;
23017         
23018     },
23019         
23020     applyBlacklists : function()
23021     {
23022         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23023         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23024         
23025         this.white = [];
23026         this.black = [];
23027         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23028             if (b.indexOf(tag) > -1) {
23029                 return;
23030             }
23031             this.white.push(tag);
23032             
23033         }, this);
23034         
23035         Roo.each(w, function(tag) {
23036             if (b.indexOf(tag) > -1) {
23037                 return;
23038             }
23039             if (this.white.indexOf(tag) > -1) {
23040                 return;
23041             }
23042             this.white.push(tag);
23043             
23044         }, this);
23045         
23046         
23047         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23048             if (w.indexOf(tag) > -1) {
23049                 return;
23050             }
23051             this.black.push(tag);
23052             
23053         }, this);
23054         
23055         Roo.each(b, function(tag) {
23056             if (w.indexOf(tag) > -1) {
23057                 return;
23058             }
23059             if (this.black.indexOf(tag) > -1) {
23060                 return;
23061             }
23062             this.black.push(tag);
23063             
23064         }, this);
23065         
23066         
23067         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23068         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23069         
23070         this.cwhite = [];
23071         this.cblack = [];
23072         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23073             if (b.indexOf(tag) > -1) {
23074                 return;
23075             }
23076             this.cwhite.push(tag);
23077             
23078         }, this);
23079         
23080         Roo.each(w, function(tag) {
23081             if (b.indexOf(tag) > -1) {
23082                 return;
23083             }
23084             if (this.cwhite.indexOf(tag) > -1) {
23085                 return;
23086             }
23087             this.cwhite.push(tag);
23088             
23089         }, this);
23090         
23091         
23092         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23093             if (w.indexOf(tag) > -1) {
23094                 return;
23095             }
23096             this.cblack.push(tag);
23097             
23098         }, this);
23099         
23100         Roo.each(b, function(tag) {
23101             if (w.indexOf(tag) > -1) {
23102                 return;
23103             }
23104             if (this.cblack.indexOf(tag) > -1) {
23105                 return;
23106             }
23107             this.cblack.push(tag);
23108             
23109         }, this);
23110     },
23111     
23112     setStylesheets : function(stylesheets)
23113     {
23114         if(typeof(stylesheets) == 'string'){
23115             Roo.get(this.iframe.contentDocument.head).createChild({
23116                 tag : 'link',
23117                 rel : 'stylesheet',
23118                 type : 'text/css',
23119                 href : stylesheets
23120             });
23121             
23122             return;
23123         }
23124         var _this = this;
23125      
23126         Roo.each(stylesheets, function(s) {
23127             if(!s.length){
23128                 return;
23129             }
23130             
23131             Roo.get(_this.iframe.contentDocument.head).createChild({
23132                 tag : 'link',
23133                 rel : 'stylesheet',
23134                 type : 'text/css',
23135                 href : s
23136             });
23137         });
23138
23139         
23140     },
23141     
23142     removeStylesheets : function()
23143     {
23144         var _this = this;
23145         
23146         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23147             s.remove();
23148         });
23149     },
23150     
23151     setStyle : function(style)
23152     {
23153         Roo.get(this.iframe.contentDocument.head).createChild({
23154             tag : 'style',
23155             type : 'text/css',
23156             html : style
23157         });
23158
23159         return;
23160     }
23161     
23162     // hide stuff that is not compatible
23163     /**
23164      * @event blur
23165      * @hide
23166      */
23167     /**
23168      * @event change
23169      * @hide
23170      */
23171     /**
23172      * @event focus
23173      * @hide
23174      */
23175     /**
23176      * @event specialkey
23177      * @hide
23178      */
23179     /**
23180      * @cfg {String} fieldClass @hide
23181      */
23182     /**
23183      * @cfg {String} focusClass @hide
23184      */
23185     /**
23186      * @cfg {String} autoCreate @hide
23187      */
23188     /**
23189      * @cfg {String} inputType @hide
23190      */
23191     /**
23192      * @cfg {String} invalidClass @hide
23193      */
23194     /**
23195      * @cfg {String} invalidText @hide
23196      */
23197     /**
23198      * @cfg {String} msgFx @hide
23199      */
23200     /**
23201      * @cfg {String} validateOnBlur @hide
23202      */
23203 });
23204
23205 Roo.HtmlEditorCore.white = [
23206         'area', 'br', 'img', 'input', 'hr', 'wbr',
23207         
23208        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23209        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23210        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23211        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23212        'table',   'ul',         'xmp', 
23213        
23214        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23215       'thead',   'tr', 
23216      
23217       'dir', 'menu', 'ol', 'ul', 'dl',
23218        
23219       'embed',  'object'
23220 ];
23221
23222
23223 Roo.HtmlEditorCore.black = [
23224     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23225         'applet', // 
23226         'base',   'basefont', 'bgsound', 'blink',  'body', 
23227         'frame',  'frameset', 'head',    'html',   'ilayer', 
23228         'iframe', 'layer',  'link',     'meta',    'object',   
23229         'script', 'style' ,'title',  'xml' // clean later..
23230 ];
23231 Roo.HtmlEditorCore.clean = [
23232     'script', 'style', 'title', 'xml'
23233 ];
23234 Roo.HtmlEditorCore.remove = [
23235     'font'
23236 ];
23237 // attributes..
23238
23239 Roo.HtmlEditorCore.ablack = [
23240     'on'
23241 ];
23242     
23243 Roo.HtmlEditorCore.aclean = [ 
23244     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23245 ];
23246
23247 // protocols..
23248 Roo.HtmlEditorCore.pwhite= [
23249         'http',  'https',  'mailto'
23250 ];
23251
23252 // white listed style attributes.
23253 Roo.HtmlEditorCore.cwhite= [
23254       //  'text-align', /// default is to allow most things..
23255       
23256          
23257 //        'font-size'//??
23258 ];
23259
23260 // black listed style attributes.
23261 Roo.HtmlEditorCore.cblack= [
23262       //  'font-size' -- this can be set by the project 
23263 ];
23264
23265
23266 Roo.HtmlEditorCore.swapCodes   =[ 
23267     [    8211, "--" ], 
23268     [    8212, "--" ], 
23269     [    8216,  "'" ],  
23270     [    8217, "'" ],  
23271     [    8220, '"' ],  
23272     [    8221, '"' ],  
23273     [    8226, "*" ],  
23274     [    8230, "..." ]
23275 ]; 
23276
23277     /*
23278  * - LGPL
23279  *
23280  * HtmlEditor
23281  * 
23282  */
23283
23284 /**
23285  * @class Roo.bootstrap.HtmlEditor
23286  * @extends Roo.bootstrap.TextArea
23287  * Bootstrap HtmlEditor class
23288
23289  * @constructor
23290  * Create a new HtmlEditor
23291  * @param {Object} config The config object
23292  */
23293
23294 Roo.bootstrap.HtmlEditor = function(config){
23295     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23296     if (!this.toolbars) {
23297         this.toolbars = [];
23298     }
23299     
23300     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23301     this.addEvents({
23302             /**
23303              * @event initialize
23304              * Fires when the editor is fully initialized (including the iframe)
23305              * @param {HtmlEditor} this
23306              */
23307             initialize: true,
23308             /**
23309              * @event activate
23310              * Fires when the editor is first receives the focus. Any insertion must wait
23311              * until after this event.
23312              * @param {HtmlEditor} this
23313              */
23314             activate: true,
23315              /**
23316              * @event beforesync
23317              * Fires before the textarea is updated with content from the editor iframe. Return false
23318              * to cancel the sync.
23319              * @param {HtmlEditor} this
23320              * @param {String} html
23321              */
23322             beforesync: true,
23323              /**
23324              * @event beforepush
23325              * Fires before the iframe editor is updated with content from the textarea. Return false
23326              * to cancel the push.
23327              * @param {HtmlEditor} this
23328              * @param {String} html
23329              */
23330             beforepush: true,
23331              /**
23332              * @event sync
23333              * Fires when the textarea is updated with content from the editor iframe.
23334              * @param {HtmlEditor} this
23335              * @param {String} html
23336              */
23337             sync: true,
23338              /**
23339              * @event push
23340              * Fires when the iframe editor is updated with content from the textarea.
23341              * @param {HtmlEditor} this
23342              * @param {String} html
23343              */
23344             push: true,
23345              /**
23346              * @event editmodechange
23347              * Fires when the editor switches edit modes
23348              * @param {HtmlEditor} this
23349              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23350              */
23351             editmodechange: true,
23352             /**
23353              * @event editorevent
23354              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23355              * @param {HtmlEditor} this
23356              */
23357             editorevent: true,
23358             /**
23359              * @event firstfocus
23360              * Fires when on first focus - needed by toolbars..
23361              * @param {HtmlEditor} this
23362              */
23363             firstfocus: true,
23364             /**
23365              * @event autosave
23366              * Auto save the htmlEditor value as a file into Events
23367              * @param {HtmlEditor} this
23368              */
23369             autosave: true,
23370             /**
23371              * @event savedpreview
23372              * preview the saved version of htmlEditor
23373              * @param {HtmlEditor} this
23374              */
23375             savedpreview: true
23376         });
23377 };
23378
23379
23380 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23381     
23382     
23383       /**
23384      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23385      */
23386     toolbars : false,
23387     
23388      /**
23389     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23390     */
23391     btns : [],
23392    
23393      /**
23394      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23395      *                        Roo.resizable.
23396      */
23397     resizable : false,
23398      /**
23399      * @cfg {Number} height (in pixels)
23400      */   
23401     height: 300,
23402    /**
23403      * @cfg {Number} width (in pixels)
23404      */   
23405     width: false,
23406     
23407     /**
23408      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23409      * 
23410      */
23411     stylesheets: false,
23412     
23413     // id of frame..
23414     frameId: false,
23415     
23416     // private properties
23417     validationEvent : false,
23418     deferHeight: true,
23419     initialized : false,
23420     activated : false,
23421     
23422     onFocus : Roo.emptyFn,
23423     iframePad:3,
23424     hideMode:'offsets',
23425     
23426     tbContainer : false,
23427     
23428     bodyCls : '',
23429     
23430     toolbarContainer :function() {
23431         return this.wrap.select('.x-html-editor-tb',true).first();
23432     },
23433
23434     /**
23435      * Protected method that will not generally be called directly. It
23436      * is called when the editor creates its toolbar. Override this method if you need to
23437      * add custom toolbar buttons.
23438      * @param {HtmlEditor} editor
23439      */
23440     createToolbar : function(){
23441         Roo.log('renewing');
23442         Roo.log("create toolbars");
23443         
23444         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23445         this.toolbars[0].render(this.toolbarContainer());
23446         
23447         return;
23448         
23449 //        if (!editor.toolbars || !editor.toolbars.length) {
23450 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23451 //        }
23452 //        
23453 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23454 //            editor.toolbars[i] = Roo.factory(
23455 //                    typeof(editor.toolbars[i]) == 'string' ?
23456 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23457 //                Roo.bootstrap.HtmlEditor);
23458 //            editor.toolbars[i].init(editor);
23459 //        }
23460     },
23461
23462      
23463     // private
23464     onRender : function(ct, position)
23465     {
23466        // Roo.log("Call onRender: " + this.xtype);
23467         var _t = this;
23468         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23469       
23470         this.wrap = this.inputEl().wrap({
23471             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23472         });
23473         
23474         this.editorcore.onRender(ct, position);
23475          
23476         if (this.resizable) {
23477             this.resizeEl = new Roo.Resizable(this.wrap, {
23478                 pinned : true,
23479                 wrap: true,
23480                 dynamic : true,
23481                 minHeight : this.height,
23482                 height: this.height,
23483                 handles : this.resizable,
23484                 width: this.width,
23485                 listeners : {
23486                     resize : function(r, w, h) {
23487                         _t.onResize(w,h); // -something
23488                     }
23489                 }
23490             });
23491             
23492         }
23493         this.createToolbar(this);
23494        
23495         
23496         if(!this.width && this.resizable){
23497             this.setSize(this.wrap.getSize());
23498         }
23499         if (this.resizeEl) {
23500             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23501             // should trigger onReize..
23502         }
23503         
23504     },
23505
23506     // private
23507     onResize : function(w, h)
23508     {
23509         Roo.log('resize: ' +w + ',' + h );
23510         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23511         var ew = false;
23512         var eh = false;
23513         
23514         if(this.inputEl() ){
23515             if(typeof w == 'number'){
23516                 var aw = w - this.wrap.getFrameWidth('lr');
23517                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23518                 ew = aw;
23519             }
23520             if(typeof h == 'number'){
23521                  var tbh = -11;  // fixme it needs to tool bar size!
23522                 for (var i =0; i < this.toolbars.length;i++) {
23523                     // fixme - ask toolbars for heights?
23524                     tbh += this.toolbars[i].el.getHeight();
23525                     //if (this.toolbars[i].footer) {
23526                     //    tbh += this.toolbars[i].footer.el.getHeight();
23527                     //}
23528                 }
23529               
23530                 
23531                 
23532                 
23533                 
23534                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23535                 ah -= 5; // knock a few pixes off for look..
23536                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23537                 var eh = ah;
23538             }
23539         }
23540         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23541         this.editorcore.onResize(ew,eh);
23542         
23543     },
23544
23545     /**
23546      * Toggles the editor between standard and source edit mode.
23547      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23548      */
23549     toggleSourceEdit : function(sourceEditMode)
23550     {
23551         this.editorcore.toggleSourceEdit(sourceEditMode);
23552         
23553         if(this.editorcore.sourceEditMode){
23554             Roo.log('editor - showing textarea');
23555             
23556 //            Roo.log('in');
23557 //            Roo.log(this.syncValue());
23558             this.syncValue();
23559             this.inputEl().removeClass(['hide', 'x-hidden']);
23560             this.inputEl().dom.removeAttribute('tabIndex');
23561             this.inputEl().focus();
23562         }else{
23563             Roo.log('editor - hiding textarea');
23564 //            Roo.log('out')
23565 //            Roo.log(this.pushValue()); 
23566             this.pushValue();
23567             
23568             this.inputEl().addClass(['hide', 'x-hidden']);
23569             this.inputEl().dom.setAttribute('tabIndex', -1);
23570             //this.deferFocus();
23571         }
23572          
23573         if(this.resizable){
23574             this.setSize(this.wrap.getSize());
23575         }
23576         
23577         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23578     },
23579  
23580     // private (for BoxComponent)
23581     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23582
23583     // private (for BoxComponent)
23584     getResizeEl : function(){
23585         return this.wrap;
23586     },
23587
23588     // private (for BoxComponent)
23589     getPositionEl : function(){
23590         return this.wrap;
23591     },
23592
23593     // private
23594     initEvents : function(){
23595         this.originalValue = this.getValue();
23596     },
23597
23598 //    /**
23599 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23600 //     * @method
23601 //     */
23602 //    markInvalid : Roo.emptyFn,
23603 //    /**
23604 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23605 //     * @method
23606 //     */
23607 //    clearInvalid : Roo.emptyFn,
23608
23609     setValue : function(v){
23610         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23611         this.editorcore.pushValue();
23612     },
23613
23614      
23615     // private
23616     deferFocus : function(){
23617         this.focus.defer(10, this);
23618     },
23619
23620     // doc'ed in Field
23621     focus : function(){
23622         this.editorcore.focus();
23623         
23624     },
23625       
23626
23627     // private
23628     onDestroy : function(){
23629         
23630         
23631         
23632         if(this.rendered){
23633             
23634             for (var i =0; i < this.toolbars.length;i++) {
23635                 // fixme - ask toolbars for heights?
23636                 this.toolbars[i].onDestroy();
23637             }
23638             
23639             this.wrap.dom.innerHTML = '';
23640             this.wrap.remove();
23641         }
23642     },
23643
23644     // private
23645     onFirstFocus : function(){
23646         //Roo.log("onFirstFocus");
23647         this.editorcore.onFirstFocus();
23648          for (var i =0; i < this.toolbars.length;i++) {
23649             this.toolbars[i].onFirstFocus();
23650         }
23651         
23652     },
23653     
23654     // private
23655     syncValue : function()
23656     {   
23657         this.editorcore.syncValue();
23658     },
23659     
23660     pushValue : function()
23661     {   
23662         this.editorcore.pushValue();
23663     }
23664      
23665     
23666     // hide stuff that is not compatible
23667     /**
23668      * @event blur
23669      * @hide
23670      */
23671     /**
23672      * @event change
23673      * @hide
23674      */
23675     /**
23676      * @event focus
23677      * @hide
23678      */
23679     /**
23680      * @event specialkey
23681      * @hide
23682      */
23683     /**
23684      * @cfg {String} fieldClass @hide
23685      */
23686     /**
23687      * @cfg {String} focusClass @hide
23688      */
23689     /**
23690      * @cfg {String} autoCreate @hide
23691      */
23692     /**
23693      * @cfg {String} inputType @hide
23694      */
23695     /**
23696      * @cfg {String} invalidClass @hide
23697      */
23698     /**
23699      * @cfg {String} invalidText @hide
23700      */
23701     /**
23702      * @cfg {String} msgFx @hide
23703      */
23704     /**
23705      * @cfg {String} validateOnBlur @hide
23706      */
23707 });
23708  
23709     
23710    
23711    
23712    
23713       
23714 Roo.namespace('Roo.bootstrap.htmleditor');
23715 /**
23716  * @class Roo.bootstrap.HtmlEditorToolbar1
23717  * Basic Toolbar
23718  * 
23719  * Usage:
23720  *
23721  new Roo.bootstrap.HtmlEditor({
23722     ....
23723     toolbars : [
23724         new Roo.bootstrap.HtmlEditorToolbar1({
23725             disable : { fonts: 1 , format: 1, ..., ... , ...],
23726             btns : [ .... ]
23727         })
23728     }
23729      
23730  * 
23731  * @cfg {Object} disable List of elements to disable..
23732  * @cfg {Array} btns List of additional buttons.
23733  * 
23734  * 
23735  * NEEDS Extra CSS? 
23736  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23737  */
23738  
23739 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23740 {
23741     
23742     Roo.apply(this, config);
23743     
23744     // default disabled, based on 'good practice'..
23745     this.disable = this.disable || {};
23746     Roo.applyIf(this.disable, {
23747         fontSize : true,
23748         colors : true,
23749         specialElements : true
23750     });
23751     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23752     
23753     this.editor = config.editor;
23754     this.editorcore = config.editor.editorcore;
23755     
23756     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23757     
23758     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23759     // dont call parent... till later.
23760 }
23761 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23762      
23763     bar : true,
23764     
23765     editor : false,
23766     editorcore : false,
23767     
23768     
23769     formats : [
23770         "p" ,  
23771         "h1","h2","h3","h4","h5","h6", 
23772         "pre", "code", 
23773         "abbr", "acronym", "address", "cite", "samp", "var",
23774         'div','span'
23775     ],
23776     
23777     onRender : function(ct, position)
23778     {
23779        // Roo.log("Call onRender: " + this.xtype);
23780         
23781        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23782        Roo.log(this.el);
23783        this.el.dom.style.marginBottom = '0';
23784        var _this = this;
23785        var editorcore = this.editorcore;
23786        var editor= this.editor;
23787        
23788        var children = [];
23789        var btn = function(id,cmd , toggle, handler, html){
23790        
23791             var  event = toggle ? 'toggle' : 'click';
23792        
23793             var a = {
23794                 size : 'sm',
23795                 xtype: 'Button',
23796                 xns: Roo.bootstrap,
23797                 glyphicon : id,
23798                 cmd : id || cmd,
23799                 enableToggle:toggle !== false,
23800                 html : html || '',
23801                 pressed : toggle ? false : null,
23802                 listeners : {}
23803             };
23804             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23805                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23806             };
23807             children.push(a);
23808             return a;
23809        }
23810        
23811     //    var cb_box = function...
23812         
23813         var style = {
23814                 xtype: 'Button',
23815                 size : 'sm',
23816                 xns: Roo.bootstrap,
23817                 glyphicon : 'font',
23818                 //html : 'submit'
23819                 menu : {
23820                     xtype: 'Menu',
23821                     xns: Roo.bootstrap,
23822                     items:  []
23823                 }
23824         };
23825         Roo.each(this.formats, function(f) {
23826             style.menu.items.push({
23827                 xtype :'MenuItem',
23828                 xns: Roo.bootstrap,
23829                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23830                 tagname : f,
23831                 listeners : {
23832                     click : function()
23833                     {
23834                         editorcore.insertTag(this.tagname);
23835                         editor.focus();
23836                     }
23837                 }
23838                 
23839             });
23840         });
23841         children.push(style);   
23842         
23843         btn('bold',false,true);
23844         btn('italic',false,true);
23845         btn('align-left', 'justifyleft',true);
23846         btn('align-center', 'justifycenter',true);
23847         btn('align-right' , 'justifyright',true);
23848         btn('link', false, false, function(btn) {
23849             //Roo.log("create link?");
23850             var url = prompt(this.createLinkText, this.defaultLinkValue);
23851             if(url && url != 'http:/'+'/'){
23852                 this.editorcore.relayCmd('createlink', url);
23853             }
23854         }),
23855         btn('list','insertunorderedlist',true);
23856         btn('pencil', false,true, function(btn){
23857                 Roo.log(this);
23858                 this.toggleSourceEdit(btn.pressed);
23859         });
23860         
23861         if (this.editor.btns.length > 0) {
23862             for (var i = 0; i<this.editor.btns.length; i++) {
23863                 children.push(this.editor.btns[i]);
23864             }
23865         }
23866         
23867         /*
23868         var cog = {
23869                 xtype: 'Button',
23870                 size : 'sm',
23871                 xns: Roo.bootstrap,
23872                 glyphicon : 'cog',
23873                 //html : 'submit'
23874                 menu : {
23875                     xtype: 'Menu',
23876                     xns: Roo.bootstrap,
23877                     items:  []
23878                 }
23879         };
23880         
23881         cog.menu.items.push({
23882             xtype :'MenuItem',
23883             xns: Roo.bootstrap,
23884             html : Clean styles,
23885             tagname : f,
23886             listeners : {
23887                 click : function()
23888                 {
23889                     editorcore.insertTag(this.tagname);
23890                     editor.focus();
23891                 }
23892             }
23893             
23894         });
23895        */
23896         
23897          
23898        this.xtype = 'NavSimplebar';
23899         
23900         for(var i=0;i< children.length;i++) {
23901             
23902             this.buttons.add(this.addxtypeChild(children[i]));
23903             
23904         }
23905         
23906         editor.on('editorevent', this.updateToolbar, this);
23907     },
23908     onBtnClick : function(id)
23909     {
23910        this.editorcore.relayCmd(id);
23911        this.editorcore.focus();
23912     },
23913     
23914     /**
23915      * Protected method that will not generally be called directly. It triggers
23916      * a toolbar update by reading the markup state of the current selection in the editor.
23917      */
23918     updateToolbar: function(){
23919
23920         if(!this.editorcore.activated){
23921             this.editor.onFirstFocus(); // is this neeed?
23922             return;
23923         }
23924
23925         var btns = this.buttons; 
23926         var doc = this.editorcore.doc;
23927         btns.get('bold').setActive(doc.queryCommandState('bold'));
23928         btns.get('italic').setActive(doc.queryCommandState('italic'));
23929         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23930         
23931         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23932         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23933         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23934         
23935         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23936         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23937          /*
23938         
23939         var ans = this.editorcore.getAllAncestors();
23940         if (this.formatCombo) {
23941             
23942             
23943             var store = this.formatCombo.store;
23944             this.formatCombo.setValue("");
23945             for (var i =0; i < ans.length;i++) {
23946                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23947                     // select it..
23948                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23949                     break;
23950                 }
23951             }
23952         }
23953         
23954         
23955         
23956         // hides menus... - so this cant be on a menu...
23957         Roo.bootstrap.MenuMgr.hideAll();
23958         */
23959         Roo.bootstrap.MenuMgr.hideAll();
23960         //this.editorsyncValue();
23961     },
23962     onFirstFocus: function() {
23963         this.buttons.each(function(item){
23964            item.enable();
23965         });
23966     },
23967     toggleSourceEdit : function(sourceEditMode){
23968         
23969           
23970         if(sourceEditMode){
23971             Roo.log("disabling buttons");
23972            this.buttons.each( function(item){
23973                 if(item.cmd != 'pencil'){
23974                     item.disable();
23975                 }
23976             });
23977           
23978         }else{
23979             Roo.log("enabling buttons");
23980             if(this.editorcore.initialized){
23981                 this.buttons.each( function(item){
23982                     item.enable();
23983                 });
23984             }
23985             
23986         }
23987         Roo.log("calling toggole on editor");
23988         // tell the editor that it's been pressed..
23989         this.editor.toggleSourceEdit(sourceEditMode);
23990        
23991     }
23992 });
23993
23994
23995
23996
23997
23998 /**
23999  * @class Roo.bootstrap.Table.AbstractSelectionModel
24000  * @extends Roo.util.Observable
24001  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24002  * implemented by descendant classes.  This class should not be directly instantiated.
24003  * @constructor
24004  */
24005 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24006     this.locked = false;
24007     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24008 };
24009
24010
24011 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24012     /** @ignore Called by the grid automatically. Do not call directly. */
24013     init : function(grid){
24014         this.grid = grid;
24015         this.initEvents();
24016     },
24017
24018     /**
24019      * Locks the selections.
24020      */
24021     lock : function(){
24022         this.locked = true;
24023     },
24024
24025     /**
24026      * Unlocks the selections.
24027      */
24028     unlock : function(){
24029         this.locked = false;
24030     },
24031
24032     /**
24033      * Returns true if the selections are locked.
24034      * @return {Boolean}
24035      */
24036     isLocked : function(){
24037         return this.locked;
24038     }
24039 });
24040 /**
24041  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24042  * @class Roo.bootstrap.Table.RowSelectionModel
24043  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24044  * It supports multiple selections and keyboard selection/navigation. 
24045  * @constructor
24046  * @param {Object} config
24047  */
24048
24049 Roo.bootstrap.Table.RowSelectionModel = function(config){
24050     Roo.apply(this, config);
24051     this.selections = new Roo.util.MixedCollection(false, function(o){
24052         return o.id;
24053     });
24054
24055     this.last = false;
24056     this.lastActive = false;
24057
24058     this.addEvents({
24059         /**
24060              * @event selectionchange
24061              * Fires when the selection changes
24062              * @param {SelectionModel} this
24063              */
24064             "selectionchange" : true,
24065         /**
24066              * @event afterselectionchange
24067              * Fires after the selection changes (eg. by key press or clicking)
24068              * @param {SelectionModel} this
24069              */
24070             "afterselectionchange" : true,
24071         /**
24072              * @event beforerowselect
24073              * Fires when a row is selected being selected, return false to cancel.
24074              * @param {SelectionModel} this
24075              * @param {Number} rowIndex The selected index
24076              * @param {Boolean} keepExisting False if other selections will be cleared
24077              */
24078             "beforerowselect" : true,
24079         /**
24080              * @event rowselect
24081              * Fires when a row is selected.
24082              * @param {SelectionModel} this
24083              * @param {Number} rowIndex The selected index
24084              * @param {Roo.data.Record} r The record
24085              */
24086             "rowselect" : true,
24087         /**
24088              * @event rowdeselect
24089              * Fires when a row is deselected.
24090              * @param {SelectionModel} this
24091              * @param {Number} rowIndex The selected index
24092              */
24093         "rowdeselect" : true
24094     });
24095     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24096     this.locked = false;
24097  };
24098
24099 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24100     /**
24101      * @cfg {Boolean} singleSelect
24102      * True to allow selection of only one row at a time (defaults to false)
24103      */
24104     singleSelect : false,
24105
24106     // private
24107     initEvents : function()
24108     {
24109
24110         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24111         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24112         //}else{ // allow click to work like normal
24113          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24114         //}
24115         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24116         this.grid.on("rowclick", this.handleMouseDown, this);
24117         
24118         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24119             "up" : function(e){
24120                 if(!e.shiftKey){
24121                     this.selectPrevious(e.shiftKey);
24122                 }else if(this.last !== false && this.lastActive !== false){
24123                     var last = this.last;
24124                     this.selectRange(this.last,  this.lastActive-1);
24125                     this.grid.getView().focusRow(this.lastActive);
24126                     if(last !== false){
24127                         this.last = last;
24128                     }
24129                 }else{
24130                     this.selectFirstRow();
24131                 }
24132                 this.fireEvent("afterselectionchange", this);
24133             },
24134             "down" : function(e){
24135                 if(!e.shiftKey){
24136                     this.selectNext(e.shiftKey);
24137                 }else if(this.last !== false && this.lastActive !== false){
24138                     var last = this.last;
24139                     this.selectRange(this.last,  this.lastActive+1);
24140                     this.grid.getView().focusRow(this.lastActive);
24141                     if(last !== false){
24142                         this.last = last;
24143                     }
24144                 }else{
24145                     this.selectFirstRow();
24146                 }
24147                 this.fireEvent("afterselectionchange", this);
24148             },
24149             scope: this
24150         });
24151         this.grid.store.on('load', function(){
24152             this.selections.clear();
24153         },this);
24154         /*
24155         var view = this.grid.view;
24156         view.on("refresh", this.onRefresh, this);
24157         view.on("rowupdated", this.onRowUpdated, this);
24158         view.on("rowremoved", this.onRemove, this);
24159         */
24160     },
24161
24162     // private
24163     onRefresh : function()
24164     {
24165         var ds = this.grid.store, i, v = this.grid.view;
24166         var s = this.selections;
24167         s.each(function(r){
24168             if((i = ds.indexOfId(r.id)) != -1){
24169                 v.onRowSelect(i);
24170             }else{
24171                 s.remove(r);
24172             }
24173         });
24174     },
24175
24176     // private
24177     onRemove : function(v, index, r){
24178         this.selections.remove(r);
24179     },
24180
24181     // private
24182     onRowUpdated : function(v, index, r){
24183         if(this.isSelected(r)){
24184             v.onRowSelect(index);
24185         }
24186     },
24187
24188     /**
24189      * Select records.
24190      * @param {Array} records The records to select
24191      * @param {Boolean} keepExisting (optional) True to keep existing selections
24192      */
24193     selectRecords : function(records, keepExisting)
24194     {
24195         if(!keepExisting){
24196             this.clearSelections();
24197         }
24198             var ds = this.grid.store;
24199         for(var i = 0, len = records.length; i < len; i++){
24200             this.selectRow(ds.indexOf(records[i]), true);
24201         }
24202     },
24203
24204     /**
24205      * Gets the number of selected rows.
24206      * @return {Number}
24207      */
24208     getCount : function(){
24209         return this.selections.length;
24210     },
24211
24212     /**
24213      * Selects the first row in the grid.
24214      */
24215     selectFirstRow : function(){
24216         this.selectRow(0);
24217     },
24218
24219     /**
24220      * Select the last row.
24221      * @param {Boolean} keepExisting (optional) True to keep existing selections
24222      */
24223     selectLastRow : function(keepExisting){
24224         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24225         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24226     },
24227
24228     /**
24229      * Selects the row immediately following the last selected row.
24230      * @param {Boolean} keepExisting (optional) True to keep existing selections
24231      */
24232     selectNext : function(keepExisting)
24233     {
24234             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24235             this.selectRow(this.last+1, keepExisting);
24236             this.grid.getView().focusRow(this.last);
24237         }
24238     },
24239
24240     /**
24241      * Selects the row that precedes the last selected row.
24242      * @param {Boolean} keepExisting (optional) True to keep existing selections
24243      */
24244     selectPrevious : function(keepExisting){
24245         if(this.last){
24246             this.selectRow(this.last-1, keepExisting);
24247             this.grid.getView().focusRow(this.last);
24248         }
24249     },
24250
24251     /**
24252      * Returns the selected records
24253      * @return {Array} Array of selected records
24254      */
24255     getSelections : function(){
24256         return [].concat(this.selections.items);
24257     },
24258
24259     /**
24260      * Returns the first selected record.
24261      * @return {Record}
24262      */
24263     getSelected : function(){
24264         return this.selections.itemAt(0);
24265     },
24266
24267
24268     /**
24269      * Clears all selections.
24270      */
24271     clearSelections : function(fast)
24272     {
24273         if(this.locked) {
24274             return;
24275         }
24276         if(fast !== true){
24277                 var ds = this.grid.store;
24278             var s = this.selections;
24279             s.each(function(r){
24280                 this.deselectRow(ds.indexOfId(r.id));
24281             }, this);
24282             s.clear();
24283         }else{
24284             this.selections.clear();
24285         }
24286         this.last = false;
24287     },
24288
24289
24290     /**
24291      * Selects all rows.
24292      */
24293     selectAll : function(){
24294         if(this.locked) {
24295             return;
24296         }
24297         this.selections.clear();
24298         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24299             this.selectRow(i, true);
24300         }
24301     },
24302
24303     /**
24304      * Returns True if there is a selection.
24305      * @return {Boolean}
24306      */
24307     hasSelection : function(){
24308         return this.selections.length > 0;
24309     },
24310
24311     /**
24312      * Returns True if the specified row is selected.
24313      * @param {Number/Record} record The record or index of the record to check
24314      * @return {Boolean}
24315      */
24316     isSelected : function(index){
24317             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24318         return (r && this.selections.key(r.id) ? true : false);
24319     },
24320
24321     /**
24322      * Returns True if the specified record id is selected.
24323      * @param {String} id The id of record to check
24324      * @return {Boolean}
24325      */
24326     isIdSelected : function(id){
24327         return (this.selections.key(id) ? true : false);
24328     },
24329
24330
24331     // private
24332     handleMouseDBClick : function(e, t){
24333         
24334     },
24335     // private
24336     handleMouseDown : function(e, t)
24337     {
24338             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24339         if(this.isLocked() || rowIndex < 0 ){
24340             return;
24341         };
24342         if(e.shiftKey && this.last !== false){
24343             var last = this.last;
24344             this.selectRange(last, rowIndex, e.ctrlKey);
24345             this.last = last; // reset the last
24346             t.focus();
24347     
24348         }else{
24349             var isSelected = this.isSelected(rowIndex);
24350             //Roo.log("select row:" + rowIndex);
24351             if(isSelected){
24352                 this.deselectRow(rowIndex);
24353             } else {
24354                         this.selectRow(rowIndex, true);
24355             }
24356     
24357             /*
24358                 if(e.button !== 0 && isSelected){
24359                 alert('rowIndex 2: ' + rowIndex);
24360                     view.focusRow(rowIndex);
24361                 }else if(e.ctrlKey && isSelected){
24362                     this.deselectRow(rowIndex);
24363                 }else if(!isSelected){
24364                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24365                     view.focusRow(rowIndex);
24366                 }
24367             */
24368         }
24369         this.fireEvent("afterselectionchange", this);
24370     },
24371     // private
24372     handleDragableRowClick :  function(grid, rowIndex, e) 
24373     {
24374         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24375             this.selectRow(rowIndex, false);
24376             grid.view.focusRow(rowIndex);
24377              this.fireEvent("afterselectionchange", this);
24378         }
24379     },
24380     
24381     /**
24382      * Selects multiple rows.
24383      * @param {Array} rows Array of the indexes of the row to select
24384      * @param {Boolean} keepExisting (optional) True to keep existing selections
24385      */
24386     selectRows : function(rows, keepExisting){
24387         if(!keepExisting){
24388             this.clearSelections();
24389         }
24390         for(var i = 0, len = rows.length; i < len; i++){
24391             this.selectRow(rows[i], true);
24392         }
24393     },
24394
24395     /**
24396      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24397      * @param {Number} startRow The index of the first row in the range
24398      * @param {Number} endRow The index of the last row in the range
24399      * @param {Boolean} keepExisting (optional) True to retain existing selections
24400      */
24401     selectRange : function(startRow, endRow, keepExisting){
24402         if(this.locked) {
24403             return;
24404         }
24405         if(!keepExisting){
24406             this.clearSelections();
24407         }
24408         if(startRow <= endRow){
24409             for(var i = startRow; i <= endRow; i++){
24410                 this.selectRow(i, true);
24411             }
24412         }else{
24413             for(var i = startRow; i >= endRow; i--){
24414                 this.selectRow(i, true);
24415             }
24416         }
24417     },
24418
24419     /**
24420      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24421      * @param {Number} startRow The index of the first row in the range
24422      * @param {Number} endRow The index of the last row in the range
24423      */
24424     deselectRange : function(startRow, endRow, preventViewNotify){
24425         if(this.locked) {
24426             return;
24427         }
24428         for(var i = startRow; i <= endRow; i++){
24429             this.deselectRow(i, preventViewNotify);
24430         }
24431     },
24432
24433     /**
24434      * Selects a row.
24435      * @param {Number} row The index of the row to select
24436      * @param {Boolean} keepExisting (optional) True to keep existing selections
24437      */
24438     selectRow : function(index, keepExisting, preventViewNotify)
24439     {
24440             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24441             return;
24442         }
24443         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24444             if(!keepExisting || this.singleSelect){
24445                 this.clearSelections();
24446             }
24447             
24448             var r = this.grid.store.getAt(index);
24449             //console.log('selectRow - record id :' + r.id);
24450             
24451             this.selections.add(r);
24452             this.last = this.lastActive = index;
24453             if(!preventViewNotify){
24454                 var proxy = new Roo.Element(
24455                                 this.grid.getRowDom(index)
24456                 );
24457                 proxy.addClass('bg-info info');
24458             }
24459             this.fireEvent("rowselect", this, index, r);
24460             this.fireEvent("selectionchange", this);
24461         }
24462     },
24463
24464     /**
24465      * Deselects a row.
24466      * @param {Number} row The index of the row to deselect
24467      */
24468     deselectRow : function(index, preventViewNotify)
24469     {
24470         if(this.locked) {
24471             return;
24472         }
24473         if(this.last == index){
24474             this.last = false;
24475         }
24476         if(this.lastActive == index){
24477             this.lastActive = false;
24478         }
24479         
24480         var r = this.grid.store.getAt(index);
24481         if (!r) {
24482             return;
24483         }
24484         
24485         this.selections.remove(r);
24486         //.console.log('deselectRow - record id :' + r.id);
24487         if(!preventViewNotify){
24488         
24489             var proxy = new Roo.Element(
24490                 this.grid.getRowDom(index)
24491             );
24492             proxy.removeClass('bg-info info');
24493         }
24494         this.fireEvent("rowdeselect", this, index);
24495         this.fireEvent("selectionchange", this);
24496     },
24497
24498     // private
24499     restoreLast : function(){
24500         if(this._last){
24501             this.last = this._last;
24502         }
24503     },
24504
24505     // private
24506     acceptsNav : function(row, col, cm){
24507         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24508     },
24509
24510     // private
24511     onEditorKey : function(field, e){
24512         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24513         if(k == e.TAB){
24514             e.stopEvent();
24515             ed.completeEdit();
24516             if(e.shiftKey){
24517                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24518             }else{
24519                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24520             }
24521         }else if(k == e.ENTER && !e.ctrlKey){
24522             e.stopEvent();
24523             ed.completeEdit();
24524             if(e.shiftKey){
24525                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24526             }else{
24527                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24528             }
24529         }else if(k == e.ESC){
24530             ed.cancelEdit();
24531         }
24532         if(newCell){
24533             g.startEditing(newCell[0], newCell[1]);
24534         }
24535     }
24536 });
24537 /*
24538  * Based on:
24539  * Ext JS Library 1.1.1
24540  * Copyright(c) 2006-2007, Ext JS, LLC.
24541  *
24542  * Originally Released Under LGPL - original licence link has changed is not relivant.
24543  *
24544  * Fork - LGPL
24545  * <script type="text/javascript">
24546  */
24547  
24548 /**
24549  * @class Roo.bootstrap.PagingToolbar
24550  * @extends Roo.bootstrap.NavSimplebar
24551  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24552  * @constructor
24553  * Create a new PagingToolbar
24554  * @param {Object} config The config object
24555  * @param {Roo.data.Store} store
24556  */
24557 Roo.bootstrap.PagingToolbar = function(config)
24558 {
24559     // old args format still supported... - xtype is prefered..
24560         // created from xtype...
24561     
24562     this.ds = config.dataSource;
24563     
24564     if (config.store && !this.ds) {
24565         this.store= Roo.factory(config.store, Roo.data);
24566         this.ds = this.store;
24567         this.ds.xmodule = this.xmodule || false;
24568     }
24569     
24570     this.toolbarItems = [];
24571     if (config.items) {
24572         this.toolbarItems = config.items;
24573     }
24574     
24575     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24576     
24577     this.cursor = 0;
24578     
24579     if (this.ds) { 
24580         this.bind(this.ds);
24581     }
24582     
24583     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24584     
24585 };
24586
24587 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24588     /**
24589      * @cfg {Roo.data.Store} dataSource
24590      * The underlying data store providing the paged data
24591      */
24592     /**
24593      * @cfg {String/HTMLElement/Element} container
24594      * container The id or element that will contain the toolbar
24595      */
24596     /**
24597      * @cfg {Boolean} displayInfo
24598      * True to display the displayMsg (defaults to false)
24599      */
24600     /**
24601      * @cfg {Number} pageSize
24602      * The number of records to display per page (defaults to 20)
24603      */
24604     pageSize: 20,
24605     /**
24606      * @cfg {String} displayMsg
24607      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24608      */
24609     displayMsg : 'Displaying {0} - {1} of {2}',
24610     /**
24611      * @cfg {String} emptyMsg
24612      * The message to display when no records are found (defaults to "No data to display")
24613      */
24614     emptyMsg : 'No data to display',
24615     /**
24616      * Customizable piece of the default paging text (defaults to "Page")
24617      * @type String
24618      */
24619     beforePageText : "Page",
24620     /**
24621      * Customizable piece of the default paging text (defaults to "of %0")
24622      * @type String
24623      */
24624     afterPageText : "of {0}",
24625     /**
24626      * Customizable piece of the default paging text (defaults to "First Page")
24627      * @type String
24628      */
24629     firstText : "First Page",
24630     /**
24631      * Customizable piece of the default paging text (defaults to "Previous Page")
24632      * @type String
24633      */
24634     prevText : "Previous Page",
24635     /**
24636      * Customizable piece of the default paging text (defaults to "Next Page")
24637      * @type String
24638      */
24639     nextText : "Next Page",
24640     /**
24641      * Customizable piece of the default paging text (defaults to "Last Page")
24642      * @type String
24643      */
24644     lastText : "Last Page",
24645     /**
24646      * Customizable piece of the default paging text (defaults to "Refresh")
24647      * @type String
24648      */
24649     refreshText : "Refresh",
24650
24651     buttons : false,
24652     // private
24653     onRender : function(ct, position) 
24654     {
24655         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24656         this.navgroup.parentId = this.id;
24657         this.navgroup.onRender(this.el, null);
24658         // add the buttons to the navgroup
24659         
24660         if(this.displayInfo){
24661             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24662             this.displayEl = this.el.select('.x-paging-info', true).first();
24663 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24664 //            this.displayEl = navel.el.select('span',true).first();
24665         }
24666         
24667         var _this = this;
24668         
24669         if(this.buttons){
24670             Roo.each(_this.buttons, function(e){ // this might need to use render????
24671                Roo.factory(e).render(_this.el);
24672             });
24673         }
24674             
24675         Roo.each(_this.toolbarItems, function(e) {
24676             _this.navgroup.addItem(e);
24677         });
24678         
24679         
24680         this.first = this.navgroup.addItem({
24681             tooltip: this.firstText,
24682             cls: "prev",
24683             icon : 'fa fa-backward',
24684             disabled: true,
24685             preventDefault: true,
24686             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24687         });
24688         
24689         this.prev =  this.navgroup.addItem({
24690             tooltip: this.prevText,
24691             cls: "prev",
24692             icon : 'fa fa-step-backward',
24693             disabled: true,
24694             preventDefault: true,
24695             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24696         });
24697     //this.addSeparator();
24698         
24699         
24700         var field = this.navgroup.addItem( {
24701             tagtype : 'span',
24702             cls : 'x-paging-position',
24703             
24704             html : this.beforePageText  +
24705                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24706                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24707          } ); //?? escaped?
24708         
24709         this.field = field.el.select('input', true).first();
24710         this.field.on("keydown", this.onPagingKeydown, this);
24711         this.field.on("focus", function(){this.dom.select();});
24712     
24713     
24714         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24715         //this.field.setHeight(18);
24716         //this.addSeparator();
24717         this.next = this.navgroup.addItem({
24718             tooltip: this.nextText,
24719             cls: "next",
24720             html : ' <i class="fa fa-step-forward">',
24721             disabled: true,
24722             preventDefault: true,
24723             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24724         });
24725         this.last = this.navgroup.addItem({
24726             tooltip: this.lastText,
24727             icon : 'fa fa-forward',
24728             cls: "next",
24729             disabled: true,
24730             preventDefault: true,
24731             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24732         });
24733     //this.addSeparator();
24734         this.loading = this.navgroup.addItem({
24735             tooltip: this.refreshText,
24736             icon: 'fa fa-refresh',
24737             preventDefault: true,
24738             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24739         });
24740         
24741     },
24742
24743     // private
24744     updateInfo : function(){
24745         if(this.displayEl){
24746             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24747             var msg = count == 0 ?
24748                 this.emptyMsg :
24749                 String.format(
24750                     this.displayMsg,
24751                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24752                 );
24753             this.displayEl.update(msg);
24754         }
24755     },
24756
24757     // private
24758     onLoad : function(ds, r, o)
24759     {
24760         this.cursor = o.params.start ? o.params.start : 0;
24761         
24762         var d = this.getPageData(),
24763             ap = d.activePage,
24764             ps = d.pages;
24765         
24766         
24767         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24768         this.field.dom.value = ap;
24769         this.first.setDisabled(ap == 1);
24770         this.prev.setDisabled(ap == 1);
24771         this.next.setDisabled(ap == ps);
24772         this.last.setDisabled(ap == ps);
24773         this.loading.enable();
24774         this.updateInfo();
24775     },
24776
24777     // private
24778     getPageData : function(){
24779         var total = this.ds.getTotalCount();
24780         return {
24781             total : total,
24782             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24783             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24784         };
24785     },
24786
24787     // private
24788     onLoadError : function(){
24789         this.loading.enable();
24790     },
24791
24792     // private
24793     onPagingKeydown : function(e){
24794         var k = e.getKey();
24795         var d = this.getPageData();
24796         if(k == e.RETURN){
24797             var v = this.field.dom.value, pageNum;
24798             if(!v || isNaN(pageNum = parseInt(v, 10))){
24799                 this.field.dom.value = d.activePage;
24800                 return;
24801             }
24802             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24803             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24804             e.stopEvent();
24805         }
24806         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))
24807         {
24808           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24809           this.field.dom.value = pageNum;
24810           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24811           e.stopEvent();
24812         }
24813         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24814         {
24815           var v = this.field.dom.value, pageNum; 
24816           var increment = (e.shiftKey) ? 10 : 1;
24817           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24818                 increment *= -1;
24819           }
24820           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24821             this.field.dom.value = d.activePage;
24822             return;
24823           }
24824           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24825           {
24826             this.field.dom.value = parseInt(v, 10) + increment;
24827             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24828             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24829           }
24830           e.stopEvent();
24831         }
24832     },
24833
24834     // private
24835     beforeLoad : function(){
24836         if(this.loading){
24837             this.loading.disable();
24838         }
24839     },
24840
24841     // private
24842     onClick : function(which){
24843         
24844         var ds = this.ds;
24845         if (!ds) {
24846             return;
24847         }
24848         
24849         switch(which){
24850             case "first":
24851                 ds.load({params:{start: 0, limit: this.pageSize}});
24852             break;
24853             case "prev":
24854                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24855             break;
24856             case "next":
24857                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24858             break;
24859             case "last":
24860                 var total = ds.getTotalCount();
24861                 var extra = total % this.pageSize;
24862                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24863                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24864             break;
24865             case "refresh":
24866                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24867             break;
24868         }
24869     },
24870
24871     /**
24872      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24873      * @param {Roo.data.Store} store The data store to unbind
24874      */
24875     unbind : function(ds){
24876         ds.un("beforeload", this.beforeLoad, this);
24877         ds.un("load", this.onLoad, this);
24878         ds.un("loadexception", this.onLoadError, this);
24879         ds.un("remove", this.updateInfo, this);
24880         ds.un("add", this.updateInfo, this);
24881         this.ds = undefined;
24882     },
24883
24884     /**
24885      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24886      * @param {Roo.data.Store} store The data store to bind
24887      */
24888     bind : function(ds){
24889         ds.on("beforeload", this.beforeLoad, this);
24890         ds.on("load", this.onLoad, this);
24891         ds.on("loadexception", this.onLoadError, this);
24892         ds.on("remove", this.updateInfo, this);
24893         ds.on("add", this.updateInfo, this);
24894         this.ds = ds;
24895     }
24896 });/*
24897  * - LGPL
24898  *
24899  * element
24900  * 
24901  */
24902
24903 /**
24904  * @class Roo.bootstrap.MessageBar
24905  * @extends Roo.bootstrap.Component
24906  * Bootstrap MessageBar class
24907  * @cfg {String} html contents of the MessageBar
24908  * @cfg {String} weight (info | success | warning | danger) default info
24909  * @cfg {String} beforeClass insert the bar before the given class
24910  * @cfg {Boolean} closable (true | false) default false
24911  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24912  * 
24913  * @constructor
24914  * Create a new Element
24915  * @param {Object} config The config object
24916  */
24917
24918 Roo.bootstrap.MessageBar = function(config){
24919     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24920 };
24921
24922 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24923     
24924     html: '',
24925     weight: 'info',
24926     closable: false,
24927     fixed: false,
24928     beforeClass: 'bootstrap-sticky-wrap',
24929     
24930     getAutoCreate : function(){
24931         
24932         var cfg = {
24933             tag: 'div',
24934             cls: 'alert alert-dismissable alert-' + this.weight,
24935             cn: [
24936                 {
24937                     tag: 'span',
24938                     cls: 'message',
24939                     html: this.html || ''
24940                 }
24941             ]
24942         };
24943         
24944         if(this.fixed){
24945             cfg.cls += ' alert-messages-fixed';
24946         }
24947         
24948         if(this.closable){
24949             cfg.cn.push({
24950                 tag: 'button',
24951                 cls: 'close',
24952                 html: 'x'
24953             });
24954         }
24955         
24956         return cfg;
24957     },
24958     
24959     onRender : function(ct, position)
24960     {
24961         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24962         
24963         if(!this.el){
24964             var cfg = Roo.apply({},  this.getAutoCreate());
24965             cfg.id = Roo.id();
24966             
24967             if (this.cls) {
24968                 cfg.cls += ' ' + this.cls;
24969             }
24970             if (this.style) {
24971                 cfg.style = this.style;
24972             }
24973             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24974             
24975             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24976         }
24977         
24978         this.el.select('>button.close').on('click', this.hide, this);
24979         
24980     },
24981     
24982     show : function()
24983     {
24984         if (!this.rendered) {
24985             this.render();
24986         }
24987         
24988         this.el.show();
24989         
24990         this.fireEvent('show', this);
24991         
24992     },
24993     
24994     hide : function()
24995     {
24996         if (!this.rendered) {
24997             this.render();
24998         }
24999         
25000         this.el.hide();
25001         
25002         this.fireEvent('hide', this);
25003     },
25004     
25005     update : function()
25006     {
25007 //        var e = this.el.dom.firstChild;
25008 //        
25009 //        if(this.closable){
25010 //            e = e.nextSibling;
25011 //        }
25012 //        
25013 //        e.data = this.html || '';
25014
25015         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25016     }
25017    
25018 });
25019
25020  
25021
25022      /*
25023  * - LGPL
25024  *
25025  * Graph
25026  * 
25027  */
25028
25029
25030 /**
25031  * @class Roo.bootstrap.Graph
25032  * @extends Roo.bootstrap.Component
25033  * Bootstrap Graph class
25034 > Prameters
25035  -sm {number} sm 4
25036  -md {number} md 5
25037  @cfg {String} graphtype  bar | vbar | pie
25038  @cfg {number} g_x coodinator | centre x (pie)
25039  @cfg {number} g_y coodinator | centre y (pie)
25040  @cfg {number} g_r radius (pie)
25041  @cfg {number} g_height height of the chart (respected by all elements in the set)
25042  @cfg {number} g_width width of the chart (respected by all elements in the set)
25043  @cfg {Object} title The title of the chart
25044     
25045  -{Array}  values
25046  -opts (object) options for the chart 
25047      o {
25048      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25049      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25050      o vgutter (number)
25051      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.
25052      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25053      o to
25054      o stretch (boolean)
25055      o }
25056  -opts (object) options for the pie
25057      o{
25058      o cut
25059      o startAngle (number)
25060      o endAngle (number)
25061      } 
25062  *
25063  * @constructor
25064  * Create a new Input
25065  * @param {Object} config The config object
25066  */
25067
25068 Roo.bootstrap.Graph = function(config){
25069     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25070     
25071     this.addEvents({
25072         // img events
25073         /**
25074          * @event click
25075          * The img click event for the img.
25076          * @param {Roo.EventObject} e
25077          */
25078         "click" : true
25079     });
25080 };
25081
25082 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25083     
25084     sm: 4,
25085     md: 5,
25086     graphtype: 'bar',
25087     g_height: 250,
25088     g_width: 400,
25089     g_x: 50,
25090     g_y: 50,
25091     g_r: 30,
25092     opts:{
25093         //g_colors: this.colors,
25094         g_type: 'soft',
25095         g_gutter: '20%'
25096
25097     },
25098     title : false,
25099
25100     getAutoCreate : function(){
25101         
25102         var cfg = {
25103             tag: 'div',
25104             html : null
25105         };
25106         
25107         
25108         return  cfg;
25109     },
25110
25111     onRender : function(ct,position){
25112         
25113         
25114         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25115         
25116         if (typeof(Raphael) == 'undefined') {
25117             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25118             return;
25119         }
25120         
25121         this.raphael = Raphael(this.el.dom);
25122         
25123                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25124                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25125                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25126                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25127                 /*
25128                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25129                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25130                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25131                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25132                 
25133                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25134                 r.barchart(330, 10, 300, 220, data1);
25135                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25136                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25137                 */
25138                 
25139                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25140                 // r.barchart(30, 30, 560, 250,  xdata, {
25141                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25142                 //     axis : "0 0 1 1",
25143                 //     axisxlabels :  xdata
25144                 //     //yvalues : cols,
25145                    
25146                 // });
25147 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25148 //        
25149 //        this.load(null,xdata,{
25150 //                axis : "0 0 1 1",
25151 //                axisxlabels :  xdata
25152 //                });
25153
25154     },
25155
25156     load : function(graphtype,xdata,opts)
25157     {
25158         this.raphael.clear();
25159         if(!graphtype) {
25160             graphtype = this.graphtype;
25161         }
25162         if(!opts){
25163             opts = this.opts;
25164         }
25165         var r = this.raphael,
25166             fin = function () {
25167                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25168             },
25169             fout = function () {
25170                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25171             },
25172             pfin = function() {
25173                 this.sector.stop();
25174                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25175
25176                 if (this.label) {
25177                     this.label[0].stop();
25178                     this.label[0].attr({ r: 7.5 });
25179                     this.label[1].attr({ "font-weight": 800 });
25180                 }
25181             },
25182             pfout = function() {
25183                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25184
25185                 if (this.label) {
25186                     this.label[0].animate({ r: 5 }, 500, "bounce");
25187                     this.label[1].attr({ "font-weight": 400 });
25188                 }
25189             };
25190
25191         switch(graphtype){
25192             case 'bar':
25193                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25194                 break;
25195             case 'hbar':
25196                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25197                 break;
25198             case 'pie':
25199 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25200 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25201 //            
25202                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25203                 
25204                 break;
25205
25206         }
25207         
25208         if(this.title){
25209             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25210         }
25211         
25212     },
25213     
25214     setTitle: function(o)
25215     {
25216         this.title = o;
25217     },
25218     
25219     initEvents: function() {
25220         
25221         if(!this.href){
25222             this.el.on('click', this.onClick, this);
25223         }
25224     },
25225     
25226     onClick : function(e)
25227     {
25228         Roo.log('img onclick');
25229         this.fireEvent('click', this, e);
25230     }
25231    
25232 });
25233
25234  
25235 /*
25236  * - LGPL
25237  *
25238  * numberBox
25239  * 
25240  */
25241 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25242
25243 /**
25244  * @class Roo.bootstrap.dash.NumberBox
25245  * @extends Roo.bootstrap.Component
25246  * Bootstrap NumberBox class
25247  * @cfg {String} headline Box headline
25248  * @cfg {String} content Box content
25249  * @cfg {String} icon Box icon
25250  * @cfg {String} footer Footer text
25251  * @cfg {String} fhref Footer href
25252  * 
25253  * @constructor
25254  * Create a new NumberBox
25255  * @param {Object} config The config object
25256  */
25257
25258
25259 Roo.bootstrap.dash.NumberBox = function(config){
25260     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25261     
25262 };
25263
25264 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25265     
25266     headline : '',
25267     content : '',
25268     icon : '',
25269     footer : '',
25270     fhref : '',
25271     ficon : '',
25272     
25273     getAutoCreate : function(){
25274         
25275         var cfg = {
25276             tag : 'div',
25277             cls : 'small-box ',
25278             cn : [
25279                 {
25280                     tag : 'div',
25281                     cls : 'inner',
25282                     cn :[
25283                         {
25284                             tag : 'h3',
25285                             cls : 'roo-headline',
25286                             html : this.headline
25287                         },
25288                         {
25289                             tag : 'p',
25290                             cls : 'roo-content',
25291                             html : this.content
25292                         }
25293                     ]
25294                 }
25295             ]
25296         };
25297         
25298         if(this.icon){
25299             cfg.cn.push({
25300                 tag : 'div',
25301                 cls : 'icon',
25302                 cn :[
25303                     {
25304                         tag : 'i',
25305                         cls : 'ion ' + this.icon
25306                     }
25307                 ]
25308             });
25309         }
25310         
25311         if(this.footer){
25312             var footer = {
25313                 tag : 'a',
25314                 cls : 'small-box-footer',
25315                 href : this.fhref || '#',
25316                 html : this.footer
25317             };
25318             
25319             cfg.cn.push(footer);
25320             
25321         }
25322         
25323         return  cfg;
25324     },
25325
25326     onRender : function(ct,position){
25327         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25328
25329
25330        
25331                 
25332     },
25333
25334     setHeadline: function (value)
25335     {
25336         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25337     },
25338     
25339     setFooter: function (value, href)
25340     {
25341         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25342         
25343         if(href){
25344             this.el.select('a.small-box-footer',true).first().attr('href', href);
25345         }
25346         
25347     },
25348
25349     setContent: function (value)
25350     {
25351         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25352     },
25353
25354     initEvents: function() 
25355     {   
25356         
25357     }
25358     
25359 });
25360
25361  
25362 /*
25363  * - LGPL
25364  *
25365  * TabBox
25366  * 
25367  */
25368 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25369
25370 /**
25371  * @class Roo.bootstrap.dash.TabBox
25372  * @extends Roo.bootstrap.Component
25373  * Bootstrap TabBox class
25374  * @cfg {String} title Title of the TabBox
25375  * @cfg {String} icon Icon of the TabBox
25376  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25377  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25378  * 
25379  * @constructor
25380  * Create a new TabBox
25381  * @param {Object} config The config object
25382  */
25383
25384
25385 Roo.bootstrap.dash.TabBox = function(config){
25386     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25387     this.addEvents({
25388         // raw events
25389         /**
25390          * @event addpane
25391          * When a pane is added
25392          * @param {Roo.bootstrap.dash.TabPane} pane
25393          */
25394         "addpane" : true,
25395         /**
25396          * @event activatepane
25397          * When a pane is activated
25398          * @param {Roo.bootstrap.dash.TabPane} pane
25399          */
25400         "activatepane" : true
25401         
25402          
25403     });
25404     
25405     this.panes = [];
25406 };
25407
25408 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25409
25410     title : '',
25411     icon : false,
25412     showtabs : true,
25413     tabScrollable : false,
25414     
25415     getChildContainer : function()
25416     {
25417         return this.el.select('.tab-content', true).first();
25418     },
25419     
25420     getAutoCreate : function(){
25421         
25422         var header = {
25423             tag: 'li',
25424             cls: 'pull-left header',
25425             html: this.title,
25426             cn : []
25427         };
25428         
25429         if(this.icon){
25430             header.cn.push({
25431                 tag: 'i',
25432                 cls: 'fa ' + this.icon
25433             });
25434         }
25435         
25436         var h = {
25437             tag: 'ul',
25438             cls: 'nav nav-tabs pull-right',
25439             cn: [
25440                 header
25441             ]
25442         };
25443         
25444         if(this.tabScrollable){
25445             h = {
25446                 tag: 'div',
25447                 cls: 'tab-header',
25448                 cn: [
25449                     {
25450                         tag: 'ul',
25451                         cls: 'nav nav-tabs pull-right',
25452                         cn: [
25453                             header
25454                         ]
25455                     }
25456                 ]
25457             };
25458         }
25459         
25460         var cfg = {
25461             tag: 'div',
25462             cls: 'nav-tabs-custom',
25463             cn: [
25464                 h,
25465                 {
25466                     tag: 'div',
25467                     cls: 'tab-content no-padding',
25468                     cn: []
25469                 }
25470             ]
25471         };
25472
25473         return  cfg;
25474     },
25475     initEvents : function()
25476     {
25477         //Roo.log('add add pane handler');
25478         this.on('addpane', this.onAddPane, this);
25479     },
25480      /**
25481      * Updates the box title
25482      * @param {String} html to set the title to.
25483      */
25484     setTitle : function(value)
25485     {
25486         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25487     },
25488     onAddPane : function(pane)
25489     {
25490         this.panes.push(pane);
25491         //Roo.log('addpane');
25492         //Roo.log(pane);
25493         // tabs are rendere left to right..
25494         if(!this.showtabs){
25495             return;
25496         }
25497         
25498         var ctr = this.el.select('.nav-tabs', true).first();
25499          
25500          
25501         var existing = ctr.select('.nav-tab',true);
25502         var qty = existing.getCount();;
25503         
25504         
25505         var tab = ctr.createChild({
25506             tag : 'li',
25507             cls : 'nav-tab' + (qty ? '' : ' active'),
25508             cn : [
25509                 {
25510                     tag : 'a',
25511                     href:'#',
25512                     html : pane.title
25513                 }
25514             ]
25515         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25516         pane.tab = tab;
25517         
25518         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25519         if (!qty) {
25520             pane.el.addClass('active');
25521         }
25522         
25523                 
25524     },
25525     onTabClick : function(ev,un,ob,pane)
25526     {
25527         //Roo.log('tab - prev default');
25528         ev.preventDefault();
25529         
25530         
25531         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25532         pane.tab.addClass('active');
25533         //Roo.log(pane.title);
25534         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25535         // technically we should have a deactivate event.. but maybe add later.
25536         // and it should not de-activate the selected tab...
25537         this.fireEvent('activatepane', pane);
25538         pane.el.addClass('active');
25539         pane.fireEvent('activate');
25540         
25541         
25542     },
25543     
25544     getActivePane : function()
25545     {
25546         var r = false;
25547         Roo.each(this.panes, function(p) {
25548             if(p.el.hasClass('active')){
25549                 r = p;
25550                 return false;
25551             }
25552             
25553             return;
25554         });
25555         
25556         return r;
25557     }
25558     
25559     
25560 });
25561
25562  
25563 /*
25564  * - LGPL
25565  *
25566  * Tab pane
25567  * 
25568  */
25569 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25570 /**
25571  * @class Roo.bootstrap.TabPane
25572  * @extends Roo.bootstrap.Component
25573  * Bootstrap TabPane class
25574  * @cfg {Boolean} active (false | true) Default false
25575  * @cfg {String} title title of panel
25576
25577  * 
25578  * @constructor
25579  * Create a new TabPane
25580  * @param {Object} config The config object
25581  */
25582
25583 Roo.bootstrap.dash.TabPane = function(config){
25584     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25585     
25586     this.addEvents({
25587         // raw events
25588         /**
25589          * @event activate
25590          * When a pane is activated
25591          * @param {Roo.bootstrap.dash.TabPane} pane
25592          */
25593         "activate" : true
25594          
25595     });
25596 };
25597
25598 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25599     
25600     active : false,
25601     title : '',
25602     
25603     // the tabBox that this is attached to.
25604     tab : false,
25605      
25606     getAutoCreate : function() 
25607     {
25608         var cfg = {
25609             tag: 'div',
25610             cls: 'tab-pane'
25611         };
25612         
25613         if(this.active){
25614             cfg.cls += ' active';
25615         }
25616         
25617         return cfg;
25618     },
25619     initEvents  : function()
25620     {
25621         //Roo.log('trigger add pane handler');
25622         this.parent().fireEvent('addpane', this)
25623     },
25624     
25625      /**
25626      * Updates the tab title 
25627      * @param {String} html to set the title to.
25628      */
25629     setTitle: function(str)
25630     {
25631         if (!this.tab) {
25632             return;
25633         }
25634         this.title = str;
25635         this.tab.select('a', true).first().dom.innerHTML = str;
25636         
25637     }
25638     
25639     
25640     
25641 });
25642
25643  
25644
25645
25646  /*
25647  * - LGPL
25648  *
25649  * menu
25650  * 
25651  */
25652 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25653
25654 /**
25655  * @class Roo.bootstrap.menu.Menu
25656  * @extends Roo.bootstrap.Component
25657  * Bootstrap Menu class - container for Menu
25658  * @cfg {String} html Text of the menu
25659  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25660  * @cfg {String} icon Font awesome icon
25661  * @cfg {String} pos Menu align to (top | bottom) default bottom
25662  * 
25663  * 
25664  * @constructor
25665  * Create a new Menu
25666  * @param {Object} config The config object
25667  */
25668
25669
25670 Roo.bootstrap.menu.Menu = function(config){
25671     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25672     
25673     this.addEvents({
25674         /**
25675          * @event beforeshow
25676          * Fires before this menu is displayed
25677          * @param {Roo.bootstrap.menu.Menu} this
25678          */
25679         beforeshow : true,
25680         /**
25681          * @event beforehide
25682          * Fires before this menu is hidden
25683          * @param {Roo.bootstrap.menu.Menu} this
25684          */
25685         beforehide : true,
25686         /**
25687          * @event show
25688          * Fires after this menu is displayed
25689          * @param {Roo.bootstrap.menu.Menu} this
25690          */
25691         show : true,
25692         /**
25693          * @event hide
25694          * Fires after this menu is hidden
25695          * @param {Roo.bootstrap.menu.Menu} this
25696          */
25697         hide : true,
25698         /**
25699          * @event click
25700          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25701          * @param {Roo.bootstrap.menu.Menu} this
25702          * @param {Roo.EventObject} e
25703          */
25704         click : true
25705     });
25706     
25707 };
25708
25709 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25710     
25711     submenu : false,
25712     html : '',
25713     weight : 'default',
25714     icon : false,
25715     pos : 'bottom',
25716     
25717     
25718     getChildContainer : function() {
25719         if(this.isSubMenu){
25720             return this.el;
25721         }
25722         
25723         return this.el.select('ul.dropdown-menu', true).first();  
25724     },
25725     
25726     getAutoCreate : function()
25727     {
25728         var text = [
25729             {
25730                 tag : 'span',
25731                 cls : 'roo-menu-text',
25732                 html : this.html
25733             }
25734         ];
25735         
25736         if(this.icon){
25737             text.unshift({
25738                 tag : 'i',
25739                 cls : 'fa ' + this.icon
25740             })
25741         }
25742         
25743         
25744         var cfg = {
25745             tag : 'div',
25746             cls : 'btn-group',
25747             cn : [
25748                 {
25749                     tag : 'button',
25750                     cls : 'dropdown-button btn btn-' + this.weight,
25751                     cn : text
25752                 },
25753                 {
25754                     tag : 'button',
25755                     cls : 'dropdown-toggle btn btn-' + this.weight,
25756                     cn : [
25757                         {
25758                             tag : 'span',
25759                             cls : 'caret'
25760                         }
25761                     ]
25762                 },
25763                 {
25764                     tag : 'ul',
25765                     cls : 'dropdown-menu'
25766                 }
25767             ]
25768             
25769         };
25770         
25771         if(this.pos == 'top'){
25772             cfg.cls += ' dropup';
25773         }
25774         
25775         if(this.isSubMenu){
25776             cfg = {
25777                 tag : 'ul',
25778                 cls : 'dropdown-menu'
25779             }
25780         }
25781         
25782         return cfg;
25783     },
25784     
25785     onRender : function(ct, position)
25786     {
25787         this.isSubMenu = ct.hasClass('dropdown-submenu');
25788         
25789         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25790     },
25791     
25792     initEvents : function() 
25793     {
25794         if(this.isSubMenu){
25795             return;
25796         }
25797         
25798         this.hidden = true;
25799         
25800         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25801         this.triggerEl.on('click', this.onTriggerPress, this);
25802         
25803         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25804         this.buttonEl.on('click', this.onClick, this);
25805         
25806     },
25807     
25808     list : function()
25809     {
25810         if(this.isSubMenu){
25811             return this.el;
25812         }
25813         
25814         return this.el.select('ul.dropdown-menu', true).first();
25815     },
25816     
25817     onClick : function(e)
25818     {
25819         this.fireEvent("click", this, e);
25820     },
25821     
25822     onTriggerPress  : function(e)
25823     {   
25824         if (this.isVisible()) {
25825             this.hide();
25826         } else {
25827             this.show();
25828         }
25829     },
25830     
25831     isVisible : function(){
25832         return !this.hidden;
25833     },
25834     
25835     show : function()
25836     {
25837         this.fireEvent("beforeshow", this);
25838         
25839         this.hidden = false;
25840         this.el.addClass('open');
25841         
25842         Roo.get(document).on("mouseup", this.onMouseUp, this);
25843         
25844         this.fireEvent("show", this);
25845         
25846         
25847     },
25848     
25849     hide : function()
25850     {
25851         this.fireEvent("beforehide", this);
25852         
25853         this.hidden = true;
25854         this.el.removeClass('open');
25855         
25856         Roo.get(document).un("mouseup", this.onMouseUp);
25857         
25858         this.fireEvent("hide", this);
25859     },
25860     
25861     onMouseUp : function()
25862     {
25863         this.hide();
25864     }
25865     
25866 });
25867
25868  
25869  /*
25870  * - LGPL
25871  *
25872  * menu item
25873  * 
25874  */
25875 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25876
25877 /**
25878  * @class Roo.bootstrap.menu.Item
25879  * @extends Roo.bootstrap.Component
25880  * Bootstrap MenuItem class
25881  * @cfg {Boolean} submenu (true | false) default false
25882  * @cfg {String} html text of the item
25883  * @cfg {String} href the link
25884  * @cfg {Boolean} disable (true | false) default false
25885  * @cfg {Boolean} preventDefault (true | false) default true
25886  * @cfg {String} icon Font awesome icon
25887  * @cfg {String} pos Submenu align to (left | right) default right 
25888  * 
25889  * 
25890  * @constructor
25891  * Create a new Item
25892  * @param {Object} config The config object
25893  */
25894
25895
25896 Roo.bootstrap.menu.Item = function(config){
25897     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25898     this.addEvents({
25899         /**
25900          * @event mouseover
25901          * Fires when the mouse is hovering over this menu
25902          * @param {Roo.bootstrap.menu.Item} this
25903          * @param {Roo.EventObject} e
25904          */
25905         mouseover : true,
25906         /**
25907          * @event mouseout
25908          * Fires when the mouse exits this menu
25909          * @param {Roo.bootstrap.menu.Item} this
25910          * @param {Roo.EventObject} e
25911          */
25912         mouseout : true,
25913         // raw events
25914         /**
25915          * @event click
25916          * The raw click event for the entire grid.
25917          * @param {Roo.EventObject} e
25918          */
25919         click : true
25920     });
25921 };
25922
25923 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25924     
25925     submenu : false,
25926     href : '',
25927     html : '',
25928     preventDefault: true,
25929     disable : false,
25930     icon : false,
25931     pos : 'right',
25932     
25933     getAutoCreate : function()
25934     {
25935         var text = [
25936             {
25937                 tag : 'span',
25938                 cls : 'roo-menu-item-text',
25939                 html : this.html
25940             }
25941         ];
25942         
25943         if(this.icon){
25944             text.unshift({
25945                 tag : 'i',
25946                 cls : 'fa ' + this.icon
25947             })
25948         }
25949         
25950         var cfg = {
25951             tag : 'li',
25952             cn : [
25953                 {
25954                     tag : 'a',
25955                     href : this.href || '#',
25956                     cn : text
25957                 }
25958             ]
25959         };
25960         
25961         if(this.disable){
25962             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25963         }
25964         
25965         if(this.submenu){
25966             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25967             
25968             if(this.pos == 'left'){
25969                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25970             }
25971         }
25972         
25973         return cfg;
25974     },
25975     
25976     initEvents : function() 
25977     {
25978         this.el.on('mouseover', this.onMouseOver, this);
25979         this.el.on('mouseout', this.onMouseOut, this);
25980         
25981         this.el.select('a', true).first().on('click', this.onClick, this);
25982         
25983     },
25984     
25985     onClick : function(e)
25986     {
25987         if(this.preventDefault){
25988             e.preventDefault();
25989         }
25990         
25991         this.fireEvent("click", this, e);
25992     },
25993     
25994     onMouseOver : function(e)
25995     {
25996         if(this.submenu && this.pos == 'left'){
25997             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25998         }
25999         
26000         this.fireEvent("mouseover", this, e);
26001     },
26002     
26003     onMouseOut : function(e)
26004     {
26005         this.fireEvent("mouseout", this, e);
26006     }
26007 });
26008
26009  
26010
26011  /*
26012  * - LGPL
26013  *
26014  * menu separator
26015  * 
26016  */
26017 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26018
26019 /**
26020  * @class Roo.bootstrap.menu.Separator
26021  * @extends Roo.bootstrap.Component
26022  * Bootstrap Separator class
26023  * 
26024  * @constructor
26025  * Create a new Separator
26026  * @param {Object} config The config object
26027  */
26028
26029
26030 Roo.bootstrap.menu.Separator = function(config){
26031     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26032 };
26033
26034 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26035     
26036     getAutoCreate : function(){
26037         var cfg = {
26038             tag : 'li',
26039             cls: 'divider'
26040         };
26041         
26042         return cfg;
26043     }
26044    
26045 });
26046
26047  
26048
26049  /*
26050  * - LGPL
26051  *
26052  * Tooltip
26053  * 
26054  */
26055
26056 /**
26057  * @class Roo.bootstrap.Tooltip
26058  * Bootstrap Tooltip class
26059  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26060  * to determine which dom element triggers the tooltip.
26061  * 
26062  * It needs to add support for additional attributes like tooltip-position
26063  * 
26064  * @constructor
26065  * Create a new Toolti
26066  * @param {Object} config The config object
26067  */
26068
26069 Roo.bootstrap.Tooltip = function(config){
26070     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26071     
26072     this.alignment = Roo.bootstrap.Tooltip.alignment;
26073     
26074     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26075         this.alignment = config.alignment;
26076     }
26077     
26078 };
26079
26080 Roo.apply(Roo.bootstrap.Tooltip, {
26081     /**
26082      * @function init initialize tooltip monitoring.
26083      * @static
26084      */
26085     currentEl : false,
26086     currentTip : false,
26087     currentRegion : false,
26088     
26089     //  init : delay?
26090     
26091     init : function()
26092     {
26093         Roo.get(document).on('mouseover', this.enter ,this);
26094         Roo.get(document).on('mouseout', this.leave, this);
26095          
26096         
26097         this.currentTip = new Roo.bootstrap.Tooltip();
26098     },
26099     
26100     enter : function(ev)
26101     {
26102         var dom = ev.getTarget();
26103         
26104         //Roo.log(['enter',dom]);
26105         var el = Roo.fly(dom);
26106         if (this.currentEl) {
26107             //Roo.log(dom);
26108             //Roo.log(this.currentEl);
26109             //Roo.log(this.currentEl.contains(dom));
26110             if (this.currentEl == el) {
26111                 return;
26112             }
26113             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26114                 return;
26115             }
26116
26117         }
26118         
26119         if (this.currentTip.el) {
26120             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26121         }    
26122         //Roo.log(ev);
26123         
26124         if(!el || el.dom == document){
26125             return;
26126         }
26127         
26128         var bindEl = el;
26129         
26130         // you can not look for children, as if el is the body.. then everythign is the child..
26131         if (!el.attr('tooltip')) { //
26132             if (!el.select("[tooltip]").elements.length) {
26133                 return;
26134             }
26135             // is the mouse over this child...?
26136             bindEl = el.select("[tooltip]").first();
26137             var xy = ev.getXY();
26138             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26139                 //Roo.log("not in region.");
26140                 return;
26141             }
26142             //Roo.log("child element over..");
26143             
26144         }
26145         this.currentEl = bindEl;
26146         this.currentTip.bind(bindEl);
26147         this.currentRegion = Roo.lib.Region.getRegion(dom);
26148         this.currentTip.enter();
26149         
26150     },
26151     leave : function(ev)
26152     {
26153         var dom = ev.getTarget();
26154         //Roo.log(['leave',dom]);
26155         if (!this.currentEl) {
26156             return;
26157         }
26158         
26159         
26160         if (dom != this.currentEl.dom) {
26161             return;
26162         }
26163         var xy = ev.getXY();
26164         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26165             return;
26166         }
26167         // only activate leave if mouse cursor is outside... bounding box..
26168         
26169         
26170         
26171         
26172         if (this.currentTip) {
26173             this.currentTip.leave();
26174         }
26175         //Roo.log('clear currentEl');
26176         this.currentEl = false;
26177         
26178         
26179     },
26180     alignment : {
26181         'left' : ['r-l', [-2,0], 'right'],
26182         'right' : ['l-r', [2,0], 'left'],
26183         'bottom' : ['t-b', [0,2], 'top'],
26184         'top' : [ 'b-t', [0,-2], 'bottom']
26185     }
26186     
26187 });
26188
26189
26190 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26191     
26192     
26193     bindEl : false,
26194     
26195     delay : null, // can be { show : 300 , hide: 500}
26196     
26197     timeout : null,
26198     
26199     hoverState : null, //???
26200     
26201     placement : 'bottom', 
26202     
26203     alignment : false,
26204     
26205     getAutoCreate : function(){
26206     
26207         var cfg = {
26208            cls : 'tooltip',
26209            role : 'tooltip',
26210            cn : [
26211                 {
26212                     cls : 'tooltip-arrow'
26213                 },
26214                 {
26215                     cls : 'tooltip-inner'
26216                 }
26217            ]
26218         };
26219         
26220         return cfg;
26221     },
26222     bind : function(el)
26223     {
26224         this.bindEl = el;
26225     },
26226       
26227     
26228     enter : function () {
26229        
26230         if (this.timeout != null) {
26231             clearTimeout(this.timeout);
26232         }
26233         
26234         this.hoverState = 'in';
26235          //Roo.log("enter - show");
26236         if (!this.delay || !this.delay.show) {
26237             this.show();
26238             return;
26239         }
26240         var _t = this;
26241         this.timeout = setTimeout(function () {
26242             if (_t.hoverState == 'in') {
26243                 _t.show();
26244             }
26245         }, this.delay.show);
26246     },
26247     leave : function()
26248     {
26249         clearTimeout(this.timeout);
26250     
26251         this.hoverState = 'out';
26252          if (!this.delay || !this.delay.hide) {
26253             this.hide();
26254             return;
26255         }
26256        
26257         var _t = this;
26258         this.timeout = setTimeout(function () {
26259             //Roo.log("leave - timeout");
26260             
26261             if (_t.hoverState == 'out') {
26262                 _t.hide();
26263                 Roo.bootstrap.Tooltip.currentEl = false;
26264             }
26265         }, delay);
26266     },
26267     
26268     show : function (msg)
26269     {
26270         if (!this.el) {
26271             this.render(document.body);
26272         }
26273         // set content.
26274         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26275         
26276         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26277         
26278         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26279         
26280         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26281         
26282         var placement = typeof this.placement == 'function' ?
26283             this.placement.call(this, this.el, on_el) :
26284             this.placement;
26285             
26286         var autoToken = /\s?auto?\s?/i;
26287         var autoPlace = autoToken.test(placement);
26288         if (autoPlace) {
26289             placement = placement.replace(autoToken, '') || 'top';
26290         }
26291         
26292         //this.el.detach()
26293         //this.el.setXY([0,0]);
26294         this.el.show();
26295         //this.el.dom.style.display='block';
26296         
26297         //this.el.appendTo(on_el);
26298         
26299         var p = this.getPosition();
26300         var box = this.el.getBox();
26301         
26302         if (autoPlace) {
26303             // fixme..
26304         }
26305         
26306         var align = this.alignment[placement];
26307         
26308         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26309         
26310         if(placement == 'top' || placement == 'bottom'){
26311             if(xy[0] < 0){
26312                 placement = 'right';
26313             }
26314             
26315             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26316                 placement = 'left';
26317             }
26318             
26319             var scroll = Roo.select('body', true).first().getScroll();
26320             
26321             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26322                 placement = 'top';
26323             }
26324             
26325             align = this.alignment[placement];
26326         }
26327         
26328         this.el.alignTo(this.bindEl, align[0],align[1]);
26329         //var arrow = this.el.select('.arrow',true).first();
26330         //arrow.set(align[2], 
26331         
26332         this.el.addClass(placement);
26333         
26334         this.el.addClass('in fade');
26335         
26336         this.hoverState = null;
26337         
26338         if (this.el.hasClass('fade')) {
26339             // fade it?
26340         }
26341         
26342     },
26343     hide : function()
26344     {
26345          
26346         if (!this.el) {
26347             return;
26348         }
26349         //this.el.setXY([0,0]);
26350         this.el.removeClass('in');
26351         //this.el.hide();
26352         
26353     }
26354     
26355 });
26356  
26357
26358  /*
26359  * - LGPL
26360  *
26361  * Location Picker
26362  * 
26363  */
26364
26365 /**
26366  * @class Roo.bootstrap.LocationPicker
26367  * @extends Roo.bootstrap.Component
26368  * Bootstrap LocationPicker class
26369  * @cfg {Number} latitude Position when init default 0
26370  * @cfg {Number} longitude Position when init default 0
26371  * @cfg {Number} zoom default 15
26372  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26373  * @cfg {Boolean} mapTypeControl default false
26374  * @cfg {Boolean} disableDoubleClickZoom default false
26375  * @cfg {Boolean} scrollwheel default true
26376  * @cfg {Boolean} streetViewControl default false
26377  * @cfg {Number} radius default 0
26378  * @cfg {String} locationName
26379  * @cfg {Boolean} draggable default true
26380  * @cfg {Boolean} enableAutocomplete default false
26381  * @cfg {Boolean} enableReverseGeocode default true
26382  * @cfg {String} markerTitle
26383  * 
26384  * @constructor
26385  * Create a new LocationPicker
26386  * @param {Object} config The config object
26387  */
26388
26389
26390 Roo.bootstrap.LocationPicker = function(config){
26391     
26392     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26393     
26394     this.addEvents({
26395         /**
26396          * @event initial
26397          * Fires when the picker initialized.
26398          * @param {Roo.bootstrap.LocationPicker} this
26399          * @param {Google Location} location
26400          */
26401         initial : true,
26402         /**
26403          * @event positionchanged
26404          * Fires when the picker position changed.
26405          * @param {Roo.bootstrap.LocationPicker} this
26406          * @param {Google Location} location
26407          */
26408         positionchanged : true,
26409         /**
26410          * @event resize
26411          * Fires when the map resize.
26412          * @param {Roo.bootstrap.LocationPicker} this
26413          */
26414         resize : true,
26415         /**
26416          * @event show
26417          * Fires when the map show.
26418          * @param {Roo.bootstrap.LocationPicker} this
26419          */
26420         show : true,
26421         /**
26422          * @event hide
26423          * Fires when the map hide.
26424          * @param {Roo.bootstrap.LocationPicker} this
26425          */
26426         hide : true,
26427         /**
26428          * @event mapClick
26429          * Fires when click the map.
26430          * @param {Roo.bootstrap.LocationPicker} this
26431          * @param {Map event} e
26432          */
26433         mapClick : true,
26434         /**
26435          * @event mapRightClick
26436          * Fires when right click the map.
26437          * @param {Roo.bootstrap.LocationPicker} this
26438          * @param {Map event} e
26439          */
26440         mapRightClick : true,
26441         /**
26442          * @event markerClick
26443          * Fires when click the marker.
26444          * @param {Roo.bootstrap.LocationPicker} this
26445          * @param {Map event} e
26446          */
26447         markerClick : true,
26448         /**
26449          * @event markerRightClick
26450          * Fires when right click the marker.
26451          * @param {Roo.bootstrap.LocationPicker} this
26452          * @param {Map event} e
26453          */
26454         markerRightClick : true,
26455         /**
26456          * @event OverlayViewDraw
26457          * Fires when OverlayView Draw
26458          * @param {Roo.bootstrap.LocationPicker} this
26459          */
26460         OverlayViewDraw : true,
26461         /**
26462          * @event OverlayViewOnAdd
26463          * Fires when OverlayView Draw
26464          * @param {Roo.bootstrap.LocationPicker} this
26465          */
26466         OverlayViewOnAdd : true,
26467         /**
26468          * @event OverlayViewOnRemove
26469          * Fires when OverlayView Draw
26470          * @param {Roo.bootstrap.LocationPicker} this
26471          */
26472         OverlayViewOnRemove : true,
26473         /**
26474          * @event OverlayViewShow
26475          * Fires when OverlayView Draw
26476          * @param {Roo.bootstrap.LocationPicker} this
26477          * @param {Pixel} cpx
26478          */
26479         OverlayViewShow : true,
26480         /**
26481          * @event OverlayViewHide
26482          * Fires when OverlayView Draw
26483          * @param {Roo.bootstrap.LocationPicker} this
26484          */
26485         OverlayViewHide : true,
26486         /**
26487          * @event loadexception
26488          * Fires when load google lib failed.
26489          * @param {Roo.bootstrap.LocationPicker} this
26490          */
26491         loadexception : true
26492     });
26493         
26494 };
26495
26496 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26497     
26498     gMapContext: false,
26499     
26500     latitude: 0,
26501     longitude: 0,
26502     zoom: 15,
26503     mapTypeId: false,
26504     mapTypeControl: false,
26505     disableDoubleClickZoom: false,
26506     scrollwheel: true,
26507     streetViewControl: false,
26508     radius: 0,
26509     locationName: '',
26510     draggable: true,
26511     enableAutocomplete: false,
26512     enableReverseGeocode: true,
26513     markerTitle: '',
26514     
26515     getAutoCreate: function()
26516     {
26517
26518         var cfg = {
26519             tag: 'div',
26520             cls: 'roo-location-picker'
26521         };
26522         
26523         return cfg
26524     },
26525     
26526     initEvents: function(ct, position)
26527     {       
26528         if(!this.el.getWidth() || this.isApplied()){
26529             return;
26530         }
26531         
26532         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26533         
26534         this.initial();
26535     },
26536     
26537     initial: function()
26538     {
26539         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26540             this.fireEvent('loadexception', this);
26541             return;
26542         }
26543         
26544         if(!this.mapTypeId){
26545             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26546         }
26547         
26548         this.gMapContext = this.GMapContext();
26549         
26550         this.initOverlayView();
26551         
26552         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26553         
26554         var _this = this;
26555                 
26556         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26557             _this.setPosition(_this.gMapContext.marker.position);
26558         });
26559         
26560         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26561             _this.fireEvent('mapClick', this, event);
26562             
26563         });
26564
26565         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26566             _this.fireEvent('mapRightClick', this, event);
26567             
26568         });
26569         
26570         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26571             _this.fireEvent('markerClick', this, event);
26572             
26573         });
26574
26575         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26576             _this.fireEvent('markerRightClick', this, event);
26577             
26578         });
26579         
26580         this.setPosition(this.gMapContext.location);
26581         
26582         this.fireEvent('initial', this, this.gMapContext.location);
26583     },
26584     
26585     initOverlayView: function()
26586     {
26587         var _this = this;
26588         
26589         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26590             
26591             draw: function()
26592             {
26593                 _this.fireEvent('OverlayViewDraw', _this);
26594             },
26595             
26596             onAdd: function()
26597             {
26598                 _this.fireEvent('OverlayViewOnAdd', _this);
26599             },
26600             
26601             onRemove: function()
26602             {
26603                 _this.fireEvent('OverlayViewOnRemove', _this);
26604             },
26605             
26606             show: function(cpx)
26607             {
26608                 _this.fireEvent('OverlayViewShow', _this, cpx);
26609             },
26610             
26611             hide: function()
26612             {
26613                 _this.fireEvent('OverlayViewHide', _this);
26614             }
26615             
26616         });
26617     },
26618     
26619     fromLatLngToContainerPixel: function(event)
26620     {
26621         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26622     },
26623     
26624     isApplied: function() 
26625     {
26626         return this.getGmapContext() == false ? false : true;
26627     },
26628     
26629     getGmapContext: function() 
26630     {
26631         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26632     },
26633     
26634     GMapContext: function() 
26635     {
26636         var position = new google.maps.LatLng(this.latitude, this.longitude);
26637         
26638         var _map = new google.maps.Map(this.el.dom, {
26639             center: position,
26640             zoom: this.zoom,
26641             mapTypeId: this.mapTypeId,
26642             mapTypeControl: this.mapTypeControl,
26643             disableDoubleClickZoom: this.disableDoubleClickZoom,
26644             scrollwheel: this.scrollwheel,
26645             streetViewControl: this.streetViewControl,
26646             locationName: this.locationName,
26647             draggable: this.draggable,
26648             enableAutocomplete: this.enableAutocomplete,
26649             enableReverseGeocode: this.enableReverseGeocode
26650         });
26651         
26652         var _marker = new google.maps.Marker({
26653             position: position,
26654             map: _map,
26655             title: this.markerTitle,
26656             draggable: this.draggable
26657         });
26658         
26659         return {
26660             map: _map,
26661             marker: _marker,
26662             circle: null,
26663             location: position,
26664             radius: this.radius,
26665             locationName: this.locationName,
26666             addressComponents: {
26667                 formatted_address: null,
26668                 addressLine1: null,
26669                 addressLine2: null,
26670                 streetName: null,
26671                 streetNumber: null,
26672                 city: null,
26673                 district: null,
26674                 state: null,
26675                 stateOrProvince: null
26676             },
26677             settings: this,
26678             domContainer: this.el.dom,
26679             geodecoder: new google.maps.Geocoder()
26680         };
26681     },
26682     
26683     drawCircle: function(center, radius, options) 
26684     {
26685         if (this.gMapContext.circle != null) {
26686             this.gMapContext.circle.setMap(null);
26687         }
26688         if (radius > 0) {
26689             radius *= 1;
26690             options = Roo.apply({}, options, {
26691                 strokeColor: "#0000FF",
26692                 strokeOpacity: .35,
26693                 strokeWeight: 2,
26694                 fillColor: "#0000FF",
26695                 fillOpacity: .2
26696             });
26697             
26698             options.map = this.gMapContext.map;
26699             options.radius = radius;
26700             options.center = center;
26701             this.gMapContext.circle = new google.maps.Circle(options);
26702             return this.gMapContext.circle;
26703         }
26704         
26705         return null;
26706     },
26707     
26708     setPosition: function(location) 
26709     {
26710         this.gMapContext.location = location;
26711         this.gMapContext.marker.setPosition(location);
26712         this.gMapContext.map.panTo(location);
26713         this.drawCircle(location, this.gMapContext.radius, {});
26714         
26715         var _this = this;
26716         
26717         if (this.gMapContext.settings.enableReverseGeocode) {
26718             this.gMapContext.geodecoder.geocode({
26719                 latLng: this.gMapContext.location
26720             }, function(results, status) {
26721                 
26722                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26723                     _this.gMapContext.locationName = results[0].formatted_address;
26724                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26725                     
26726                     _this.fireEvent('positionchanged', this, location);
26727                 }
26728             });
26729             
26730             return;
26731         }
26732         
26733         this.fireEvent('positionchanged', this, location);
26734     },
26735     
26736     resize: function()
26737     {
26738         google.maps.event.trigger(this.gMapContext.map, "resize");
26739         
26740         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26741         
26742         this.fireEvent('resize', this);
26743     },
26744     
26745     setPositionByLatLng: function(latitude, longitude)
26746     {
26747         this.setPosition(new google.maps.LatLng(latitude, longitude));
26748     },
26749     
26750     getCurrentPosition: function() 
26751     {
26752         return {
26753             latitude: this.gMapContext.location.lat(),
26754             longitude: this.gMapContext.location.lng()
26755         };
26756     },
26757     
26758     getAddressName: function() 
26759     {
26760         return this.gMapContext.locationName;
26761     },
26762     
26763     getAddressComponents: function() 
26764     {
26765         return this.gMapContext.addressComponents;
26766     },
26767     
26768     address_component_from_google_geocode: function(address_components) 
26769     {
26770         var result = {};
26771         
26772         for (var i = 0; i < address_components.length; i++) {
26773             var component = address_components[i];
26774             if (component.types.indexOf("postal_code") >= 0) {
26775                 result.postalCode = component.short_name;
26776             } else if (component.types.indexOf("street_number") >= 0) {
26777                 result.streetNumber = component.short_name;
26778             } else if (component.types.indexOf("route") >= 0) {
26779                 result.streetName = component.short_name;
26780             } else if (component.types.indexOf("neighborhood") >= 0) {
26781                 result.city = component.short_name;
26782             } else if (component.types.indexOf("locality") >= 0) {
26783                 result.city = component.short_name;
26784             } else if (component.types.indexOf("sublocality") >= 0) {
26785                 result.district = component.short_name;
26786             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26787                 result.stateOrProvince = component.short_name;
26788             } else if (component.types.indexOf("country") >= 0) {
26789                 result.country = component.short_name;
26790             }
26791         }
26792         
26793         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26794         result.addressLine2 = "";
26795         return result;
26796     },
26797     
26798     setZoomLevel: function(zoom)
26799     {
26800         this.gMapContext.map.setZoom(zoom);
26801     },
26802     
26803     show: function()
26804     {
26805         if(!this.el){
26806             return;
26807         }
26808         
26809         this.el.show();
26810         
26811         this.resize();
26812         
26813         this.fireEvent('show', this);
26814     },
26815     
26816     hide: function()
26817     {
26818         if(!this.el){
26819             return;
26820         }
26821         
26822         this.el.hide();
26823         
26824         this.fireEvent('hide', this);
26825     }
26826     
26827 });
26828
26829 Roo.apply(Roo.bootstrap.LocationPicker, {
26830     
26831     OverlayView : function(map, options)
26832     {
26833         options = options || {};
26834         
26835         this.setMap(map);
26836     }
26837     
26838     
26839 });/*
26840  * - LGPL
26841  *
26842  * Alert
26843  * 
26844  */
26845
26846 /**
26847  * @class Roo.bootstrap.Alert
26848  * @extends Roo.bootstrap.Component
26849  * Bootstrap Alert class
26850  * @cfg {String} title The title of alert
26851  * @cfg {String} html The content of alert
26852  * @cfg {String} weight (  success | info | warning | danger )
26853  * @cfg {String} faicon font-awesomeicon
26854  * 
26855  * @constructor
26856  * Create a new alert
26857  * @param {Object} config The config object
26858  */
26859
26860
26861 Roo.bootstrap.Alert = function(config){
26862     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26863     
26864 };
26865
26866 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26867     
26868     title: '',
26869     html: '',
26870     weight: false,
26871     faicon: false,
26872     
26873     getAutoCreate : function()
26874     {
26875         
26876         var cfg = {
26877             tag : 'div',
26878             cls : 'alert',
26879             cn : [
26880                 {
26881                     tag : 'i',
26882                     cls : 'roo-alert-icon'
26883                     
26884                 },
26885                 {
26886                     tag : 'b',
26887                     cls : 'roo-alert-title',
26888                     html : this.title
26889                 },
26890                 {
26891                     tag : 'span',
26892                     cls : 'roo-alert-text',
26893                     html : this.html
26894                 }
26895             ]
26896         };
26897         
26898         if(this.faicon){
26899             cfg.cn[0].cls += ' fa ' + this.faicon;
26900         }
26901         
26902         if(this.weight){
26903             cfg.cls += ' alert-' + this.weight;
26904         }
26905         
26906         return cfg;
26907     },
26908     
26909     initEvents: function() 
26910     {
26911         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26912     },
26913     
26914     setTitle : function(str)
26915     {
26916         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26917     },
26918     
26919     setText : function(str)
26920     {
26921         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26922     },
26923     
26924     setWeight : function(weight)
26925     {
26926         if(this.weight){
26927             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26928         }
26929         
26930         this.weight = weight;
26931         
26932         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26933     },
26934     
26935     setIcon : function(icon)
26936     {
26937         if(this.faicon){
26938             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26939         }
26940         
26941         this.faicon = icon;
26942         
26943         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26944     },
26945     
26946     hide: function() 
26947     {
26948         this.el.hide();   
26949     },
26950     
26951     show: function() 
26952     {  
26953         this.el.show();   
26954     }
26955     
26956 });
26957
26958  
26959 /*
26960 * Licence: LGPL
26961 */
26962
26963 /**
26964  * @class Roo.bootstrap.UploadCropbox
26965  * @extends Roo.bootstrap.Component
26966  * Bootstrap UploadCropbox class
26967  * @cfg {String} emptyText show when image has been loaded
26968  * @cfg {String} rotateNotify show when image too small to rotate
26969  * @cfg {Number} errorTimeout default 3000
26970  * @cfg {Number} minWidth default 300
26971  * @cfg {Number} minHeight default 300
26972  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26973  * @cfg {Boolean} isDocument (true|false) default false
26974  * @cfg {String} url action url
26975  * @cfg {String} paramName default 'imageUpload'
26976  * @cfg {String} method default POST
26977  * @cfg {Boolean} loadMask (true|false) default true
26978  * @cfg {Boolean} loadingText default 'Loading...'
26979  * 
26980  * @constructor
26981  * Create a new UploadCropbox
26982  * @param {Object} config The config object
26983  */
26984
26985 Roo.bootstrap.UploadCropbox = function(config){
26986     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26987     
26988     this.addEvents({
26989         /**
26990          * @event beforeselectfile
26991          * Fire before select file
26992          * @param {Roo.bootstrap.UploadCropbox} this
26993          */
26994         "beforeselectfile" : true,
26995         /**
26996          * @event initial
26997          * Fire after initEvent
26998          * @param {Roo.bootstrap.UploadCropbox} this
26999          */
27000         "initial" : true,
27001         /**
27002          * @event crop
27003          * Fire after initEvent
27004          * @param {Roo.bootstrap.UploadCropbox} this
27005          * @param {String} data
27006          */
27007         "crop" : true,
27008         /**
27009          * @event prepare
27010          * Fire when preparing the file data
27011          * @param {Roo.bootstrap.UploadCropbox} this
27012          * @param {Object} file
27013          */
27014         "prepare" : true,
27015         /**
27016          * @event exception
27017          * Fire when get exception
27018          * @param {Roo.bootstrap.UploadCropbox} this
27019          * @param {XMLHttpRequest} xhr
27020          */
27021         "exception" : true,
27022         /**
27023          * @event beforeloadcanvas
27024          * Fire before load the canvas
27025          * @param {Roo.bootstrap.UploadCropbox} this
27026          * @param {String} src
27027          */
27028         "beforeloadcanvas" : true,
27029         /**
27030          * @event trash
27031          * Fire when trash image
27032          * @param {Roo.bootstrap.UploadCropbox} this
27033          */
27034         "trash" : true,
27035         /**
27036          * @event download
27037          * Fire when download the image
27038          * @param {Roo.bootstrap.UploadCropbox} this
27039          */
27040         "download" : true,
27041         /**
27042          * @event footerbuttonclick
27043          * Fire when footerbuttonclick
27044          * @param {Roo.bootstrap.UploadCropbox} this
27045          * @param {String} type
27046          */
27047         "footerbuttonclick" : true,
27048         /**
27049          * @event resize
27050          * Fire when resize
27051          * @param {Roo.bootstrap.UploadCropbox} this
27052          */
27053         "resize" : true,
27054         /**
27055          * @event rotate
27056          * Fire when rotate the image
27057          * @param {Roo.bootstrap.UploadCropbox} this
27058          * @param {String} pos
27059          */
27060         "rotate" : true,
27061         /**
27062          * @event inspect
27063          * Fire when inspect the file
27064          * @param {Roo.bootstrap.UploadCropbox} this
27065          * @param {Object} file
27066          */
27067         "inspect" : true,
27068         /**
27069          * @event upload
27070          * Fire when xhr upload the file
27071          * @param {Roo.bootstrap.UploadCropbox} this
27072          * @param {Object} data
27073          */
27074         "upload" : true,
27075         /**
27076          * @event arrange
27077          * Fire when arrange the file data
27078          * @param {Roo.bootstrap.UploadCropbox} this
27079          * @param {Object} formData
27080          */
27081         "arrange" : true
27082     });
27083     
27084     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27085 };
27086
27087 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27088     
27089     emptyText : 'Click to upload image',
27090     rotateNotify : 'Image is too small to rotate',
27091     errorTimeout : 3000,
27092     scale : 0,
27093     baseScale : 1,
27094     rotate : 0,
27095     dragable : false,
27096     pinching : false,
27097     mouseX : 0,
27098     mouseY : 0,
27099     cropData : false,
27100     minWidth : 300,
27101     minHeight : 300,
27102     file : false,
27103     exif : {},
27104     baseRotate : 1,
27105     cropType : 'image/jpeg',
27106     buttons : false,
27107     canvasLoaded : false,
27108     isDocument : false,
27109     method : 'POST',
27110     paramName : 'imageUpload',
27111     loadMask : true,
27112     loadingText : 'Loading...',
27113     maskEl : false,
27114     
27115     getAutoCreate : function()
27116     {
27117         var cfg = {
27118             tag : 'div',
27119             cls : 'roo-upload-cropbox',
27120             cn : [
27121                 {
27122                     tag : 'input',
27123                     cls : 'roo-upload-cropbox-selector',
27124                     type : 'file'
27125                 },
27126                 {
27127                     tag : 'div',
27128                     cls : 'roo-upload-cropbox-body',
27129                     style : 'cursor:pointer',
27130                     cn : [
27131                         {
27132                             tag : 'div',
27133                             cls : 'roo-upload-cropbox-preview'
27134                         },
27135                         {
27136                             tag : 'div',
27137                             cls : 'roo-upload-cropbox-thumb'
27138                         },
27139                         {
27140                             tag : 'div',
27141                             cls : 'roo-upload-cropbox-empty-notify',
27142                             html : this.emptyText
27143                         },
27144                         {
27145                             tag : 'div',
27146                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27147                             html : this.rotateNotify
27148                         }
27149                     ]
27150                 },
27151                 {
27152                     tag : 'div',
27153                     cls : 'roo-upload-cropbox-footer',
27154                     cn : {
27155                         tag : 'div',
27156                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27157                         cn : []
27158                     }
27159                 }
27160             ]
27161         };
27162         
27163         return cfg;
27164     },
27165     
27166     onRender : function(ct, position)
27167     {
27168         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27169         
27170         if (this.buttons.length) {
27171             
27172             Roo.each(this.buttons, function(bb) {
27173                 
27174                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27175                 
27176                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27177                 
27178             }, this);
27179         }
27180         
27181         if(this.loadMask){
27182             this.maskEl = this.el;
27183         }
27184     },
27185     
27186     initEvents : function()
27187     {
27188         this.urlAPI = (window.createObjectURL && window) || 
27189                                 (window.URL && URL.revokeObjectURL && URL) || 
27190                                 (window.webkitURL && webkitURL);
27191                         
27192         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27193         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27194         
27195         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27196         this.selectorEl.hide();
27197         
27198         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27199         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27200         
27201         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27202         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27203         this.thumbEl.hide();
27204         
27205         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27206         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27207         
27208         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27209         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27210         this.errorEl.hide();
27211         
27212         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27213         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27214         this.footerEl.hide();
27215         
27216         this.setThumbBoxSize();
27217         
27218         this.bind();
27219         
27220         this.resize();
27221         
27222         this.fireEvent('initial', this);
27223     },
27224
27225     bind : function()
27226     {
27227         var _this = this;
27228         
27229         window.addEventListener("resize", function() { _this.resize(); } );
27230         
27231         this.bodyEl.on('click', this.beforeSelectFile, this);
27232         
27233         if(Roo.isTouch){
27234             this.bodyEl.on('touchstart', this.onTouchStart, this);
27235             this.bodyEl.on('touchmove', this.onTouchMove, this);
27236             this.bodyEl.on('touchend', this.onTouchEnd, this);
27237         }
27238         
27239         if(!Roo.isTouch){
27240             this.bodyEl.on('mousedown', this.onMouseDown, this);
27241             this.bodyEl.on('mousemove', this.onMouseMove, this);
27242             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27243             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27244             Roo.get(document).on('mouseup', this.onMouseUp, this);
27245         }
27246         
27247         this.selectorEl.on('change', this.onFileSelected, this);
27248     },
27249     
27250     reset : function()
27251     {    
27252         this.scale = 0;
27253         this.baseScale = 1;
27254         this.rotate = 0;
27255         this.baseRotate = 1;
27256         this.dragable = false;
27257         this.pinching = false;
27258         this.mouseX = 0;
27259         this.mouseY = 0;
27260         this.cropData = false;
27261         this.notifyEl.dom.innerHTML = this.emptyText;
27262         
27263         this.selectorEl.dom.value = '';
27264         
27265     },
27266     
27267     resize : function()
27268     {
27269         if(this.fireEvent('resize', this) != false){
27270             this.setThumbBoxPosition();
27271             this.setCanvasPosition();
27272         }
27273     },
27274     
27275     onFooterButtonClick : function(e, el, o, type)
27276     {
27277         switch (type) {
27278             case 'rotate-left' :
27279                 this.onRotateLeft(e);
27280                 break;
27281             case 'rotate-right' :
27282                 this.onRotateRight(e);
27283                 break;
27284             case 'picture' :
27285                 this.beforeSelectFile(e);
27286                 break;
27287             case 'trash' :
27288                 this.trash(e);
27289                 break;
27290             case 'crop' :
27291                 this.crop(e);
27292                 break;
27293             case 'download' :
27294                 this.download(e);
27295                 break;
27296             default :
27297                 break;
27298         }
27299         
27300         this.fireEvent('footerbuttonclick', this, type);
27301     },
27302     
27303     beforeSelectFile : function(e)
27304     {
27305         e.preventDefault();
27306         
27307         if(this.fireEvent('beforeselectfile', this) != false){
27308             this.selectorEl.dom.click();
27309         }
27310     },
27311     
27312     onFileSelected : function(e)
27313     {
27314         e.preventDefault();
27315         
27316         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27317             return;
27318         }
27319         
27320         var file = this.selectorEl.dom.files[0];
27321         
27322         if(this.fireEvent('inspect', this, file) != false){
27323             this.prepare(file);
27324         }
27325         
27326     },
27327     
27328     trash : function(e)
27329     {
27330         this.fireEvent('trash', this);
27331     },
27332     
27333     download : function(e)
27334     {
27335         this.fireEvent('download', this);
27336     },
27337     
27338     loadCanvas : function(src)
27339     {   
27340         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27341             
27342             this.reset();
27343             
27344             this.imageEl = document.createElement('img');
27345             
27346             var _this = this;
27347             
27348             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27349             
27350             this.imageEl.src = src;
27351         }
27352     },
27353     
27354     onLoadCanvas : function()
27355     {   
27356         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27357         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27358         
27359         this.bodyEl.un('click', this.beforeSelectFile, this);
27360         
27361         this.notifyEl.hide();
27362         this.thumbEl.show();
27363         this.footerEl.show();
27364         
27365         this.baseRotateLevel();
27366         
27367         if(this.isDocument){
27368             this.setThumbBoxSize();
27369         }
27370         
27371         this.setThumbBoxPosition();
27372         
27373         this.baseScaleLevel();
27374         
27375         this.draw();
27376         
27377         this.resize();
27378         
27379         this.canvasLoaded = true;
27380         
27381         if(this.loadMask){
27382             this.maskEl.unmask();
27383         }
27384         
27385     },
27386     
27387     setCanvasPosition : function()
27388     {   
27389         if(!this.canvasEl){
27390             return;
27391         }
27392         
27393         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27394         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27395         
27396         this.previewEl.setLeft(pw);
27397         this.previewEl.setTop(ph);
27398         
27399     },
27400     
27401     onMouseDown : function(e)
27402     {   
27403         e.stopEvent();
27404         
27405         this.dragable = true;
27406         this.pinching = false;
27407         
27408         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27409             this.dragable = false;
27410             return;
27411         }
27412         
27413         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27414         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27415         
27416     },
27417     
27418     onMouseMove : function(e)
27419     {   
27420         e.stopEvent();
27421         
27422         if(!this.canvasLoaded){
27423             return;
27424         }
27425         
27426         if (!this.dragable){
27427             return;
27428         }
27429         
27430         var minX = Math.ceil(this.thumbEl.getLeft(true));
27431         var minY = Math.ceil(this.thumbEl.getTop(true));
27432         
27433         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27434         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27435         
27436         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27437         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27438         
27439         x = x - this.mouseX;
27440         y = y - this.mouseY;
27441         
27442         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27443         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27444         
27445         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27446         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27447         
27448         this.previewEl.setLeft(bgX);
27449         this.previewEl.setTop(bgY);
27450         
27451         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27452         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27453     },
27454     
27455     onMouseUp : function(e)
27456     {   
27457         e.stopEvent();
27458         
27459         this.dragable = false;
27460     },
27461     
27462     onMouseWheel : function(e)
27463     {   
27464         e.stopEvent();
27465         
27466         this.startScale = this.scale;
27467         
27468         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27469         
27470         if(!this.zoomable()){
27471             this.scale = this.startScale;
27472             return;
27473         }
27474         
27475         this.draw();
27476         
27477         return;
27478     },
27479     
27480     zoomable : function()
27481     {
27482         var minScale = this.thumbEl.getWidth() / this.minWidth;
27483         
27484         if(this.minWidth < this.minHeight){
27485             minScale = this.thumbEl.getHeight() / this.minHeight;
27486         }
27487         
27488         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27489         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27490         
27491         if(
27492                 this.isDocument &&
27493                 (this.rotate == 0 || this.rotate == 180) && 
27494                 (
27495                     width > this.imageEl.OriginWidth || 
27496                     height > this.imageEl.OriginHeight ||
27497                     (width < this.minWidth && height < this.minHeight)
27498                 )
27499         ){
27500             return false;
27501         }
27502         
27503         if(
27504                 this.isDocument &&
27505                 (this.rotate == 90 || this.rotate == 270) && 
27506                 (
27507                     width > this.imageEl.OriginWidth || 
27508                     height > this.imageEl.OriginHeight ||
27509                     (width < this.minHeight && height < this.minWidth)
27510                 )
27511         ){
27512             return false;
27513         }
27514         
27515         if(
27516                 !this.isDocument &&
27517                 (this.rotate == 0 || this.rotate == 180) && 
27518                 (
27519                     width < this.minWidth || 
27520                     width > this.imageEl.OriginWidth || 
27521                     height < this.minHeight || 
27522                     height > this.imageEl.OriginHeight
27523                 )
27524         ){
27525             return false;
27526         }
27527         
27528         if(
27529                 !this.isDocument &&
27530                 (this.rotate == 90 || this.rotate == 270) && 
27531                 (
27532                     width < this.minHeight || 
27533                     width > this.imageEl.OriginWidth || 
27534                     height < this.minWidth || 
27535                     height > this.imageEl.OriginHeight
27536                 )
27537         ){
27538             return false;
27539         }
27540         
27541         return true;
27542         
27543     },
27544     
27545     onRotateLeft : function(e)
27546     {   
27547         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27548             
27549             var minScale = this.thumbEl.getWidth() / this.minWidth;
27550             
27551             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27552             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27553             
27554             this.startScale = this.scale;
27555             
27556             while (this.getScaleLevel() < minScale){
27557             
27558                 this.scale = this.scale + 1;
27559                 
27560                 if(!this.zoomable()){
27561                     break;
27562                 }
27563                 
27564                 if(
27565                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27566                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27567                 ){
27568                     continue;
27569                 }
27570                 
27571                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27572
27573                 this.draw();
27574                 
27575                 return;
27576             }
27577             
27578             this.scale = this.startScale;
27579             
27580             this.onRotateFail();
27581             
27582             return false;
27583         }
27584         
27585         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27586
27587         if(this.isDocument){
27588             this.setThumbBoxSize();
27589             this.setThumbBoxPosition();
27590             this.setCanvasPosition();
27591         }
27592         
27593         this.draw();
27594         
27595         this.fireEvent('rotate', this, 'left');
27596         
27597     },
27598     
27599     onRotateRight : function(e)
27600     {
27601         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27602             
27603             var minScale = this.thumbEl.getWidth() / this.minWidth;
27604         
27605             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27606             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27607             
27608             this.startScale = this.scale;
27609             
27610             while (this.getScaleLevel() < minScale){
27611             
27612                 this.scale = this.scale + 1;
27613                 
27614                 if(!this.zoomable()){
27615                     break;
27616                 }
27617                 
27618                 if(
27619                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27620                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27621                 ){
27622                     continue;
27623                 }
27624                 
27625                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27626
27627                 this.draw();
27628                 
27629                 return;
27630             }
27631             
27632             this.scale = this.startScale;
27633             
27634             this.onRotateFail();
27635             
27636             return false;
27637         }
27638         
27639         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27640
27641         if(this.isDocument){
27642             this.setThumbBoxSize();
27643             this.setThumbBoxPosition();
27644             this.setCanvasPosition();
27645         }
27646         
27647         this.draw();
27648         
27649         this.fireEvent('rotate', this, 'right');
27650     },
27651     
27652     onRotateFail : function()
27653     {
27654         this.errorEl.show(true);
27655         
27656         var _this = this;
27657         
27658         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27659     },
27660     
27661     draw : function()
27662     {
27663         this.previewEl.dom.innerHTML = '';
27664         
27665         var canvasEl = document.createElement("canvas");
27666         
27667         var contextEl = canvasEl.getContext("2d");
27668         
27669         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27670         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27671         var center = this.imageEl.OriginWidth / 2;
27672         
27673         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27674             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27675             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27676             center = this.imageEl.OriginHeight / 2;
27677         }
27678         
27679         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27680         
27681         contextEl.translate(center, center);
27682         contextEl.rotate(this.rotate * Math.PI / 180);
27683
27684         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27685         
27686         this.canvasEl = document.createElement("canvas");
27687         
27688         this.contextEl = this.canvasEl.getContext("2d");
27689         
27690         switch (this.rotate) {
27691             case 0 :
27692                 
27693                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27694                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27695                 
27696                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27697                 
27698                 break;
27699             case 90 : 
27700                 
27701                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27702                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27703                 
27704                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27705                     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);
27706                     break;
27707                 }
27708                 
27709                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27710                 
27711                 break;
27712             case 180 :
27713                 
27714                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27715                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27716                 
27717                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27718                     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);
27719                     break;
27720                 }
27721                 
27722                 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);
27723                 
27724                 break;
27725             case 270 :
27726                 
27727                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27728                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27729         
27730                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27731                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27732                     break;
27733                 }
27734                 
27735                 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);
27736                 
27737                 break;
27738             default : 
27739                 break;
27740         }
27741         
27742         this.previewEl.appendChild(this.canvasEl);
27743         
27744         this.setCanvasPosition();
27745     },
27746     
27747     crop : function()
27748     {
27749         if(!this.canvasLoaded){
27750             return;
27751         }
27752         
27753         var imageCanvas = document.createElement("canvas");
27754         
27755         var imageContext = imageCanvas.getContext("2d");
27756         
27757         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27758         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27759         
27760         var center = imageCanvas.width / 2;
27761         
27762         imageContext.translate(center, center);
27763         
27764         imageContext.rotate(this.rotate * Math.PI / 180);
27765         
27766         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27767         
27768         var canvas = document.createElement("canvas");
27769         
27770         var context = canvas.getContext("2d");
27771                 
27772         canvas.width = this.minWidth;
27773         canvas.height = this.minHeight;
27774
27775         switch (this.rotate) {
27776             case 0 :
27777                 
27778                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27779                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27780                 
27781                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27782                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27783                 
27784                 var targetWidth = this.minWidth - 2 * x;
27785                 var targetHeight = this.minHeight - 2 * y;
27786                 
27787                 var scale = 1;
27788                 
27789                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27790                     scale = targetWidth / width;
27791                 }
27792                 
27793                 if(x > 0 && y == 0){
27794                     scale = targetHeight / height;
27795                 }
27796                 
27797                 if(x > 0 && y > 0){
27798                     scale = targetWidth / width;
27799                     
27800                     if(width < height){
27801                         scale = targetHeight / height;
27802                     }
27803                 }
27804                 
27805                 context.scale(scale, scale);
27806                 
27807                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27808                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27809
27810                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27811                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27812
27813                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27814                 
27815                 break;
27816             case 90 : 
27817                 
27818                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27819                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27820                 
27821                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27822                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27823                 
27824                 var targetWidth = this.minWidth - 2 * x;
27825                 var targetHeight = this.minHeight - 2 * y;
27826                 
27827                 var scale = 1;
27828                 
27829                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27830                     scale = targetWidth / width;
27831                 }
27832                 
27833                 if(x > 0 && y == 0){
27834                     scale = targetHeight / height;
27835                 }
27836                 
27837                 if(x > 0 && y > 0){
27838                     scale = targetWidth / width;
27839                     
27840                     if(width < height){
27841                         scale = targetHeight / height;
27842                     }
27843                 }
27844                 
27845                 context.scale(scale, scale);
27846                 
27847                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27848                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27849
27850                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27851                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27852                 
27853                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27854                 
27855                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27856                 
27857                 break;
27858             case 180 :
27859                 
27860                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27861                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27862                 
27863                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27864                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27865                 
27866                 var targetWidth = this.minWidth - 2 * x;
27867                 var targetHeight = this.minHeight - 2 * y;
27868                 
27869                 var scale = 1;
27870                 
27871                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27872                     scale = targetWidth / width;
27873                 }
27874                 
27875                 if(x > 0 && y == 0){
27876                     scale = targetHeight / height;
27877                 }
27878                 
27879                 if(x > 0 && y > 0){
27880                     scale = targetWidth / width;
27881                     
27882                     if(width < height){
27883                         scale = targetHeight / height;
27884                     }
27885                 }
27886                 
27887                 context.scale(scale, scale);
27888                 
27889                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27890                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27891
27892                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27893                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27894
27895                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27896                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27897                 
27898                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27899                 
27900                 break;
27901             case 270 :
27902                 
27903                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27904                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27905                 
27906                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27907                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27908                 
27909                 var targetWidth = this.minWidth - 2 * x;
27910                 var targetHeight = this.minHeight - 2 * y;
27911                 
27912                 var scale = 1;
27913                 
27914                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27915                     scale = targetWidth / width;
27916                 }
27917                 
27918                 if(x > 0 && y == 0){
27919                     scale = targetHeight / height;
27920                 }
27921                 
27922                 if(x > 0 && y > 0){
27923                     scale = targetWidth / width;
27924                     
27925                     if(width < height){
27926                         scale = targetHeight / height;
27927                     }
27928                 }
27929                 
27930                 context.scale(scale, scale);
27931                 
27932                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27933                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27934
27935                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27936                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27937                 
27938                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27939                 
27940                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27941                 
27942                 break;
27943             default : 
27944                 break;
27945         }
27946         
27947         this.cropData = canvas.toDataURL(this.cropType);
27948         
27949         if(this.fireEvent('crop', this, this.cropData) !== false){
27950             this.process(this.file, this.cropData);
27951         }
27952         
27953         return;
27954         
27955     },
27956     
27957     setThumbBoxSize : function()
27958     {
27959         var width, height;
27960         
27961         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27962             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27963             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27964             
27965             this.minWidth = width;
27966             this.minHeight = height;
27967             
27968             if(this.rotate == 90 || this.rotate == 270){
27969                 this.minWidth = height;
27970                 this.minHeight = width;
27971             }
27972         }
27973         
27974         height = 300;
27975         width = Math.ceil(this.minWidth * height / this.minHeight);
27976         
27977         if(this.minWidth > this.minHeight){
27978             width = 300;
27979             height = Math.ceil(this.minHeight * width / this.minWidth);
27980         }
27981         
27982         this.thumbEl.setStyle({
27983             width : width + 'px',
27984             height : height + 'px'
27985         });
27986
27987         return;
27988             
27989     },
27990     
27991     setThumbBoxPosition : function()
27992     {
27993         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27994         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27995         
27996         this.thumbEl.setLeft(x);
27997         this.thumbEl.setTop(y);
27998         
27999     },
28000     
28001     baseRotateLevel : function()
28002     {
28003         this.baseRotate = 1;
28004         
28005         if(
28006                 typeof(this.exif) != 'undefined' &&
28007                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28008                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28009         ){
28010             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28011         }
28012         
28013         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28014         
28015     },
28016     
28017     baseScaleLevel : function()
28018     {
28019         var width, height;
28020         
28021         if(this.isDocument){
28022             
28023             if(this.baseRotate == 6 || this.baseRotate == 8){
28024             
28025                 height = this.thumbEl.getHeight();
28026                 this.baseScale = height / this.imageEl.OriginWidth;
28027
28028                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28029                     width = this.thumbEl.getWidth();
28030                     this.baseScale = width / this.imageEl.OriginHeight;
28031                 }
28032
28033                 return;
28034             }
28035
28036             height = this.thumbEl.getHeight();
28037             this.baseScale = height / this.imageEl.OriginHeight;
28038
28039             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28040                 width = this.thumbEl.getWidth();
28041                 this.baseScale = width / this.imageEl.OriginWidth;
28042             }
28043
28044             return;
28045         }
28046         
28047         if(this.baseRotate == 6 || this.baseRotate == 8){
28048             
28049             width = this.thumbEl.getHeight();
28050             this.baseScale = width / this.imageEl.OriginHeight;
28051             
28052             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28053                 height = this.thumbEl.getWidth();
28054                 this.baseScale = height / this.imageEl.OriginHeight;
28055             }
28056             
28057             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28058                 height = this.thumbEl.getWidth();
28059                 this.baseScale = height / this.imageEl.OriginHeight;
28060                 
28061                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28062                     width = this.thumbEl.getHeight();
28063                     this.baseScale = width / this.imageEl.OriginWidth;
28064                 }
28065             }
28066             
28067             return;
28068         }
28069         
28070         width = this.thumbEl.getWidth();
28071         this.baseScale = width / this.imageEl.OriginWidth;
28072         
28073         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28074             height = this.thumbEl.getHeight();
28075             this.baseScale = height / this.imageEl.OriginHeight;
28076         }
28077         
28078         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28079             
28080             height = this.thumbEl.getHeight();
28081             this.baseScale = height / this.imageEl.OriginHeight;
28082             
28083             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28084                 width = this.thumbEl.getWidth();
28085                 this.baseScale = width / this.imageEl.OriginWidth;
28086             }
28087             
28088         }
28089         
28090         return;
28091     },
28092     
28093     getScaleLevel : function()
28094     {
28095         return this.baseScale * Math.pow(1.1, this.scale);
28096     },
28097     
28098     onTouchStart : function(e)
28099     {
28100         if(!this.canvasLoaded){
28101             this.beforeSelectFile(e);
28102             return;
28103         }
28104         
28105         var touches = e.browserEvent.touches;
28106         
28107         if(!touches){
28108             return;
28109         }
28110         
28111         if(touches.length == 1){
28112             this.onMouseDown(e);
28113             return;
28114         }
28115         
28116         if(touches.length != 2){
28117             return;
28118         }
28119         
28120         var coords = [];
28121         
28122         for(var i = 0, finger; finger = touches[i]; i++){
28123             coords.push(finger.pageX, finger.pageY);
28124         }
28125         
28126         var x = Math.pow(coords[0] - coords[2], 2);
28127         var y = Math.pow(coords[1] - coords[3], 2);
28128         
28129         this.startDistance = Math.sqrt(x + y);
28130         
28131         this.startScale = this.scale;
28132         
28133         this.pinching = true;
28134         this.dragable = false;
28135         
28136     },
28137     
28138     onTouchMove : function(e)
28139     {
28140         if(!this.pinching && !this.dragable){
28141             return;
28142         }
28143         
28144         var touches = e.browserEvent.touches;
28145         
28146         if(!touches){
28147             return;
28148         }
28149         
28150         if(this.dragable){
28151             this.onMouseMove(e);
28152             return;
28153         }
28154         
28155         var coords = [];
28156         
28157         for(var i = 0, finger; finger = touches[i]; i++){
28158             coords.push(finger.pageX, finger.pageY);
28159         }
28160         
28161         var x = Math.pow(coords[0] - coords[2], 2);
28162         var y = Math.pow(coords[1] - coords[3], 2);
28163         
28164         this.endDistance = Math.sqrt(x + y);
28165         
28166         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28167         
28168         if(!this.zoomable()){
28169             this.scale = this.startScale;
28170             return;
28171         }
28172         
28173         this.draw();
28174         
28175     },
28176     
28177     onTouchEnd : function(e)
28178     {
28179         this.pinching = false;
28180         this.dragable = false;
28181         
28182     },
28183     
28184     process : function(file, crop)
28185     {
28186         if(this.loadMask){
28187             this.maskEl.mask(this.loadingText);
28188         }
28189         
28190         this.xhr = new XMLHttpRequest();
28191         
28192         file.xhr = this.xhr;
28193
28194         this.xhr.open(this.method, this.url, true);
28195         
28196         var headers = {
28197             "Accept": "application/json",
28198             "Cache-Control": "no-cache",
28199             "X-Requested-With": "XMLHttpRequest"
28200         };
28201         
28202         for (var headerName in headers) {
28203             var headerValue = headers[headerName];
28204             if (headerValue) {
28205                 this.xhr.setRequestHeader(headerName, headerValue);
28206             }
28207         }
28208         
28209         var _this = this;
28210         
28211         this.xhr.onload = function()
28212         {
28213             _this.xhrOnLoad(_this.xhr);
28214         }
28215         
28216         this.xhr.onerror = function()
28217         {
28218             _this.xhrOnError(_this.xhr);
28219         }
28220         
28221         var formData = new FormData();
28222
28223         formData.append('returnHTML', 'NO');
28224         
28225         if(crop){
28226             formData.append('crop', crop);
28227         }
28228         
28229         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28230             formData.append(this.paramName, file, file.name);
28231         }
28232         
28233         if(typeof(file.filename) != 'undefined'){
28234             formData.append('filename', file.filename);
28235         }
28236         
28237         if(typeof(file.mimetype) != 'undefined'){
28238             formData.append('mimetype', file.mimetype);
28239         }
28240         
28241         if(this.fireEvent('arrange', this, formData) != false){
28242             this.xhr.send(formData);
28243         };
28244     },
28245     
28246     xhrOnLoad : function(xhr)
28247     {
28248         if(this.loadMask){
28249             this.maskEl.unmask();
28250         }
28251         
28252         if (xhr.readyState !== 4) {
28253             this.fireEvent('exception', this, xhr);
28254             return;
28255         }
28256
28257         var response = Roo.decode(xhr.responseText);
28258         
28259         if(!response.success){
28260             this.fireEvent('exception', this, xhr);
28261             return;
28262         }
28263         
28264         var response = Roo.decode(xhr.responseText);
28265         
28266         this.fireEvent('upload', this, response);
28267         
28268     },
28269     
28270     xhrOnError : function()
28271     {
28272         if(this.loadMask){
28273             this.maskEl.unmask();
28274         }
28275         
28276         Roo.log('xhr on error');
28277         
28278         var response = Roo.decode(xhr.responseText);
28279           
28280         Roo.log(response);
28281         
28282     },
28283     
28284     prepare : function(file)
28285     {   
28286         if(this.loadMask){
28287             this.maskEl.mask(this.loadingText);
28288         }
28289         
28290         this.file = false;
28291         this.exif = {};
28292         
28293         if(typeof(file) === 'string'){
28294             this.loadCanvas(file);
28295             return;
28296         }
28297         
28298         if(!file || !this.urlAPI){
28299             return;
28300         }
28301         
28302         this.file = file;
28303         this.cropType = file.type;
28304         
28305         var _this = this;
28306         
28307         if(this.fireEvent('prepare', this, this.file) != false){
28308             
28309             var reader = new FileReader();
28310             
28311             reader.onload = function (e) {
28312                 if (e.target.error) {
28313                     Roo.log(e.target.error);
28314                     return;
28315                 }
28316                 
28317                 var buffer = e.target.result,
28318                     dataView = new DataView(buffer),
28319                     offset = 2,
28320                     maxOffset = dataView.byteLength - 4,
28321                     markerBytes,
28322                     markerLength;
28323                 
28324                 if (dataView.getUint16(0) === 0xffd8) {
28325                     while (offset < maxOffset) {
28326                         markerBytes = dataView.getUint16(offset);
28327                         
28328                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28329                             markerLength = dataView.getUint16(offset + 2) + 2;
28330                             if (offset + markerLength > dataView.byteLength) {
28331                                 Roo.log('Invalid meta data: Invalid segment size.');
28332                                 break;
28333                             }
28334                             
28335                             if(markerBytes == 0xffe1){
28336                                 _this.parseExifData(
28337                                     dataView,
28338                                     offset,
28339                                     markerLength
28340                                 );
28341                             }
28342                             
28343                             offset += markerLength;
28344                             
28345                             continue;
28346                         }
28347                         
28348                         break;
28349                     }
28350                     
28351                 }
28352                 
28353                 var url = _this.urlAPI.createObjectURL(_this.file);
28354                 
28355                 _this.loadCanvas(url);
28356                 
28357                 return;
28358             }
28359             
28360             reader.readAsArrayBuffer(this.file);
28361             
28362         }
28363         
28364     },
28365     
28366     parseExifData : function(dataView, offset, length)
28367     {
28368         var tiffOffset = offset + 10,
28369             littleEndian,
28370             dirOffset;
28371     
28372         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28373             // No Exif data, might be XMP data instead
28374             return;
28375         }
28376         
28377         // Check for the ASCII code for "Exif" (0x45786966):
28378         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28379             // No Exif data, might be XMP data instead
28380             return;
28381         }
28382         if (tiffOffset + 8 > dataView.byteLength) {
28383             Roo.log('Invalid Exif data: Invalid segment size.');
28384             return;
28385         }
28386         // Check for the two null bytes:
28387         if (dataView.getUint16(offset + 8) !== 0x0000) {
28388             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28389             return;
28390         }
28391         // Check the byte alignment:
28392         switch (dataView.getUint16(tiffOffset)) {
28393         case 0x4949:
28394             littleEndian = true;
28395             break;
28396         case 0x4D4D:
28397             littleEndian = false;
28398             break;
28399         default:
28400             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28401             return;
28402         }
28403         // Check for the TIFF tag marker (0x002A):
28404         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28405             Roo.log('Invalid Exif data: Missing TIFF marker.');
28406             return;
28407         }
28408         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28409         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28410         
28411         this.parseExifTags(
28412             dataView,
28413             tiffOffset,
28414             tiffOffset + dirOffset,
28415             littleEndian
28416         );
28417     },
28418     
28419     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28420     {
28421         var tagsNumber,
28422             dirEndOffset,
28423             i;
28424         if (dirOffset + 6 > dataView.byteLength) {
28425             Roo.log('Invalid Exif data: Invalid directory offset.');
28426             return;
28427         }
28428         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28429         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28430         if (dirEndOffset + 4 > dataView.byteLength) {
28431             Roo.log('Invalid Exif data: Invalid directory size.');
28432             return;
28433         }
28434         for (i = 0; i < tagsNumber; i += 1) {
28435             this.parseExifTag(
28436                 dataView,
28437                 tiffOffset,
28438                 dirOffset + 2 + 12 * i, // tag offset
28439                 littleEndian
28440             );
28441         }
28442         // Return the offset to the next directory:
28443         return dataView.getUint32(dirEndOffset, littleEndian);
28444     },
28445     
28446     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28447     {
28448         var tag = dataView.getUint16(offset, littleEndian);
28449         
28450         this.exif[tag] = this.getExifValue(
28451             dataView,
28452             tiffOffset,
28453             offset,
28454             dataView.getUint16(offset + 2, littleEndian), // tag type
28455             dataView.getUint32(offset + 4, littleEndian), // tag length
28456             littleEndian
28457         );
28458     },
28459     
28460     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28461     {
28462         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28463             tagSize,
28464             dataOffset,
28465             values,
28466             i,
28467             str,
28468             c;
28469     
28470         if (!tagType) {
28471             Roo.log('Invalid Exif data: Invalid tag type.');
28472             return;
28473         }
28474         
28475         tagSize = tagType.size * length;
28476         // Determine if the value is contained in the dataOffset bytes,
28477         // or if the value at the dataOffset is a pointer to the actual data:
28478         dataOffset = tagSize > 4 ?
28479                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28480         if (dataOffset + tagSize > dataView.byteLength) {
28481             Roo.log('Invalid Exif data: Invalid data offset.');
28482             return;
28483         }
28484         if (length === 1) {
28485             return tagType.getValue(dataView, dataOffset, littleEndian);
28486         }
28487         values = [];
28488         for (i = 0; i < length; i += 1) {
28489             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28490         }
28491         
28492         if (tagType.ascii) {
28493             str = '';
28494             // Concatenate the chars:
28495             for (i = 0; i < values.length; i += 1) {
28496                 c = values[i];
28497                 // Ignore the terminating NULL byte(s):
28498                 if (c === '\u0000') {
28499                     break;
28500                 }
28501                 str += c;
28502             }
28503             return str;
28504         }
28505         return values;
28506     }
28507     
28508 });
28509
28510 Roo.apply(Roo.bootstrap.UploadCropbox, {
28511     tags : {
28512         'Orientation': 0x0112
28513     },
28514     
28515     Orientation: {
28516             1: 0, //'top-left',
28517 //            2: 'top-right',
28518             3: 180, //'bottom-right',
28519 //            4: 'bottom-left',
28520 //            5: 'left-top',
28521             6: 90, //'right-top',
28522 //            7: 'right-bottom',
28523             8: 270 //'left-bottom'
28524     },
28525     
28526     exifTagTypes : {
28527         // byte, 8-bit unsigned int:
28528         1: {
28529             getValue: function (dataView, dataOffset) {
28530                 return dataView.getUint8(dataOffset);
28531             },
28532             size: 1
28533         },
28534         // ascii, 8-bit byte:
28535         2: {
28536             getValue: function (dataView, dataOffset) {
28537                 return String.fromCharCode(dataView.getUint8(dataOffset));
28538             },
28539             size: 1,
28540             ascii: true
28541         },
28542         // short, 16 bit int:
28543         3: {
28544             getValue: function (dataView, dataOffset, littleEndian) {
28545                 return dataView.getUint16(dataOffset, littleEndian);
28546             },
28547             size: 2
28548         },
28549         // long, 32 bit int:
28550         4: {
28551             getValue: function (dataView, dataOffset, littleEndian) {
28552                 return dataView.getUint32(dataOffset, littleEndian);
28553             },
28554             size: 4
28555         },
28556         // rational = two long values, first is numerator, second is denominator:
28557         5: {
28558             getValue: function (dataView, dataOffset, littleEndian) {
28559                 return dataView.getUint32(dataOffset, littleEndian) /
28560                     dataView.getUint32(dataOffset + 4, littleEndian);
28561             },
28562             size: 8
28563         },
28564         // slong, 32 bit signed int:
28565         9: {
28566             getValue: function (dataView, dataOffset, littleEndian) {
28567                 return dataView.getInt32(dataOffset, littleEndian);
28568             },
28569             size: 4
28570         },
28571         // srational, two slongs, first is numerator, second is denominator:
28572         10: {
28573             getValue: function (dataView, dataOffset, littleEndian) {
28574                 return dataView.getInt32(dataOffset, littleEndian) /
28575                     dataView.getInt32(dataOffset + 4, littleEndian);
28576             },
28577             size: 8
28578         }
28579     },
28580     
28581     footer : {
28582         STANDARD : [
28583             {
28584                 tag : 'div',
28585                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28586                 action : 'rotate-left',
28587                 cn : [
28588                     {
28589                         tag : 'button',
28590                         cls : 'btn btn-default',
28591                         html : '<i class="fa fa-undo"></i>'
28592                     }
28593                 ]
28594             },
28595             {
28596                 tag : 'div',
28597                 cls : 'btn-group roo-upload-cropbox-picture',
28598                 action : 'picture',
28599                 cn : [
28600                     {
28601                         tag : 'button',
28602                         cls : 'btn btn-default',
28603                         html : '<i class="fa fa-picture-o"></i>'
28604                     }
28605                 ]
28606             },
28607             {
28608                 tag : 'div',
28609                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28610                 action : 'rotate-right',
28611                 cn : [
28612                     {
28613                         tag : 'button',
28614                         cls : 'btn btn-default',
28615                         html : '<i class="fa fa-repeat"></i>'
28616                     }
28617                 ]
28618             }
28619         ],
28620         DOCUMENT : [
28621             {
28622                 tag : 'div',
28623                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28624                 action : 'rotate-left',
28625                 cn : [
28626                     {
28627                         tag : 'button',
28628                         cls : 'btn btn-default',
28629                         html : '<i class="fa fa-undo"></i>'
28630                     }
28631                 ]
28632             },
28633             {
28634                 tag : 'div',
28635                 cls : 'btn-group roo-upload-cropbox-download',
28636                 action : 'download',
28637                 cn : [
28638                     {
28639                         tag : 'button',
28640                         cls : 'btn btn-default',
28641                         html : '<i class="fa fa-download"></i>'
28642                     }
28643                 ]
28644             },
28645             {
28646                 tag : 'div',
28647                 cls : 'btn-group roo-upload-cropbox-crop',
28648                 action : 'crop',
28649                 cn : [
28650                     {
28651                         tag : 'button',
28652                         cls : 'btn btn-default',
28653                         html : '<i class="fa fa-crop"></i>'
28654                     }
28655                 ]
28656             },
28657             {
28658                 tag : 'div',
28659                 cls : 'btn-group roo-upload-cropbox-trash',
28660                 action : 'trash',
28661                 cn : [
28662                     {
28663                         tag : 'button',
28664                         cls : 'btn btn-default',
28665                         html : '<i class="fa fa-trash"></i>'
28666                     }
28667                 ]
28668             },
28669             {
28670                 tag : 'div',
28671                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28672                 action : 'rotate-right',
28673                 cn : [
28674                     {
28675                         tag : 'button',
28676                         cls : 'btn btn-default',
28677                         html : '<i class="fa fa-repeat"></i>'
28678                     }
28679                 ]
28680             }
28681         ],
28682         ROTATOR : [
28683             {
28684                 tag : 'div',
28685                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28686                 action : 'rotate-left',
28687                 cn : [
28688                     {
28689                         tag : 'button',
28690                         cls : 'btn btn-default',
28691                         html : '<i class="fa fa-undo"></i>'
28692                     }
28693                 ]
28694             },
28695             {
28696                 tag : 'div',
28697                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28698                 action : 'rotate-right',
28699                 cn : [
28700                     {
28701                         tag : 'button',
28702                         cls : 'btn btn-default',
28703                         html : '<i class="fa fa-repeat"></i>'
28704                     }
28705                 ]
28706             }
28707         ]
28708     }
28709 });
28710
28711 /*
28712 * Licence: LGPL
28713 */
28714
28715 /**
28716  * @class Roo.bootstrap.DocumentManager
28717  * @extends Roo.bootstrap.Component
28718  * Bootstrap DocumentManager class
28719  * @cfg {String} paramName default 'imageUpload'
28720  * @cfg {String} toolTipName default 'filename'
28721  * @cfg {String} method default POST
28722  * @cfg {String} url action url
28723  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28724  * @cfg {Boolean} multiple multiple upload default true
28725  * @cfg {Number} thumbSize default 300
28726  * @cfg {String} fieldLabel
28727  * @cfg {Number} labelWidth default 4
28728  * @cfg {String} labelAlign (left|top) default left
28729  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28730 * @cfg {Number} labellg set the width of label (1-12)
28731  * @cfg {Number} labelmd set the width of label (1-12)
28732  * @cfg {Number} labelsm set the width of label (1-12)
28733  * @cfg {Number} labelxs set the width of label (1-12)
28734  * 
28735  * @constructor
28736  * Create a new DocumentManager
28737  * @param {Object} config The config object
28738  */
28739
28740 Roo.bootstrap.DocumentManager = function(config){
28741     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28742     
28743     this.files = [];
28744     this.delegates = [];
28745     
28746     this.addEvents({
28747         /**
28748          * @event initial
28749          * Fire when initial the DocumentManager
28750          * @param {Roo.bootstrap.DocumentManager} this
28751          */
28752         "initial" : true,
28753         /**
28754          * @event inspect
28755          * inspect selected file
28756          * @param {Roo.bootstrap.DocumentManager} this
28757          * @param {File} file
28758          */
28759         "inspect" : true,
28760         /**
28761          * @event exception
28762          * Fire when xhr load exception
28763          * @param {Roo.bootstrap.DocumentManager} this
28764          * @param {XMLHttpRequest} xhr
28765          */
28766         "exception" : true,
28767         /**
28768          * @event afterupload
28769          * Fire when xhr load exception
28770          * @param {Roo.bootstrap.DocumentManager} this
28771          * @param {XMLHttpRequest} xhr
28772          */
28773         "afterupload" : true,
28774         /**
28775          * @event prepare
28776          * prepare the form data
28777          * @param {Roo.bootstrap.DocumentManager} this
28778          * @param {Object} formData
28779          */
28780         "prepare" : true,
28781         /**
28782          * @event remove
28783          * Fire when remove the file
28784          * @param {Roo.bootstrap.DocumentManager} this
28785          * @param {Object} file
28786          */
28787         "remove" : true,
28788         /**
28789          * @event refresh
28790          * Fire after refresh the file
28791          * @param {Roo.bootstrap.DocumentManager} this
28792          */
28793         "refresh" : true,
28794         /**
28795          * @event click
28796          * Fire after click the image
28797          * @param {Roo.bootstrap.DocumentManager} this
28798          * @param {Object} file
28799          */
28800         "click" : true,
28801         /**
28802          * @event edit
28803          * Fire when upload a image and editable set to true
28804          * @param {Roo.bootstrap.DocumentManager} this
28805          * @param {Object} file
28806          */
28807         "edit" : true,
28808         /**
28809          * @event beforeselectfile
28810          * Fire before select file
28811          * @param {Roo.bootstrap.DocumentManager} this
28812          */
28813         "beforeselectfile" : true,
28814         /**
28815          * @event process
28816          * Fire before process file
28817          * @param {Roo.bootstrap.DocumentManager} this
28818          * @param {Object} file
28819          */
28820         "process" : true,
28821         /**
28822          * @event previewrendered
28823          * Fire when preview rendered
28824          * @param {Roo.bootstrap.DocumentManager} this
28825          * @param {Object} file
28826          */
28827         "previewrendered" : true,
28828         /**
28829          */
28830         "previewResize" : true
28831         
28832     });
28833 };
28834
28835 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28836     
28837     boxes : 0,
28838     inputName : '',
28839     thumbSize : 300,
28840     multiple : true,
28841     files : false,
28842     method : 'POST',
28843     url : '',
28844     paramName : 'imageUpload',
28845     toolTipName : 'filename',
28846     fieldLabel : '',
28847     labelWidth : 4,
28848     labelAlign : 'left',
28849     editable : true,
28850     delegates : false,
28851     xhr : false, 
28852     
28853     labellg : 0,
28854     labelmd : 0,
28855     labelsm : 0,
28856     labelxs : 0,
28857     
28858     getAutoCreate : function()
28859     {   
28860         var managerWidget = {
28861             tag : 'div',
28862             cls : 'roo-document-manager',
28863             cn : [
28864                 {
28865                     tag : 'input',
28866                     cls : 'roo-document-manager-selector',
28867                     type : 'file'
28868                 },
28869                 {
28870                     tag : 'div',
28871                     cls : 'roo-document-manager-uploader',
28872                     cn : [
28873                         {
28874                             tag : 'div',
28875                             cls : 'roo-document-manager-upload-btn',
28876                             html : '<i class="fa fa-plus"></i>'
28877                         }
28878                     ]
28879                     
28880                 }
28881             ]
28882         };
28883         
28884         var content = [
28885             {
28886                 tag : 'div',
28887                 cls : 'column col-md-12',
28888                 cn : managerWidget
28889             }
28890         ];
28891         
28892         if(this.fieldLabel.length){
28893             
28894             content = [
28895                 {
28896                     tag : 'div',
28897                     cls : 'column col-md-12',
28898                     html : this.fieldLabel
28899                 },
28900                 {
28901                     tag : 'div',
28902                     cls : 'column col-md-12',
28903                     cn : managerWidget
28904                 }
28905             ];
28906
28907             if(this.labelAlign == 'left'){
28908                 content = [
28909                     {
28910                         tag : 'div',
28911                         cls : 'column',
28912                         html : this.fieldLabel
28913                     },
28914                     {
28915                         tag : 'div',
28916                         cls : 'column',
28917                         cn : managerWidget
28918                     }
28919                 ];
28920                 
28921                 if(this.labelWidth > 12){
28922                     content[0].style = "width: " + this.labelWidth + 'px';
28923                 }
28924
28925                 if(this.labelWidth < 13 && this.labelmd == 0){
28926                     this.labelmd = this.labelWidth;
28927                 }
28928
28929                 if(this.labellg > 0){
28930                     content[0].cls += ' col-lg-' + this.labellg;
28931                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28932                 }
28933
28934                 if(this.labelmd > 0){
28935                     content[0].cls += ' col-md-' + this.labelmd;
28936                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28937                 }
28938
28939                 if(this.labelsm > 0){
28940                     content[0].cls += ' col-sm-' + this.labelsm;
28941                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28942                 }
28943
28944                 if(this.labelxs > 0){
28945                     content[0].cls += ' col-xs-' + this.labelxs;
28946                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28947                 }
28948                 
28949             }
28950         }
28951         
28952         var cfg = {
28953             tag : 'div',
28954             cls : 'row clearfix',
28955             cn : content
28956         };
28957         
28958         return cfg;
28959         
28960     },
28961     
28962     initEvents : function()
28963     {
28964         this.managerEl = this.el.select('.roo-document-manager', true).first();
28965         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28966         
28967         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28968         this.selectorEl.hide();
28969         
28970         if(this.multiple){
28971             this.selectorEl.attr('multiple', 'multiple');
28972         }
28973         
28974         this.selectorEl.on('change', this.onFileSelected, this);
28975         
28976         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28977         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28978         
28979         this.uploader.on('click', this.onUploaderClick, this);
28980         
28981         this.renderProgressDialog();
28982         
28983         var _this = this;
28984         
28985         window.addEventListener("resize", function() { _this.refresh(); } );
28986         
28987         this.fireEvent('initial', this);
28988     },
28989     
28990     renderProgressDialog : function()
28991     {
28992         var _this = this;
28993         
28994         this.progressDialog = new Roo.bootstrap.Modal({
28995             cls : 'roo-document-manager-progress-dialog',
28996             allow_close : false,
28997             title : '',
28998             buttons : [
28999                 {
29000                     name  :'cancel',
29001                     weight : 'danger',
29002                     html : 'Cancel'
29003                 }
29004             ], 
29005             listeners : { 
29006                 btnclick : function() {
29007                     _this.uploadCancel();
29008                     this.hide();
29009                 }
29010             }
29011         });
29012          
29013         this.progressDialog.render(Roo.get(document.body));
29014          
29015         this.progress = new Roo.bootstrap.Progress({
29016             cls : 'roo-document-manager-progress',
29017             active : true,
29018             striped : true
29019         });
29020         
29021         this.progress.render(this.progressDialog.getChildContainer());
29022         
29023         this.progressBar = new Roo.bootstrap.ProgressBar({
29024             cls : 'roo-document-manager-progress-bar',
29025             aria_valuenow : 0,
29026             aria_valuemin : 0,
29027             aria_valuemax : 12,
29028             panel : 'success'
29029         });
29030         
29031         this.progressBar.render(this.progress.getChildContainer());
29032     },
29033     
29034     onUploaderClick : function(e)
29035     {
29036         e.preventDefault();
29037      
29038         if(this.fireEvent('beforeselectfile', this) != false){
29039             this.selectorEl.dom.click();
29040         }
29041         
29042     },
29043     
29044     onFileSelected : function(e)
29045     {
29046         e.preventDefault();
29047         
29048         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29049             return;
29050         }
29051         
29052         Roo.each(this.selectorEl.dom.files, function(file){
29053             if(this.fireEvent('inspect', this, file) != false){
29054                 this.files.push(file);
29055             }
29056         }, this);
29057         
29058         this.queue();
29059         
29060     },
29061     
29062     queue : function()
29063     {
29064         this.selectorEl.dom.value = '';
29065         
29066         if(!this.files || !this.files.length){
29067             return;
29068         }
29069         
29070         if(this.boxes > 0 && this.files.length > this.boxes){
29071             this.files = this.files.slice(0, this.boxes);
29072         }
29073         
29074         this.uploader.show();
29075         
29076         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29077             this.uploader.hide();
29078         }
29079         
29080         var _this = this;
29081         
29082         var files = [];
29083         
29084         var docs = [];
29085         
29086         Roo.each(this.files, function(file){
29087             
29088             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29089                 var f = this.renderPreview(file);
29090                 files.push(f);
29091                 return;
29092             }
29093             
29094             if(file.type.indexOf('image') != -1){
29095                 this.delegates.push(
29096                     (function(){
29097                         _this.process(file);
29098                     }).createDelegate(this)
29099                 );
29100         
29101                 return;
29102             }
29103             
29104             docs.push(
29105                 (function(){
29106                     _this.process(file);
29107                 }).createDelegate(this)
29108             );
29109             
29110         }, this);
29111         
29112         this.files = files;
29113         
29114         this.delegates = this.delegates.concat(docs);
29115         
29116         if(!this.delegates.length){
29117             this.refresh();
29118             return;
29119         }
29120         
29121         this.progressBar.aria_valuemax = this.delegates.length;
29122         
29123         this.arrange();
29124         
29125         return;
29126     },
29127     
29128     arrange : function()
29129     {
29130         if(!this.delegates.length){
29131             this.progressDialog.hide();
29132             this.refresh();
29133             return;
29134         }
29135         
29136         var delegate = this.delegates.shift();
29137         
29138         this.progressDialog.show();
29139         
29140         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29141         
29142         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29143         
29144         delegate();
29145     },
29146     
29147     refresh : function()
29148     {
29149         this.uploader.show();
29150         
29151         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29152             this.uploader.hide();
29153         }
29154         
29155         Roo.isTouch ? this.closable(false) : this.closable(true);
29156         
29157         this.fireEvent('refresh', this);
29158     },
29159     
29160     onRemove : function(e, el, o)
29161     {
29162         e.preventDefault();
29163         
29164         this.fireEvent('remove', this, o);
29165         
29166     },
29167     
29168     remove : function(o)
29169     {
29170         var files = [];
29171         
29172         Roo.each(this.files, function(file){
29173             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29174                 files.push(file);
29175                 return;
29176             }
29177
29178             o.target.remove();
29179
29180         }, this);
29181         
29182         this.files = files;
29183         
29184         this.refresh();
29185     },
29186     
29187     clear : function()
29188     {
29189         Roo.each(this.files, function(file){
29190             if(!file.target){
29191                 return;
29192             }
29193             
29194             file.target.remove();
29195
29196         }, this);
29197         
29198         this.files = [];
29199         
29200         this.refresh();
29201     },
29202     
29203     onClick : function(e, el, o)
29204     {
29205         e.preventDefault();
29206         
29207         this.fireEvent('click', this, o);
29208         
29209     },
29210     
29211     closable : function(closable)
29212     {
29213         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29214             
29215             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29216             
29217             if(closable){
29218                 el.show();
29219                 return;
29220             }
29221             
29222             el.hide();
29223             
29224         }, this);
29225     },
29226     
29227     xhrOnLoad : function(xhr)
29228     {
29229         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29230             el.remove();
29231         }, this);
29232         
29233         if (xhr.readyState !== 4) {
29234             this.arrange();
29235             this.fireEvent('exception', this, xhr);
29236             return;
29237         }
29238
29239         var response = Roo.decode(xhr.responseText);
29240         
29241         if(!response.success){
29242             this.arrange();
29243             this.fireEvent('exception', this, xhr);
29244             return;
29245         }
29246         
29247         var file = this.renderPreview(response.data);
29248         
29249         this.files.push(file);
29250         
29251         this.arrange();
29252         
29253         this.fireEvent('afterupload', this, xhr);
29254         
29255     },
29256     
29257     xhrOnError : function(xhr)
29258     {
29259         Roo.log('xhr on error');
29260         
29261         var response = Roo.decode(xhr.responseText);
29262           
29263         Roo.log(response);
29264         
29265         this.arrange();
29266     },
29267     
29268     process : function(file)
29269     {
29270         if(this.fireEvent('process', this, file) !== false){
29271             if(this.editable && file.type.indexOf('image') != -1){
29272                 this.fireEvent('edit', this, file);
29273                 return;
29274             }
29275
29276             this.uploadStart(file, false);
29277
29278             return;
29279         }
29280         
29281     },
29282     
29283     uploadStart : function(file, crop)
29284     {
29285         this.xhr = new XMLHttpRequest();
29286         
29287         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29288             this.arrange();
29289             return;
29290         }
29291         
29292         file.xhr = this.xhr;
29293             
29294         this.managerEl.createChild({
29295             tag : 'div',
29296             cls : 'roo-document-manager-loading',
29297             cn : [
29298                 {
29299                     tag : 'div',
29300                     tooltip : file.name,
29301                     cls : 'roo-document-manager-thumb',
29302                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29303                 }
29304             ]
29305
29306         });
29307
29308         this.xhr.open(this.method, this.url, true);
29309         
29310         var headers = {
29311             "Accept": "application/json",
29312             "Cache-Control": "no-cache",
29313             "X-Requested-With": "XMLHttpRequest"
29314         };
29315         
29316         for (var headerName in headers) {
29317             var headerValue = headers[headerName];
29318             if (headerValue) {
29319                 this.xhr.setRequestHeader(headerName, headerValue);
29320             }
29321         }
29322         
29323         var _this = this;
29324         
29325         this.xhr.onload = function()
29326         {
29327             _this.xhrOnLoad(_this.xhr);
29328         }
29329         
29330         this.xhr.onerror = function()
29331         {
29332             _this.xhrOnError(_this.xhr);
29333         }
29334         
29335         var formData = new FormData();
29336
29337         formData.append('returnHTML', 'NO');
29338         
29339         if(crop){
29340             formData.append('crop', crop);
29341         }
29342         
29343         formData.append(this.paramName, file, file.name);
29344         
29345         var options = {
29346             file : file, 
29347             manually : false
29348         };
29349         
29350         if(this.fireEvent('prepare', this, formData, options) != false){
29351             
29352             if(options.manually){
29353                 return;
29354             }
29355             
29356             this.xhr.send(formData);
29357             return;
29358         };
29359         
29360         this.uploadCancel();
29361     },
29362     
29363     uploadCancel : function()
29364     {
29365         if (this.xhr) {
29366             this.xhr.abort();
29367         }
29368         
29369         this.delegates = [];
29370         
29371         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29372             el.remove();
29373         }, this);
29374         
29375         this.arrange();
29376     },
29377     
29378     renderPreview : function(file)
29379     {
29380         if(typeof(file.target) != 'undefined' && file.target){
29381             return file;
29382         }
29383         
29384         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29385         
29386         var previewEl = this.managerEl.createChild({
29387             tag : 'div',
29388             cls : 'roo-document-manager-preview',
29389             cn : [
29390                 {
29391                     tag : 'div',
29392                     tooltip : file[this.toolTipName],
29393                     cls : 'roo-document-manager-thumb',
29394                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29395                 },
29396                 {
29397                     tag : 'button',
29398                     cls : 'close',
29399                     html : '<i class="fa fa-times-circle"></i>'
29400                 }
29401             ]
29402         });
29403
29404         var close = previewEl.select('button.close', true).first();
29405
29406         close.on('click', this.onRemove, this, file);
29407
29408         file.target = previewEl;
29409
29410         var image = previewEl.select('img', true).first();
29411         
29412         var _this = this;
29413         
29414         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29415         
29416         image.on('click', this.onClick, this, file);
29417         
29418         this.fireEvent('previewrendered', this, file);
29419         
29420         return file;
29421         
29422     },
29423     
29424     onPreviewLoad : function(file, image)
29425     {
29426         if(typeof(file.target) == 'undefined' || !file.target){
29427             return;
29428         }
29429         
29430         var width = image.dom.naturalWidth || image.dom.width;
29431         var height = image.dom.naturalHeight || image.dom.height;
29432         
29433         if(!this.previewResize) {
29434             return;
29435         }
29436         
29437         if(width > height){
29438             file.target.addClass('wide');
29439             return;
29440         }
29441         
29442         file.target.addClass('tall');
29443         return;
29444         
29445     },
29446     
29447     uploadFromSource : function(file, crop)
29448     {
29449         this.xhr = new XMLHttpRequest();
29450         
29451         this.managerEl.createChild({
29452             tag : 'div',
29453             cls : 'roo-document-manager-loading',
29454             cn : [
29455                 {
29456                     tag : 'div',
29457                     tooltip : file.name,
29458                     cls : 'roo-document-manager-thumb',
29459                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29460                 }
29461             ]
29462
29463         });
29464
29465         this.xhr.open(this.method, this.url, true);
29466         
29467         var headers = {
29468             "Accept": "application/json",
29469             "Cache-Control": "no-cache",
29470             "X-Requested-With": "XMLHttpRequest"
29471         };
29472         
29473         for (var headerName in headers) {
29474             var headerValue = headers[headerName];
29475             if (headerValue) {
29476                 this.xhr.setRequestHeader(headerName, headerValue);
29477             }
29478         }
29479         
29480         var _this = this;
29481         
29482         this.xhr.onload = function()
29483         {
29484             _this.xhrOnLoad(_this.xhr);
29485         }
29486         
29487         this.xhr.onerror = function()
29488         {
29489             _this.xhrOnError(_this.xhr);
29490         }
29491         
29492         var formData = new FormData();
29493
29494         formData.append('returnHTML', 'NO');
29495         
29496         formData.append('crop', crop);
29497         
29498         if(typeof(file.filename) != 'undefined'){
29499             formData.append('filename', file.filename);
29500         }
29501         
29502         if(typeof(file.mimetype) != 'undefined'){
29503             formData.append('mimetype', file.mimetype);
29504         }
29505         
29506         Roo.log(formData);
29507         
29508         if(this.fireEvent('prepare', this, formData) != false){
29509             this.xhr.send(formData);
29510         };
29511     }
29512 });
29513
29514 /*
29515 * Licence: LGPL
29516 */
29517
29518 /**
29519  * @class Roo.bootstrap.DocumentViewer
29520  * @extends Roo.bootstrap.Component
29521  * Bootstrap DocumentViewer class
29522  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29523  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29524  * 
29525  * @constructor
29526  * Create a new DocumentViewer
29527  * @param {Object} config The config object
29528  */
29529
29530 Roo.bootstrap.DocumentViewer = function(config){
29531     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29532     
29533     this.addEvents({
29534         /**
29535          * @event initial
29536          * Fire after initEvent
29537          * @param {Roo.bootstrap.DocumentViewer} this
29538          */
29539         "initial" : true,
29540         /**
29541          * @event click
29542          * Fire after click
29543          * @param {Roo.bootstrap.DocumentViewer} this
29544          */
29545         "click" : true,
29546         /**
29547          * @event download
29548          * Fire after download button
29549          * @param {Roo.bootstrap.DocumentViewer} this
29550          */
29551         "download" : true,
29552         /**
29553          * @event trash
29554          * Fire after trash button
29555          * @param {Roo.bootstrap.DocumentViewer} this
29556          */
29557         "trash" : true
29558         
29559     });
29560 };
29561
29562 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29563     
29564     showDownload : true,
29565     
29566     showTrash : true,
29567     
29568     getAutoCreate : function()
29569     {
29570         var cfg = {
29571             tag : 'div',
29572             cls : 'roo-document-viewer',
29573             cn : [
29574                 {
29575                     tag : 'div',
29576                     cls : 'roo-document-viewer-body',
29577                     cn : [
29578                         {
29579                             tag : 'div',
29580                             cls : 'roo-document-viewer-thumb',
29581                             cn : [
29582                                 {
29583                                     tag : 'img',
29584                                     cls : 'roo-document-viewer-image'
29585                                 }
29586                             ]
29587                         }
29588                     ]
29589                 },
29590                 {
29591                     tag : 'div',
29592                     cls : 'roo-document-viewer-footer',
29593                     cn : {
29594                         tag : 'div',
29595                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29596                         cn : [
29597                             {
29598                                 tag : 'div',
29599                                 cls : 'btn-group roo-document-viewer-download',
29600                                 cn : [
29601                                     {
29602                                         tag : 'button',
29603                                         cls : 'btn btn-default',
29604                                         html : '<i class="fa fa-download"></i>'
29605                                     }
29606                                 ]
29607                             },
29608                             {
29609                                 tag : 'div',
29610                                 cls : 'btn-group roo-document-viewer-trash',
29611                                 cn : [
29612                                     {
29613                                         tag : 'button',
29614                                         cls : 'btn btn-default',
29615                                         html : '<i class="fa fa-trash"></i>'
29616                                     }
29617                                 ]
29618                             }
29619                         ]
29620                     }
29621                 }
29622             ]
29623         };
29624         
29625         return cfg;
29626     },
29627     
29628     initEvents : function()
29629     {
29630         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29631         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29632         
29633         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29634         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29635         
29636         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29637         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29638         
29639         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29640         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29641         
29642         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29643         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29644         
29645         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29646         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29647         
29648         this.bodyEl.on('click', this.onClick, this);
29649         this.downloadBtn.on('click', this.onDownload, this);
29650         this.trashBtn.on('click', this.onTrash, this);
29651         
29652         this.downloadBtn.hide();
29653         this.trashBtn.hide();
29654         
29655         if(this.showDownload){
29656             this.downloadBtn.show();
29657         }
29658         
29659         if(this.showTrash){
29660             this.trashBtn.show();
29661         }
29662         
29663         if(!this.showDownload && !this.showTrash) {
29664             this.footerEl.hide();
29665         }
29666         
29667     },
29668     
29669     initial : function()
29670     {
29671         this.fireEvent('initial', this);
29672         
29673     },
29674     
29675     onClick : function(e)
29676     {
29677         e.preventDefault();
29678         
29679         this.fireEvent('click', this);
29680     },
29681     
29682     onDownload : function(e)
29683     {
29684         e.preventDefault();
29685         
29686         this.fireEvent('download', this);
29687     },
29688     
29689     onTrash : function(e)
29690     {
29691         e.preventDefault();
29692         
29693         this.fireEvent('trash', this);
29694     }
29695     
29696 });
29697 /*
29698  * - LGPL
29699  *
29700  * nav progress bar
29701  * 
29702  */
29703
29704 /**
29705  * @class Roo.bootstrap.NavProgressBar
29706  * @extends Roo.bootstrap.Component
29707  * Bootstrap NavProgressBar class
29708  * 
29709  * @constructor
29710  * Create a new nav progress bar
29711  * @param {Object} config The config object
29712  */
29713
29714 Roo.bootstrap.NavProgressBar = function(config){
29715     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29716
29717     this.bullets = this.bullets || [];
29718    
29719 //    Roo.bootstrap.NavProgressBar.register(this);
29720      this.addEvents({
29721         /**
29722              * @event changed
29723              * Fires when the active item changes
29724              * @param {Roo.bootstrap.NavProgressBar} this
29725              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29726              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29727          */
29728         'changed': true
29729      });
29730     
29731 };
29732
29733 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29734     
29735     bullets : [],
29736     barItems : [],
29737     
29738     getAutoCreate : function()
29739     {
29740         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29741         
29742         cfg = {
29743             tag : 'div',
29744             cls : 'roo-navigation-bar-group',
29745             cn : [
29746                 {
29747                     tag : 'div',
29748                     cls : 'roo-navigation-top-bar'
29749                 },
29750                 {
29751                     tag : 'div',
29752                     cls : 'roo-navigation-bullets-bar',
29753                     cn : [
29754                         {
29755                             tag : 'ul',
29756                             cls : 'roo-navigation-bar'
29757                         }
29758                     ]
29759                 },
29760                 
29761                 {
29762                     tag : 'div',
29763                     cls : 'roo-navigation-bottom-bar'
29764                 }
29765             ]
29766             
29767         };
29768         
29769         return cfg;
29770         
29771     },
29772     
29773     initEvents: function() 
29774     {
29775         
29776     },
29777     
29778     onRender : function(ct, position) 
29779     {
29780         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29781         
29782         if(this.bullets.length){
29783             Roo.each(this.bullets, function(b){
29784                this.addItem(b);
29785             }, this);
29786         }
29787         
29788         this.format();
29789         
29790     },
29791     
29792     addItem : function(cfg)
29793     {
29794         var item = new Roo.bootstrap.NavProgressItem(cfg);
29795         
29796         item.parentId = this.id;
29797         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29798         
29799         if(cfg.html){
29800             var top = new Roo.bootstrap.Element({
29801                 tag : 'div',
29802                 cls : 'roo-navigation-bar-text'
29803             });
29804             
29805             var bottom = new Roo.bootstrap.Element({
29806                 tag : 'div',
29807                 cls : 'roo-navigation-bar-text'
29808             });
29809             
29810             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29811             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29812             
29813             var topText = new Roo.bootstrap.Element({
29814                 tag : 'span',
29815                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29816             });
29817             
29818             var bottomText = new Roo.bootstrap.Element({
29819                 tag : 'span',
29820                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29821             });
29822             
29823             topText.onRender(top.el, null);
29824             bottomText.onRender(bottom.el, null);
29825             
29826             item.topEl = top;
29827             item.bottomEl = bottom;
29828         }
29829         
29830         this.barItems.push(item);
29831         
29832         return item;
29833     },
29834     
29835     getActive : function()
29836     {
29837         var active = false;
29838         
29839         Roo.each(this.barItems, function(v){
29840             
29841             if (!v.isActive()) {
29842                 return;
29843             }
29844             
29845             active = v;
29846             return false;
29847             
29848         });
29849         
29850         return active;
29851     },
29852     
29853     setActiveItem : function(item)
29854     {
29855         var prev = false;
29856         
29857         Roo.each(this.barItems, function(v){
29858             if (v.rid == item.rid) {
29859                 return ;
29860             }
29861             
29862             if (v.isActive()) {
29863                 v.setActive(false);
29864                 prev = v;
29865             }
29866         });
29867
29868         item.setActive(true);
29869         
29870         this.fireEvent('changed', this, item, prev);
29871     },
29872     
29873     getBarItem: function(rid)
29874     {
29875         var ret = false;
29876         
29877         Roo.each(this.barItems, function(e) {
29878             if (e.rid != rid) {
29879                 return;
29880             }
29881             
29882             ret =  e;
29883             return false;
29884         });
29885         
29886         return ret;
29887     },
29888     
29889     indexOfItem : function(item)
29890     {
29891         var index = false;
29892         
29893         Roo.each(this.barItems, function(v, i){
29894             
29895             if (v.rid != item.rid) {
29896                 return;
29897             }
29898             
29899             index = i;
29900             return false
29901         });
29902         
29903         return index;
29904     },
29905     
29906     setActiveNext : function()
29907     {
29908         var i = this.indexOfItem(this.getActive());
29909         
29910         if (i > this.barItems.length) {
29911             return;
29912         }
29913         
29914         this.setActiveItem(this.barItems[i+1]);
29915     },
29916     
29917     setActivePrev : function()
29918     {
29919         var i = this.indexOfItem(this.getActive());
29920         
29921         if (i  < 1) {
29922             return;
29923         }
29924         
29925         this.setActiveItem(this.barItems[i-1]);
29926     },
29927     
29928     format : function()
29929     {
29930         if(!this.barItems.length){
29931             return;
29932         }
29933      
29934         var width = 100 / this.barItems.length;
29935         
29936         Roo.each(this.barItems, function(i){
29937             i.el.setStyle('width', width + '%');
29938             i.topEl.el.setStyle('width', width + '%');
29939             i.bottomEl.el.setStyle('width', width + '%');
29940         }, this);
29941         
29942     }
29943     
29944 });
29945 /*
29946  * - LGPL
29947  *
29948  * Nav Progress Item
29949  * 
29950  */
29951
29952 /**
29953  * @class Roo.bootstrap.NavProgressItem
29954  * @extends Roo.bootstrap.Component
29955  * Bootstrap NavProgressItem class
29956  * @cfg {String} rid the reference id
29957  * @cfg {Boolean} active (true|false) Is item active default false
29958  * @cfg {Boolean} disabled (true|false) Is item active default false
29959  * @cfg {String} html
29960  * @cfg {String} position (top|bottom) text position default bottom
29961  * @cfg {String} icon show icon instead of number
29962  * 
29963  * @constructor
29964  * Create a new NavProgressItem
29965  * @param {Object} config The config object
29966  */
29967 Roo.bootstrap.NavProgressItem = function(config){
29968     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29969     this.addEvents({
29970         // raw events
29971         /**
29972          * @event click
29973          * The raw click event for the entire grid.
29974          * @param {Roo.bootstrap.NavProgressItem} this
29975          * @param {Roo.EventObject} e
29976          */
29977         "click" : true
29978     });
29979    
29980 };
29981
29982 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29983     
29984     rid : '',
29985     active : false,
29986     disabled : false,
29987     html : '',
29988     position : 'bottom',
29989     icon : false,
29990     
29991     getAutoCreate : function()
29992     {
29993         var iconCls = 'roo-navigation-bar-item-icon';
29994         
29995         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29996         
29997         var cfg = {
29998             tag: 'li',
29999             cls: 'roo-navigation-bar-item',
30000             cn : [
30001                 {
30002                     tag : 'i',
30003                     cls : iconCls
30004                 }
30005             ]
30006         };
30007         
30008         if(this.active){
30009             cfg.cls += ' active';
30010         }
30011         if(this.disabled){
30012             cfg.cls += ' disabled';
30013         }
30014         
30015         return cfg;
30016     },
30017     
30018     disable : function()
30019     {
30020         this.setDisabled(true);
30021     },
30022     
30023     enable : function()
30024     {
30025         this.setDisabled(false);
30026     },
30027     
30028     initEvents: function() 
30029     {
30030         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30031         
30032         this.iconEl.on('click', this.onClick, this);
30033     },
30034     
30035     onClick : function(e)
30036     {
30037         e.preventDefault();
30038         
30039         if(this.disabled){
30040             return;
30041         }
30042         
30043         if(this.fireEvent('click', this, e) === false){
30044             return;
30045         };
30046         
30047         this.parent().setActiveItem(this);
30048     },
30049     
30050     isActive: function () 
30051     {
30052         return this.active;
30053     },
30054     
30055     setActive : function(state)
30056     {
30057         if(this.active == state){
30058             return;
30059         }
30060         
30061         this.active = state;
30062         
30063         if (state) {
30064             this.el.addClass('active');
30065             return;
30066         }
30067         
30068         this.el.removeClass('active');
30069         
30070         return;
30071     },
30072     
30073     setDisabled : function(state)
30074     {
30075         if(this.disabled == state){
30076             return;
30077         }
30078         
30079         this.disabled = state;
30080         
30081         if (state) {
30082             this.el.addClass('disabled');
30083             return;
30084         }
30085         
30086         this.el.removeClass('disabled');
30087     },
30088     
30089     tooltipEl : function()
30090     {
30091         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30092     }
30093 });
30094  
30095
30096  /*
30097  * - LGPL
30098  *
30099  * FieldLabel
30100  * 
30101  */
30102
30103 /**
30104  * @class Roo.bootstrap.FieldLabel
30105  * @extends Roo.bootstrap.Component
30106  * Bootstrap FieldLabel class
30107  * @cfg {String} html contents of the element
30108  * @cfg {String} tag tag of the element default label
30109  * @cfg {String} cls class of the element
30110  * @cfg {String} target label target 
30111  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30112  * @cfg {String} invalidClass default "text-warning"
30113  * @cfg {String} validClass default "text-success"
30114  * @cfg {String} iconTooltip default "This field is required"
30115  * @cfg {String} indicatorpos (left|right) default left
30116  * 
30117  * @constructor
30118  * Create a new FieldLabel
30119  * @param {Object} config The config object
30120  */
30121
30122 Roo.bootstrap.FieldLabel = function(config){
30123     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30124     
30125     this.addEvents({
30126             /**
30127              * @event invalid
30128              * Fires after the field has been marked as invalid.
30129              * @param {Roo.form.FieldLabel} this
30130              * @param {String} msg The validation message
30131              */
30132             invalid : true,
30133             /**
30134              * @event valid
30135              * Fires after the field has been validated with no errors.
30136              * @param {Roo.form.FieldLabel} this
30137              */
30138             valid : true
30139         });
30140 };
30141
30142 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30143     
30144     tag: 'label',
30145     cls: '',
30146     html: '',
30147     target: '',
30148     allowBlank : true,
30149     invalidClass : 'has-warning',
30150     validClass : 'has-success',
30151     iconTooltip : 'This field is required',
30152     indicatorpos : 'left',
30153     
30154     getAutoCreate : function(){
30155         
30156         var cls = "";
30157         if (!this.allowBlank) {
30158             cls  = "visible";
30159         }
30160         
30161         var cfg = {
30162             tag : this.tag,
30163             cls : 'roo-bootstrap-field-label ' + this.cls,
30164             for : this.target,
30165             cn : [
30166                 {
30167                     tag : 'i',
30168                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30169                     tooltip : this.iconTooltip
30170                 },
30171                 {
30172                     tag : 'span',
30173                     html : this.html
30174                 }
30175             ] 
30176         };
30177         
30178         if(this.indicatorpos == 'right'){
30179             var cfg = {
30180                 tag : this.tag,
30181                 cls : 'roo-bootstrap-field-label ' + this.cls,
30182                 for : this.target,
30183                 cn : [
30184                     {
30185                         tag : 'span',
30186                         html : this.html
30187                     },
30188                     {
30189                         tag : 'i',
30190                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30191                         tooltip : this.iconTooltip
30192                     }
30193                 ] 
30194             };
30195         }
30196         
30197         return cfg;
30198     },
30199     
30200     initEvents: function() 
30201     {
30202         Roo.bootstrap.Element.superclass.initEvents.call(this);
30203         
30204         this.indicator = this.indicatorEl();
30205         
30206         if(this.indicator){
30207             this.indicator.removeClass('visible');
30208             this.indicator.addClass('invisible');
30209         }
30210         
30211         Roo.bootstrap.FieldLabel.register(this);
30212     },
30213     
30214     indicatorEl : function()
30215     {
30216         var indicator = this.el.select('i.roo-required-indicator',true).first();
30217         
30218         if(!indicator){
30219             return false;
30220         }
30221         
30222         return indicator;
30223         
30224     },
30225     
30226     /**
30227      * Mark this field as valid
30228      */
30229     markValid : function()
30230     {
30231         if(this.indicator){
30232             this.indicator.removeClass('visible');
30233             this.indicator.addClass('invisible');
30234         }
30235         
30236         this.el.removeClass(this.invalidClass);
30237         
30238         this.el.addClass(this.validClass);
30239         
30240         this.fireEvent('valid', this);
30241     },
30242     
30243     /**
30244      * Mark this field as invalid
30245      * @param {String} msg The validation message
30246      */
30247     markInvalid : function(msg)
30248     {
30249         if(this.indicator){
30250             this.indicator.removeClass('invisible');
30251             this.indicator.addClass('visible');
30252         }
30253         
30254         this.el.removeClass(this.validClass);
30255         
30256         this.el.addClass(this.invalidClass);
30257         
30258         this.fireEvent('invalid', this, msg);
30259     }
30260     
30261    
30262 });
30263
30264 Roo.apply(Roo.bootstrap.FieldLabel, {
30265     
30266     groups: {},
30267     
30268      /**
30269     * register a FieldLabel Group
30270     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30271     */
30272     register : function(label)
30273     {
30274         if(this.groups.hasOwnProperty(label.target)){
30275             return;
30276         }
30277      
30278         this.groups[label.target] = label;
30279         
30280     },
30281     /**
30282     * fetch a FieldLabel Group based on the target
30283     * @param {string} target
30284     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30285     */
30286     get: function(target) {
30287         if (typeof(this.groups[target]) == 'undefined') {
30288             return false;
30289         }
30290         
30291         return this.groups[target] ;
30292     }
30293 });
30294
30295  
30296
30297  /*
30298  * - LGPL
30299  *
30300  * page DateSplitField.
30301  * 
30302  */
30303
30304
30305 /**
30306  * @class Roo.bootstrap.DateSplitField
30307  * @extends Roo.bootstrap.Component
30308  * Bootstrap DateSplitField class
30309  * @cfg {string} fieldLabel - the label associated
30310  * @cfg {Number} labelWidth set the width of label (0-12)
30311  * @cfg {String} labelAlign (top|left)
30312  * @cfg {Boolean} dayAllowBlank (true|false) default false
30313  * @cfg {Boolean} monthAllowBlank (true|false) default false
30314  * @cfg {Boolean} yearAllowBlank (true|false) default false
30315  * @cfg {string} dayPlaceholder 
30316  * @cfg {string} monthPlaceholder
30317  * @cfg {string} yearPlaceholder
30318  * @cfg {string} dayFormat default 'd'
30319  * @cfg {string} monthFormat default 'm'
30320  * @cfg {string} yearFormat default 'Y'
30321  * @cfg {Number} labellg set the width of label (1-12)
30322  * @cfg {Number} labelmd set the width of label (1-12)
30323  * @cfg {Number} labelsm set the width of label (1-12)
30324  * @cfg {Number} labelxs set the width of label (1-12)
30325
30326  *     
30327  * @constructor
30328  * Create a new DateSplitField
30329  * @param {Object} config The config object
30330  */
30331
30332 Roo.bootstrap.DateSplitField = function(config){
30333     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30334     
30335     this.addEvents({
30336         // raw events
30337          /**
30338          * @event years
30339          * getting the data of years
30340          * @param {Roo.bootstrap.DateSplitField} this
30341          * @param {Object} years
30342          */
30343         "years" : true,
30344         /**
30345          * @event days
30346          * getting the data of days
30347          * @param {Roo.bootstrap.DateSplitField} this
30348          * @param {Object} days
30349          */
30350         "days" : true,
30351         /**
30352          * @event invalid
30353          * Fires after the field has been marked as invalid.
30354          * @param {Roo.form.Field} this
30355          * @param {String} msg The validation message
30356          */
30357         invalid : true,
30358        /**
30359          * @event valid
30360          * Fires after the field has been validated with no errors.
30361          * @param {Roo.form.Field} this
30362          */
30363         valid : true
30364     });
30365 };
30366
30367 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30368     
30369     fieldLabel : '',
30370     labelAlign : 'top',
30371     labelWidth : 3,
30372     dayAllowBlank : false,
30373     monthAllowBlank : false,
30374     yearAllowBlank : false,
30375     dayPlaceholder : '',
30376     monthPlaceholder : '',
30377     yearPlaceholder : '',
30378     dayFormat : 'd',
30379     monthFormat : 'm',
30380     yearFormat : 'Y',
30381     isFormField : true,
30382     labellg : 0,
30383     labelmd : 0,
30384     labelsm : 0,
30385     labelxs : 0,
30386     
30387     getAutoCreate : function()
30388     {
30389         var cfg = {
30390             tag : 'div',
30391             cls : 'row roo-date-split-field-group',
30392             cn : [
30393                 {
30394                     tag : 'input',
30395                     type : 'hidden',
30396                     cls : 'form-hidden-field roo-date-split-field-group-value',
30397                     name : this.name
30398                 }
30399             ]
30400         };
30401         
30402         var labelCls = 'col-md-12';
30403         var contentCls = 'col-md-4';
30404         
30405         if(this.fieldLabel){
30406             
30407             var label = {
30408                 tag : 'div',
30409                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30410                 cn : [
30411                     {
30412                         tag : 'label',
30413                         html : this.fieldLabel
30414                     }
30415                 ]
30416             };
30417             
30418             if(this.labelAlign == 'left'){
30419             
30420                 if(this.labelWidth > 12){
30421                     label.style = "width: " + this.labelWidth + 'px';
30422                 }
30423
30424                 if(this.labelWidth < 13 && this.labelmd == 0){
30425                     this.labelmd = this.labelWidth;
30426                 }
30427
30428                 if(this.labellg > 0){
30429                     labelCls = ' col-lg-' + this.labellg;
30430                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30431                 }
30432
30433                 if(this.labelmd > 0){
30434                     labelCls = ' col-md-' + this.labelmd;
30435                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30436                 }
30437
30438                 if(this.labelsm > 0){
30439                     labelCls = ' col-sm-' + this.labelsm;
30440                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30441                 }
30442
30443                 if(this.labelxs > 0){
30444                     labelCls = ' col-xs-' + this.labelxs;
30445                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30446                 }
30447             }
30448             
30449             label.cls += ' ' + labelCls;
30450             
30451             cfg.cn.push(label);
30452         }
30453         
30454         Roo.each(['day', 'month', 'year'], function(t){
30455             cfg.cn.push({
30456                 tag : 'div',
30457                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30458             });
30459         }, this);
30460         
30461         return cfg;
30462     },
30463     
30464     inputEl: function ()
30465     {
30466         return this.el.select('.roo-date-split-field-group-value', true).first();
30467     },
30468     
30469     onRender : function(ct, position) 
30470     {
30471         var _this = this;
30472         
30473         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30474         
30475         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30476         
30477         this.dayField = new Roo.bootstrap.ComboBox({
30478             allowBlank : this.dayAllowBlank,
30479             alwaysQuery : true,
30480             displayField : 'value',
30481             editable : false,
30482             fieldLabel : '',
30483             forceSelection : true,
30484             mode : 'local',
30485             placeholder : this.dayPlaceholder,
30486             selectOnFocus : true,
30487             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30488             triggerAction : 'all',
30489             typeAhead : true,
30490             valueField : 'value',
30491             store : new Roo.data.SimpleStore({
30492                 data : (function() {    
30493                     var days = [];
30494                     _this.fireEvent('days', _this, days);
30495                     return days;
30496                 })(),
30497                 fields : [ 'value' ]
30498             }),
30499             listeners : {
30500                 select : function (_self, record, index)
30501                 {
30502                     _this.setValue(_this.getValue());
30503                 }
30504             }
30505         });
30506
30507         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30508         
30509         this.monthField = new Roo.bootstrap.MonthField({
30510             after : '<i class=\"fa fa-calendar\"></i>',
30511             allowBlank : this.monthAllowBlank,
30512             placeholder : this.monthPlaceholder,
30513             readOnly : true,
30514             listeners : {
30515                 render : function (_self)
30516                 {
30517                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30518                         e.preventDefault();
30519                         _self.focus();
30520                     });
30521                 },
30522                 select : function (_self, oldvalue, newvalue)
30523                 {
30524                     _this.setValue(_this.getValue());
30525                 }
30526             }
30527         });
30528         
30529         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30530         
30531         this.yearField = new Roo.bootstrap.ComboBox({
30532             allowBlank : this.yearAllowBlank,
30533             alwaysQuery : true,
30534             displayField : 'value',
30535             editable : false,
30536             fieldLabel : '',
30537             forceSelection : true,
30538             mode : 'local',
30539             placeholder : this.yearPlaceholder,
30540             selectOnFocus : true,
30541             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30542             triggerAction : 'all',
30543             typeAhead : true,
30544             valueField : 'value',
30545             store : new Roo.data.SimpleStore({
30546                 data : (function() {
30547                     var years = [];
30548                     _this.fireEvent('years', _this, years);
30549                     return years;
30550                 })(),
30551                 fields : [ 'value' ]
30552             }),
30553             listeners : {
30554                 select : function (_self, record, index)
30555                 {
30556                     _this.setValue(_this.getValue());
30557                 }
30558             }
30559         });
30560
30561         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30562     },
30563     
30564     setValue : function(v, format)
30565     {
30566         this.inputEl.dom.value = v;
30567         
30568         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30569         
30570         var d = Date.parseDate(v, f);
30571         
30572         if(!d){
30573             this.validate();
30574             return;
30575         }
30576         
30577         this.setDay(d.format(this.dayFormat));
30578         this.setMonth(d.format(this.monthFormat));
30579         this.setYear(d.format(this.yearFormat));
30580         
30581         this.validate();
30582         
30583         return;
30584     },
30585     
30586     setDay : function(v)
30587     {
30588         this.dayField.setValue(v);
30589         this.inputEl.dom.value = this.getValue();
30590         this.validate();
30591         return;
30592     },
30593     
30594     setMonth : function(v)
30595     {
30596         this.monthField.setValue(v, true);
30597         this.inputEl.dom.value = this.getValue();
30598         this.validate();
30599         return;
30600     },
30601     
30602     setYear : function(v)
30603     {
30604         this.yearField.setValue(v);
30605         this.inputEl.dom.value = this.getValue();
30606         this.validate();
30607         return;
30608     },
30609     
30610     getDay : function()
30611     {
30612         return this.dayField.getValue();
30613     },
30614     
30615     getMonth : function()
30616     {
30617         return this.monthField.getValue();
30618     },
30619     
30620     getYear : function()
30621     {
30622         return this.yearField.getValue();
30623     },
30624     
30625     getValue : function()
30626     {
30627         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30628         
30629         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30630         
30631         return date;
30632     },
30633     
30634     reset : function()
30635     {
30636         this.setDay('');
30637         this.setMonth('');
30638         this.setYear('');
30639         this.inputEl.dom.value = '';
30640         this.validate();
30641         return;
30642     },
30643     
30644     validate : function()
30645     {
30646         var d = this.dayField.validate();
30647         var m = this.monthField.validate();
30648         var y = this.yearField.validate();
30649         
30650         var valid = true;
30651         
30652         if(
30653                 (!this.dayAllowBlank && !d) ||
30654                 (!this.monthAllowBlank && !m) ||
30655                 (!this.yearAllowBlank && !y)
30656         ){
30657             valid = false;
30658         }
30659         
30660         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30661             return valid;
30662         }
30663         
30664         if(valid){
30665             this.markValid();
30666             return valid;
30667         }
30668         
30669         this.markInvalid();
30670         
30671         return valid;
30672     },
30673     
30674     markValid : function()
30675     {
30676         
30677         var label = this.el.select('label', true).first();
30678         var icon = this.el.select('i.fa-star', true).first();
30679
30680         if(label && icon){
30681             icon.remove();
30682         }
30683         
30684         this.fireEvent('valid', this);
30685     },
30686     
30687      /**
30688      * Mark this field as invalid
30689      * @param {String} msg The validation message
30690      */
30691     markInvalid : function(msg)
30692     {
30693         
30694         var label = this.el.select('label', true).first();
30695         var icon = this.el.select('i.fa-star', true).first();
30696
30697         if(label && !icon){
30698             this.el.select('.roo-date-split-field-label', true).createChild({
30699                 tag : 'i',
30700                 cls : 'text-danger fa fa-lg fa-star',
30701                 tooltip : 'This field is required',
30702                 style : 'margin-right:5px;'
30703             }, label, true);
30704         }
30705         
30706         this.fireEvent('invalid', this, msg);
30707     },
30708     
30709     clearInvalid : function()
30710     {
30711         var label = this.el.select('label', true).first();
30712         var icon = this.el.select('i.fa-star', true).first();
30713
30714         if(label && icon){
30715             icon.remove();
30716         }
30717         
30718         this.fireEvent('valid', this);
30719     },
30720     
30721     getName: function()
30722     {
30723         return this.name;
30724     }
30725     
30726 });
30727
30728  /**
30729  *
30730  * This is based on 
30731  * http://masonry.desandro.com
30732  *
30733  * The idea is to render all the bricks based on vertical width...
30734  *
30735  * The original code extends 'outlayer' - we might need to use that....
30736  * 
30737  */
30738
30739
30740 /**
30741  * @class Roo.bootstrap.LayoutMasonry
30742  * @extends Roo.bootstrap.Component
30743  * Bootstrap Layout Masonry class
30744  * 
30745  * @constructor
30746  * Create a new Element
30747  * @param {Object} config The config object
30748  */
30749
30750 Roo.bootstrap.LayoutMasonry = function(config){
30751     
30752     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30753     
30754     this.bricks = [];
30755     
30756     Roo.bootstrap.LayoutMasonry.register(this);
30757     
30758     this.addEvents({
30759         // raw events
30760         /**
30761          * @event layout
30762          * Fire after layout the items
30763          * @param {Roo.bootstrap.LayoutMasonry} this
30764          * @param {Roo.EventObject} e
30765          */
30766         "layout" : true
30767     });
30768     
30769 };
30770
30771 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30772     
30773     /**
30774      * @cfg {Boolean} isLayoutInstant = no animation?
30775      */   
30776     isLayoutInstant : false, // needed?
30777    
30778     /**
30779      * @cfg {Number} boxWidth  width of the columns
30780      */   
30781     boxWidth : 450,
30782     
30783       /**
30784      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30785      */   
30786     boxHeight : 0,
30787     
30788     /**
30789      * @cfg {Number} padWidth padding below box..
30790      */   
30791     padWidth : 10, 
30792     
30793     /**
30794      * @cfg {Number} gutter gutter width..
30795      */   
30796     gutter : 10,
30797     
30798      /**
30799      * @cfg {Number} maxCols maximum number of columns
30800      */   
30801     
30802     maxCols: 0,
30803     
30804     /**
30805      * @cfg {Boolean} isAutoInitial defalut true
30806      */   
30807     isAutoInitial : true, 
30808     
30809     containerWidth: 0,
30810     
30811     /**
30812      * @cfg {Boolean} isHorizontal defalut false
30813      */   
30814     isHorizontal : false, 
30815
30816     currentSize : null,
30817     
30818     tag: 'div',
30819     
30820     cls: '',
30821     
30822     bricks: null, //CompositeElement
30823     
30824     cols : 1,
30825     
30826     _isLayoutInited : false,
30827     
30828 //    isAlternative : false, // only use for vertical layout...
30829     
30830     /**
30831      * @cfg {Number} alternativePadWidth padding below box..
30832      */   
30833     alternativePadWidth : 50,
30834     
30835     selectedBrick : [],
30836     
30837     getAutoCreate : function(){
30838         
30839         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30840         
30841         var cfg = {
30842             tag: this.tag,
30843             cls: 'blog-masonary-wrapper ' + this.cls,
30844             cn : {
30845                 cls : 'mas-boxes masonary'
30846             }
30847         };
30848         
30849         return cfg;
30850     },
30851     
30852     getChildContainer: function( )
30853     {
30854         if (this.boxesEl) {
30855             return this.boxesEl;
30856         }
30857         
30858         this.boxesEl = this.el.select('.mas-boxes').first();
30859         
30860         return this.boxesEl;
30861     },
30862     
30863     
30864     initEvents : function()
30865     {
30866         var _this = this;
30867         
30868         if(this.isAutoInitial){
30869             Roo.log('hook children rendered');
30870             this.on('childrenrendered', function() {
30871                 Roo.log('children rendered');
30872                 _this.initial();
30873             } ,this);
30874         }
30875     },
30876     
30877     initial : function()
30878     {
30879         this.selectedBrick = [];
30880         
30881         this.currentSize = this.el.getBox(true);
30882         
30883         Roo.EventManager.onWindowResize(this.resize, this); 
30884
30885         if(!this.isAutoInitial){
30886             this.layout();
30887             return;
30888         }
30889         
30890         this.layout();
30891         
30892         return;
30893         //this.layout.defer(500,this);
30894         
30895     },
30896     
30897     resize : function()
30898     {
30899         var cs = this.el.getBox(true);
30900         
30901         if (
30902                 this.currentSize.width == cs.width && 
30903                 this.currentSize.x == cs.x && 
30904                 this.currentSize.height == cs.height && 
30905                 this.currentSize.y == cs.y 
30906         ) {
30907             Roo.log("no change in with or X or Y");
30908             return;
30909         }
30910         
30911         this.currentSize = cs;
30912         
30913         this.layout();
30914         
30915     },
30916     
30917     layout : function()
30918     {   
30919         this._resetLayout();
30920         
30921         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30922         
30923         this.layoutItems( isInstant );
30924       
30925         this._isLayoutInited = true;
30926         
30927         this.fireEvent('layout', this);
30928         
30929     },
30930     
30931     _resetLayout : function()
30932     {
30933         if(this.isHorizontal){
30934             this.horizontalMeasureColumns();
30935             return;
30936         }
30937         
30938         this.verticalMeasureColumns();
30939         
30940     },
30941     
30942     verticalMeasureColumns : function()
30943     {
30944         this.getContainerWidth();
30945         
30946 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30947 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30948 //            return;
30949 //        }
30950         
30951         var boxWidth = this.boxWidth + this.padWidth;
30952         
30953         if(this.containerWidth < this.boxWidth){
30954             boxWidth = this.containerWidth
30955         }
30956         
30957         var containerWidth = this.containerWidth;
30958         
30959         var cols = Math.floor(containerWidth / boxWidth);
30960         
30961         this.cols = Math.max( cols, 1 );
30962         
30963         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30964         
30965         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30966         
30967         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30968         
30969         this.colWidth = boxWidth + avail - this.padWidth;
30970         
30971         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30972         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30973     },
30974     
30975     horizontalMeasureColumns : function()
30976     {
30977         this.getContainerWidth();
30978         
30979         var boxWidth = this.boxWidth;
30980         
30981         if(this.containerWidth < boxWidth){
30982             boxWidth = this.containerWidth;
30983         }
30984         
30985         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30986         
30987         this.el.setHeight(boxWidth);
30988         
30989     },
30990     
30991     getContainerWidth : function()
30992     {
30993         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30994     },
30995     
30996     layoutItems : function( isInstant )
30997     {
30998         Roo.log(this.bricks);
30999         
31000         var items = Roo.apply([], this.bricks);
31001         
31002         if(this.isHorizontal){
31003             this._horizontalLayoutItems( items , isInstant );
31004             return;
31005         }
31006         
31007 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31008 //            this._verticalAlternativeLayoutItems( items , isInstant );
31009 //            return;
31010 //        }
31011         
31012         this._verticalLayoutItems( items , isInstant );
31013         
31014     },
31015     
31016     _verticalLayoutItems : function ( items , isInstant)
31017     {
31018         if ( !items || !items.length ) {
31019             return;
31020         }
31021         
31022         var standard = [
31023             ['xs', 'xs', 'xs', 'tall'],
31024             ['xs', 'xs', 'tall'],
31025             ['xs', 'xs', 'sm'],
31026             ['xs', 'xs', 'xs'],
31027             ['xs', 'tall'],
31028             ['xs', 'sm'],
31029             ['xs', 'xs'],
31030             ['xs'],
31031             
31032             ['sm', 'xs', 'xs'],
31033             ['sm', 'xs'],
31034             ['sm'],
31035             
31036             ['tall', 'xs', 'xs', 'xs'],
31037             ['tall', 'xs', 'xs'],
31038             ['tall', 'xs'],
31039             ['tall']
31040             
31041         ];
31042         
31043         var queue = [];
31044         
31045         var boxes = [];
31046         
31047         var box = [];
31048         
31049         Roo.each(items, function(item, k){
31050             
31051             switch (item.size) {
31052                 // these layouts take up a full box,
31053                 case 'md' :
31054                 case 'md-left' :
31055                 case 'md-right' :
31056                 case 'wide' :
31057                     
31058                     if(box.length){
31059                         boxes.push(box);
31060                         box = [];
31061                     }
31062                     
31063                     boxes.push([item]);
31064                     
31065                     break;
31066                     
31067                 case 'xs' :
31068                 case 'sm' :
31069                 case 'tall' :
31070                     
31071                     box.push(item);
31072                     
31073                     break;
31074                 default :
31075                     break;
31076                     
31077             }
31078             
31079         }, this);
31080         
31081         if(box.length){
31082             boxes.push(box);
31083             box = [];
31084         }
31085         
31086         var filterPattern = function(box, length)
31087         {
31088             if(!box.length){
31089                 return;
31090             }
31091             
31092             var match = false;
31093             
31094             var pattern = box.slice(0, length);
31095             
31096             var format = [];
31097             
31098             Roo.each(pattern, function(i){
31099                 format.push(i.size);
31100             }, this);
31101             
31102             Roo.each(standard, function(s){
31103                 
31104                 if(String(s) != String(format)){
31105                     return;
31106                 }
31107                 
31108                 match = true;
31109                 return false;
31110                 
31111             }, this);
31112             
31113             if(!match && length == 1){
31114                 return;
31115             }
31116             
31117             if(!match){
31118                 filterPattern(box, length - 1);
31119                 return;
31120             }
31121                 
31122             queue.push(pattern);
31123
31124             box = box.slice(length, box.length);
31125
31126             filterPattern(box, 4);
31127
31128             return;
31129             
31130         }
31131         
31132         Roo.each(boxes, function(box, k){
31133             
31134             if(!box.length){
31135                 return;
31136             }
31137             
31138             if(box.length == 1){
31139                 queue.push(box);
31140                 return;
31141             }
31142             
31143             filterPattern(box, 4);
31144             
31145         }, this);
31146         
31147         this._processVerticalLayoutQueue( queue, isInstant );
31148         
31149     },
31150     
31151 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31152 //    {
31153 //        if ( !items || !items.length ) {
31154 //            return;
31155 //        }
31156 //
31157 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31158 //        
31159 //    },
31160     
31161     _horizontalLayoutItems : function ( items , isInstant)
31162     {
31163         if ( !items || !items.length || items.length < 3) {
31164             return;
31165         }
31166         
31167         items.reverse();
31168         
31169         var eItems = items.slice(0, 3);
31170         
31171         items = items.slice(3, items.length);
31172         
31173         var standard = [
31174             ['xs', 'xs', 'xs', 'wide'],
31175             ['xs', 'xs', 'wide'],
31176             ['xs', 'xs', 'sm'],
31177             ['xs', 'xs', 'xs'],
31178             ['xs', 'wide'],
31179             ['xs', 'sm'],
31180             ['xs', 'xs'],
31181             ['xs'],
31182             
31183             ['sm', 'xs', 'xs'],
31184             ['sm', 'xs'],
31185             ['sm'],
31186             
31187             ['wide', 'xs', 'xs', 'xs'],
31188             ['wide', 'xs', 'xs'],
31189             ['wide', 'xs'],
31190             ['wide'],
31191             
31192             ['wide-thin']
31193         ];
31194         
31195         var queue = [];
31196         
31197         var boxes = [];
31198         
31199         var box = [];
31200         
31201         Roo.each(items, function(item, k){
31202             
31203             switch (item.size) {
31204                 case 'md' :
31205                 case 'md-left' :
31206                 case 'md-right' :
31207                 case 'tall' :
31208                     
31209                     if(box.length){
31210                         boxes.push(box);
31211                         box = [];
31212                     }
31213                     
31214                     boxes.push([item]);
31215                     
31216                     break;
31217                     
31218                 case 'xs' :
31219                 case 'sm' :
31220                 case 'wide' :
31221                 case 'wide-thin' :
31222                     
31223                     box.push(item);
31224                     
31225                     break;
31226                 default :
31227                     break;
31228                     
31229             }
31230             
31231         }, this);
31232         
31233         if(box.length){
31234             boxes.push(box);
31235             box = [];
31236         }
31237         
31238         var filterPattern = function(box, length)
31239         {
31240             if(!box.length){
31241                 return;
31242             }
31243             
31244             var match = false;
31245             
31246             var pattern = box.slice(0, length);
31247             
31248             var format = [];
31249             
31250             Roo.each(pattern, function(i){
31251                 format.push(i.size);
31252             }, this);
31253             
31254             Roo.each(standard, function(s){
31255                 
31256                 if(String(s) != String(format)){
31257                     return;
31258                 }
31259                 
31260                 match = true;
31261                 return false;
31262                 
31263             }, this);
31264             
31265             if(!match && length == 1){
31266                 return;
31267             }
31268             
31269             if(!match){
31270                 filterPattern(box, length - 1);
31271                 return;
31272             }
31273                 
31274             queue.push(pattern);
31275
31276             box = box.slice(length, box.length);
31277
31278             filterPattern(box, 4);
31279
31280             return;
31281             
31282         }
31283         
31284         Roo.each(boxes, function(box, k){
31285             
31286             if(!box.length){
31287                 return;
31288             }
31289             
31290             if(box.length == 1){
31291                 queue.push(box);
31292                 return;
31293             }
31294             
31295             filterPattern(box, 4);
31296             
31297         }, this);
31298         
31299         
31300         var prune = [];
31301         
31302         var pos = this.el.getBox(true);
31303         
31304         var minX = pos.x;
31305         
31306         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31307         
31308         var hit_end = false;
31309         
31310         Roo.each(queue, function(box){
31311             
31312             if(hit_end){
31313                 
31314                 Roo.each(box, function(b){
31315                 
31316                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31317                     b.el.hide();
31318
31319                 }, this);
31320
31321                 return;
31322             }
31323             
31324             var mx = 0;
31325             
31326             Roo.each(box, function(b){
31327                 
31328                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31329                 b.el.show();
31330
31331                 mx = Math.max(mx, b.x);
31332                 
31333             }, this);
31334             
31335             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31336             
31337             if(maxX < minX){
31338                 
31339                 Roo.each(box, function(b){
31340                 
31341                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31342                     b.el.hide();
31343                     
31344                 }, this);
31345                 
31346                 hit_end = true;
31347                 
31348                 return;
31349             }
31350             
31351             prune.push(box);
31352             
31353         }, this);
31354         
31355         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31356     },
31357     
31358     /** Sets position of item in DOM
31359     * @param {Element} item
31360     * @param {Number} x - horizontal position
31361     * @param {Number} y - vertical position
31362     * @param {Boolean} isInstant - disables transitions
31363     */
31364     _processVerticalLayoutQueue : function( queue, isInstant )
31365     {
31366         var pos = this.el.getBox(true);
31367         var x = pos.x;
31368         var y = pos.y;
31369         var maxY = [];
31370         
31371         for (var i = 0; i < this.cols; i++){
31372             maxY[i] = pos.y;
31373         }
31374         
31375         Roo.each(queue, function(box, k){
31376             
31377             var col = k % this.cols;
31378             
31379             Roo.each(box, function(b,kk){
31380                 
31381                 b.el.position('absolute');
31382                 
31383                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31384                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31385                 
31386                 if(b.size == 'md-left' || b.size == 'md-right'){
31387                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31388                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31389                 }
31390                 
31391                 b.el.setWidth(width);
31392                 b.el.setHeight(height);
31393                 // iframe?
31394                 b.el.select('iframe',true).setSize(width,height);
31395                 
31396             }, this);
31397             
31398             for (var i = 0; i < this.cols; i++){
31399                 
31400                 if(maxY[i] < maxY[col]){
31401                     col = i;
31402                     continue;
31403                 }
31404                 
31405                 col = Math.min(col, i);
31406                 
31407             }
31408             
31409             x = pos.x + col * (this.colWidth + this.padWidth);
31410             
31411             y = maxY[col];
31412             
31413             var positions = [];
31414             
31415             switch (box.length){
31416                 case 1 :
31417                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31418                     break;
31419                 case 2 :
31420                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31421                     break;
31422                 case 3 :
31423                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31424                     break;
31425                 case 4 :
31426                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31427                     break;
31428                 default :
31429                     break;
31430             }
31431             
31432             Roo.each(box, function(b,kk){
31433                 
31434                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31435                 
31436                 var sz = b.el.getSize();
31437                 
31438                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31439                 
31440             }, this);
31441             
31442         }, this);
31443         
31444         var mY = 0;
31445         
31446         for (var i = 0; i < this.cols; i++){
31447             mY = Math.max(mY, maxY[i]);
31448         }
31449         
31450         this.el.setHeight(mY - pos.y);
31451         
31452     },
31453     
31454 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31455 //    {
31456 //        var pos = this.el.getBox(true);
31457 //        var x = pos.x;
31458 //        var y = pos.y;
31459 //        var maxX = pos.right;
31460 //        
31461 //        var maxHeight = 0;
31462 //        
31463 //        Roo.each(items, function(item, k){
31464 //            
31465 //            var c = k % 2;
31466 //            
31467 //            item.el.position('absolute');
31468 //                
31469 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31470 //
31471 //            item.el.setWidth(width);
31472 //
31473 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31474 //
31475 //            item.el.setHeight(height);
31476 //            
31477 //            if(c == 0){
31478 //                item.el.setXY([x, y], isInstant ? false : true);
31479 //            } else {
31480 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31481 //            }
31482 //            
31483 //            y = y + height + this.alternativePadWidth;
31484 //            
31485 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31486 //            
31487 //        }, this);
31488 //        
31489 //        this.el.setHeight(maxHeight);
31490 //        
31491 //    },
31492     
31493     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31494     {
31495         var pos = this.el.getBox(true);
31496         
31497         var minX = pos.x;
31498         var minY = pos.y;
31499         
31500         var maxX = pos.right;
31501         
31502         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31503         
31504         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31505         
31506         Roo.each(queue, function(box, k){
31507             
31508             Roo.each(box, function(b, kk){
31509                 
31510                 b.el.position('absolute');
31511                 
31512                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31513                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31514                 
31515                 if(b.size == 'md-left' || b.size == 'md-right'){
31516                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31517                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31518                 }
31519                 
31520                 b.el.setWidth(width);
31521                 b.el.setHeight(height);
31522                 
31523             }, this);
31524             
31525             if(!box.length){
31526                 return;
31527             }
31528             
31529             var positions = [];
31530             
31531             switch (box.length){
31532                 case 1 :
31533                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31534                     break;
31535                 case 2 :
31536                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31537                     break;
31538                 case 3 :
31539                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31540                     break;
31541                 case 4 :
31542                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31543                     break;
31544                 default :
31545                     break;
31546             }
31547             
31548             Roo.each(box, function(b,kk){
31549                 
31550                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31551                 
31552                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31553                 
31554             }, this);
31555             
31556         }, this);
31557         
31558     },
31559     
31560     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31561     {
31562         Roo.each(eItems, function(b,k){
31563             
31564             b.size = (k == 0) ? 'sm' : 'xs';
31565             b.x = (k == 0) ? 2 : 1;
31566             b.y = (k == 0) ? 2 : 1;
31567             
31568             b.el.position('absolute');
31569             
31570             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31571                 
31572             b.el.setWidth(width);
31573             
31574             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31575             
31576             b.el.setHeight(height);
31577             
31578         }, this);
31579
31580         var positions = [];
31581         
31582         positions.push({
31583             x : maxX - this.unitWidth * 2 - this.gutter,
31584             y : minY
31585         });
31586         
31587         positions.push({
31588             x : maxX - this.unitWidth,
31589             y : minY + (this.unitWidth + this.gutter) * 2
31590         });
31591         
31592         positions.push({
31593             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31594             y : minY
31595         });
31596         
31597         Roo.each(eItems, function(b,k){
31598             
31599             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31600
31601         }, this);
31602         
31603     },
31604     
31605     getVerticalOneBoxColPositions : function(x, y, box)
31606     {
31607         var pos = [];
31608         
31609         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31610         
31611         if(box[0].size == 'md-left'){
31612             rand = 0;
31613         }
31614         
31615         if(box[0].size == 'md-right'){
31616             rand = 1;
31617         }
31618         
31619         pos.push({
31620             x : x + (this.unitWidth + this.gutter) * rand,
31621             y : y
31622         });
31623         
31624         return pos;
31625     },
31626     
31627     getVerticalTwoBoxColPositions : function(x, y, box)
31628     {
31629         var pos = [];
31630         
31631         if(box[0].size == 'xs'){
31632             
31633             pos.push({
31634                 x : x,
31635                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31636             });
31637
31638             pos.push({
31639                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31640                 y : y
31641             });
31642             
31643             return pos;
31644             
31645         }
31646         
31647         pos.push({
31648             x : x,
31649             y : y
31650         });
31651
31652         pos.push({
31653             x : x + (this.unitWidth + this.gutter) * 2,
31654             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31655         });
31656         
31657         return pos;
31658         
31659     },
31660     
31661     getVerticalThreeBoxColPositions : function(x, y, box)
31662     {
31663         var pos = [];
31664         
31665         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31666             
31667             pos.push({
31668                 x : x,
31669                 y : y
31670             });
31671
31672             pos.push({
31673                 x : x + (this.unitWidth + this.gutter) * 1,
31674                 y : y
31675             });
31676             
31677             pos.push({
31678                 x : x + (this.unitWidth + this.gutter) * 2,
31679                 y : y
31680             });
31681             
31682             return pos;
31683             
31684         }
31685         
31686         if(box[0].size == 'xs' && box[1].size == 'xs'){
31687             
31688             pos.push({
31689                 x : x,
31690                 y : y
31691             });
31692
31693             pos.push({
31694                 x : x,
31695                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31696             });
31697             
31698             pos.push({
31699                 x : x + (this.unitWidth + this.gutter) * 1,
31700                 y : y
31701             });
31702             
31703             return pos;
31704             
31705         }
31706         
31707         pos.push({
31708             x : x,
31709             y : y
31710         });
31711
31712         pos.push({
31713             x : x + (this.unitWidth + this.gutter) * 2,
31714             y : y
31715         });
31716
31717         pos.push({
31718             x : x + (this.unitWidth + this.gutter) * 2,
31719             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31720         });
31721             
31722         return pos;
31723         
31724     },
31725     
31726     getVerticalFourBoxColPositions : function(x, y, box)
31727     {
31728         var pos = [];
31729         
31730         if(box[0].size == 'xs'){
31731             
31732             pos.push({
31733                 x : x,
31734                 y : y
31735             });
31736
31737             pos.push({
31738                 x : x,
31739                 y : y + (this.unitHeight + this.gutter) * 1
31740             });
31741             
31742             pos.push({
31743                 x : x,
31744                 y : y + (this.unitHeight + this.gutter) * 2
31745             });
31746             
31747             pos.push({
31748                 x : x + (this.unitWidth + this.gutter) * 1,
31749                 y : y
31750             });
31751             
31752             return pos;
31753             
31754         }
31755         
31756         pos.push({
31757             x : x,
31758             y : y
31759         });
31760
31761         pos.push({
31762             x : x + (this.unitWidth + this.gutter) * 2,
31763             y : y
31764         });
31765
31766         pos.push({
31767             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31768             y : y + (this.unitHeight + this.gutter) * 1
31769         });
31770
31771         pos.push({
31772             x : x + (this.unitWidth + this.gutter) * 2,
31773             y : y + (this.unitWidth + this.gutter) * 2
31774         });
31775
31776         return pos;
31777         
31778     },
31779     
31780     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31781     {
31782         var pos = [];
31783         
31784         if(box[0].size == 'md-left'){
31785             pos.push({
31786                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31787                 y : minY
31788             });
31789             
31790             return pos;
31791         }
31792         
31793         if(box[0].size == 'md-right'){
31794             pos.push({
31795                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31796                 y : minY + (this.unitWidth + this.gutter) * 1
31797             });
31798             
31799             return pos;
31800         }
31801         
31802         var rand = Math.floor(Math.random() * (4 - box[0].y));
31803         
31804         pos.push({
31805             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31806             y : minY + (this.unitWidth + this.gutter) * rand
31807         });
31808         
31809         return pos;
31810         
31811     },
31812     
31813     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31814     {
31815         var pos = [];
31816         
31817         if(box[0].size == 'xs'){
31818             
31819             pos.push({
31820                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31821                 y : minY
31822             });
31823
31824             pos.push({
31825                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31826                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31827             });
31828             
31829             return pos;
31830             
31831         }
31832         
31833         pos.push({
31834             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31835             y : minY
31836         });
31837
31838         pos.push({
31839             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31840             y : minY + (this.unitWidth + this.gutter) * 2
31841         });
31842         
31843         return pos;
31844         
31845     },
31846     
31847     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31848     {
31849         var pos = [];
31850         
31851         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31852             
31853             pos.push({
31854                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31855                 y : minY
31856             });
31857
31858             pos.push({
31859                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31860                 y : minY + (this.unitWidth + this.gutter) * 1
31861             });
31862             
31863             pos.push({
31864                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31865                 y : minY + (this.unitWidth + this.gutter) * 2
31866             });
31867             
31868             return pos;
31869             
31870         }
31871         
31872         if(box[0].size == 'xs' && box[1].size == 'xs'){
31873             
31874             pos.push({
31875                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31876                 y : minY
31877             });
31878
31879             pos.push({
31880                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31881                 y : minY
31882             });
31883             
31884             pos.push({
31885                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31886                 y : minY + (this.unitWidth + this.gutter) * 1
31887             });
31888             
31889             return pos;
31890             
31891         }
31892         
31893         pos.push({
31894             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31895             y : minY
31896         });
31897
31898         pos.push({
31899             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31900             y : minY + (this.unitWidth + this.gutter) * 2
31901         });
31902
31903         pos.push({
31904             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31905             y : minY + (this.unitWidth + this.gutter) * 2
31906         });
31907             
31908         return pos;
31909         
31910     },
31911     
31912     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31913     {
31914         var pos = [];
31915         
31916         if(box[0].size == 'xs'){
31917             
31918             pos.push({
31919                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31920                 y : minY
31921             });
31922
31923             pos.push({
31924                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31925                 y : minY
31926             });
31927             
31928             pos.push({
31929                 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),
31930                 y : minY
31931             });
31932             
31933             pos.push({
31934                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31935                 y : minY + (this.unitWidth + this.gutter) * 1
31936             });
31937             
31938             return pos;
31939             
31940         }
31941         
31942         pos.push({
31943             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31944             y : minY
31945         });
31946         
31947         pos.push({
31948             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31949             y : minY + (this.unitWidth + this.gutter) * 2
31950         });
31951         
31952         pos.push({
31953             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31954             y : minY + (this.unitWidth + this.gutter) * 2
31955         });
31956         
31957         pos.push({
31958             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),
31959             y : minY + (this.unitWidth + this.gutter) * 2
31960         });
31961
31962         return pos;
31963         
31964     },
31965     
31966     /**
31967     * remove a Masonry Brick
31968     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31969     */
31970     removeBrick : function(brick_id)
31971     {
31972         if (!brick_id) {
31973             return;
31974         }
31975         
31976         for (var i = 0; i<this.bricks.length; i++) {
31977             if (this.bricks[i].id == brick_id) {
31978                 this.bricks.splice(i,1);
31979                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31980                 this.initial();
31981             }
31982         }
31983     },
31984     
31985     /**
31986     * adds a Masonry Brick
31987     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31988     */
31989     addBrick : function(cfg)
31990     {
31991         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31992         //this.register(cn);
31993         cn.parentId = this.id;
31994         cn.onRender(this.el, null);
31995         return cn;
31996     },
31997     
31998     /**
31999     * register a Masonry Brick
32000     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32001     */
32002     
32003     register : function(brick)
32004     {
32005         this.bricks.push(brick);
32006         brick.masonryId = this.id;
32007     },
32008     
32009     /**
32010     * clear all the Masonry Brick
32011     */
32012     clearAll : function()
32013     {
32014         this.bricks = [];
32015         //this.getChildContainer().dom.innerHTML = "";
32016         this.el.dom.innerHTML = '';
32017     },
32018     
32019     getSelected : function()
32020     {
32021         if (!this.selectedBrick) {
32022             return false;
32023         }
32024         
32025         return this.selectedBrick;
32026     }
32027 });
32028
32029 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32030     
32031     groups: {},
32032      /**
32033     * register a Masonry Layout
32034     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32035     */
32036     
32037     register : function(layout)
32038     {
32039         this.groups[layout.id] = layout;
32040     },
32041     /**
32042     * fetch a  Masonry Layout based on the masonry layout ID
32043     * @param {string} the masonry layout to add
32044     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32045     */
32046     
32047     get: function(layout_id) {
32048         if (typeof(this.groups[layout_id]) == 'undefined') {
32049             return false;
32050         }
32051         return this.groups[layout_id] ;
32052     }
32053     
32054     
32055     
32056 });
32057
32058  
32059
32060  /**
32061  *
32062  * This is based on 
32063  * http://masonry.desandro.com
32064  *
32065  * The idea is to render all the bricks based on vertical width...
32066  *
32067  * The original code extends 'outlayer' - we might need to use that....
32068  * 
32069  */
32070
32071
32072 /**
32073  * @class Roo.bootstrap.LayoutMasonryAuto
32074  * @extends Roo.bootstrap.Component
32075  * Bootstrap Layout Masonry class
32076  * 
32077  * @constructor
32078  * Create a new Element
32079  * @param {Object} config The config object
32080  */
32081
32082 Roo.bootstrap.LayoutMasonryAuto = function(config){
32083     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32084 };
32085
32086 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32087     
32088       /**
32089      * @cfg {Boolean} isFitWidth  - resize the width..
32090      */   
32091     isFitWidth : false,  // options..
32092     /**
32093      * @cfg {Boolean} isOriginLeft = left align?
32094      */   
32095     isOriginLeft : true,
32096     /**
32097      * @cfg {Boolean} isOriginTop = top align?
32098      */   
32099     isOriginTop : false,
32100     /**
32101      * @cfg {Boolean} isLayoutInstant = no animation?
32102      */   
32103     isLayoutInstant : false, // needed?
32104     /**
32105      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32106      */   
32107     isResizingContainer : true,
32108     /**
32109      * @cfg {Number} columnWidth  width of the columns 
32110      */   
32111     
32112     columnWidth : 0,
32113     
32114     /**
32115      * @cfg {Number} maxCols maximum number of columns
32116      */   
32117     
32118     maxCols: 0,
32119     /**
32120      * @cfg {Number} padHeight padding below box..
32121      */   
32122     
32123     padHeight : 10, 
32124     
32125     /**
32126      * @cfg {Boolean} isAutoInitial defalut true
32127      */   
32128     
32129     isAutoInitial : true, 
32130     
32131     // private?
32132     gutter : 0,
32133     
32134     containerWidth: 0,
32135     initialColumnWidth : 0,
32136     currentSize : null,
32137     
32138     colYs : null, // array.
32139     maxY : 0,
32140     padWidth: 10,
32141     
32142     
32143     tag: 'div',
32144     cls: '',
32145     bricks: null, //CompositeElement
32146     cols : 0, // array?
32147     // element : null, // wrapped now this.el
32148     _isLayoutInited : null, 
32149     
32150     
32151     getAutoCreate : function(){
32152         
32153         var cfg = {
32154             tag: this.tag,
32155             cls: 'blog-masonary-wrapper ' + this.cls,
32156             cn : {
32157                 cls : 'mas-boxes masonary'
32158             }
32159         };
32160         
32161         return cfg;
32162     },
32163     
32164     getChildContainer: function( )
32165     {
32166         if (this.boxesEl) {
32167             return this.boxesEl;
32168         }
32169         
32170         this.boxesEl = this.el.select('.mas-boxes').first();
32171         
32172         return this.boxesEl;
32173     },
32174     
32175     
32176     initEvents : function()
32177     {
32178         var _this = this;
32179         
32180         if(this.isAutoInitial){
32181             Roo.log('hook children rendered');
32182             this.on('childrenrendered', function() {
32183                 Roo.log('children rendered');
32184                 _this.initial();
32185             } ,this);
32186         }
32187         
32188     },
32189     
32190     initial : function()
32191     {
32192         this.reloadItems();
32193
32194         this.currentSize = this.el.getBox(true);
32195
32196         /// was window resize... - let's see if this works..
32197         Roo.EventManager.onWindowResize(this.resize, this); 
32198
32199         if(!this.isAutoInitial){
32200             this.layout();
32201             return;
32202         }
32203         
32204         this.layout.defer(500,this);
32205     },
32206     
32207     reloadItems: function()
32208     {
32209         this.bricks = this.el.select('.masonry-brick', true);
32210         
32211         this.bricks.each(function(b) {
32212             //Roo.log(b.getSize());
32213             if (!b.attr('originalwidth')) {
32214                 b.attr('originalwidth',  b.getSize().width);
32215             }
32216             
32217         });
32218         
32219         Roo.log(this.bricks.elements.length);
32220     },
32221     
32222     resize : function()
32223     {
32224         Roo.log('resize');
32225         var cs = this.el.getBox(true);
32226         
32227         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32228             Roo.log("no change in with or X");
32229             return;
32230         }
32231         this.currentSize = cs;
32232         this.layout();
32233     },
32234     
32235     layout : function()
32236     {
32237          Roo.log('layout');
32238         this._resetLayout();
32239         //this._manageStamps();
32240       
32241         // don't animate first layout
32242         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32243         this.layoutItems( isInstant );
32244       
32245         // flag for initalized
32246         this._isLayoutInited = true;
32247     },
32248     
32249     layoutItems : function( isInstant )
32250     {
32251         //var items = this._getItemsForLayout( this.items );
32252         // original code supports filtering layout items.. we just ignore it..
32253         
32254         this._layoutItems( this.bricks , isInstant );
32255       
32256         this._postLayout();
32257     },
32258     _layoutItems : function ( items , isInstant)
32259     {
32260        //this.fireEvent( 'layout', this, items );
32261     
32262
32263         if ( !items || !items.elements.length ) {
32264           // no items, emit event with empty array
32265             return;
32266         }
32267
32268         var queue = [];
32269         items.each(function(item) {
32270             Roo.log("layout item");
32271             Roo.log(item);
32272             // get x/y object from method
32273             var position = this._getItemLayoutPosition( item );
32274             // enqueue
32275             position.item = item;
32276             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32277             queue.push( position );
32278         }, this);
32279       
32280         this._processLayoutQueue( queue );
32281     },
32282     /** Sets position of item in DOM
32283     * @param {Element} item
32284     * @param {Number} x - horizontal position
32285     * @param {Number} y - vertical position
32286     * @param {Boolean} isInstant - disables transitions
32287     */
32288     _processLayoutQueue : function( queue )
32289     {
32290         for ( var i=0, len = queue.length; i < len; i++ ) {
32291             var obj = queue[i];
32292             obj.item.position('absolute');
32293             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32294         }
32295     },
32296       
32297     
32298     /**
32299     * Any logic you want to do after each layout,
32300     * i.e. size the container
32301     */
32302     _postLayout : function()
32303     {
32304         this.resizeContainer();
32305     },
32306     
32307     resizeContainer : function()
32308     {
32309         if ( !this.isResizingContainer ) {
32310             return;
32311         }
32312         var size = this._getContainerSize();
32313         if ( size ) {
32314             this.el.setSize(size.width,size.height);
32315             this.boxesEl.setSize(size.width,size.height);
32316         }
32317     },
32318     
32319     
32320     
32321     _resetLayout : function()
32322     {
32323         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32324         this.colWidth = this.el.getWidth();
32325         //this.gutter = this.el.getWidth(); 
32326         
32327         this.measureColumns();
32328
32329         // reset column Y
32330         var i = this.cols;
32331         this.colYs = [];
32332         while (i--) {
32333             this.colYs.push( 0 );
32334         }
32335     
32336         this.maxY = 0;
32337     },
32338
32339     measureColumns : function()
32340     {
32341         this.getContainerWidth();
32342       // if columnWidth is 0, default to outerWidth of first item
32343         if ( !this.columnWidth ) {
32344             var firstItem = this.bricks.first();
32345             Roo.log(firstItem);
32346             this.columnWidth  = this.containerWidth;
32347             if (firstItem && firstItem.attr('originalwidth') ) {
32348                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32349             }
32350             // columnWidth fall back to item of first element
32351             Roo.log("set column width?");
32352                         this.initialColumnWidth = this.columnWidth  ;
32353
32354             // if first elem has no width, default to size of container
32355             
32356         }
32357         
32358         
32359         if (this.initialColumnWidth) {
32360             this.columnWidth = this.initialColumnWidth;
32361         }
32362         
32363         
32364             
32365         // column width is fixed at the top - however if container width get's smaller we should
32366         // reduce it...
32367         
32368         // this bit calcs how man columns..
32369             
32370         var columnWidth = this.columnWidth += this.gutter;
32371       
32372         // calculate columns
32373         var containerWidth = this.containerWidth + this.gutter;
32374         
32375         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32376         // fix rounding errors, typically with gutters
32377         var excess = columnWidth - containerWidth % columnWidth;
32378         
32379         
32380         // if overshoot is less than a pixel, round up, otherwise floor it
32381         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32382         cols = Math[ mathMethod ]( cols );
32383         this.cols = Math.max( cols, 1 );
32384         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32385         
32386          // padding positioning..
32387         var totalColWidth = this.cols * this.columnWidth;
32388         var padavail = this.containerWidth - totalColWidth;
32389         // so for 2 columns - we need 3 'pads'
32390         
32391         var padNeeded = (1+this.cols) * this.padWidth;
32392         
32393         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32394         
32395         this.columnWidth += padExtra
32396         //this.padWidth = Math.floor(padavail /  ( this.cols));
32397         
32398         // adjust colum width so that padding is fixed??
32399         
32400         // we have 3 columns ... total = width * 3
32401         // we have X left over... that should be used by 
32402         
32403         //if (this.expandC) {
32404             
32405         //}
32406         
32407         
32408         
32409     },
32410     
32411     getContainerWidth : function()
32412     {
32413        /* // container is parent if fit width
32414         var container = this.isFitWidth ? this.element.parentNode : this.element;
32415         // check that this.size and size are there
32416         // IE8 triggers resize on body size change, so they might not be
32417         
32418         var size = getSize( container );  //FIXME
32419         this.containerWidth = size && size.innerWidth; //FIXME
32420         */
32421          
32422         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32423         
32424     },
32425     
32426     _getItemLayoutPosition : function( item )  // what is item?
32427     {
32428         // we resize the item to our columnWidth..
32429       
32430         item.setWidth(this.columnWidth);
32431         item.autoBoxAdjust  = false;
32432         
32433         var sz = item.getSize();
32434  
32435         // how many columns does this brick span
32436         var remainder = this.containerWidth % this.columnWidth;
32437         
32438         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32439         // round if off by 1 pixel, otherwise use ceil
32440         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32441         colSpan = Math.min( colSpan, this.cols );
32442         
32443         // normally this should be '1' as we dont' currently allow multi width columns..
32444         
32445         var colGroup = this._getColGroup( colSpan );
32446         // get the minimum Y value from the columns
32447         var minimumY = Math.min.apply( Math, colGroup );
32448         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32449         
32450         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32451          
32452         // position the brick
32453         var position = {
32454             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32455             y: this.currentSize.y + minimumY + this.padHeight
32456         };
32457         
32458         Roo.log(position);
32459         // apply setHeight to necessary columns
32460         var setHeight = minimumY + sz.height + this.padHeight;
32461         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32462         
32463         var setSpan = this.cols + 1 - colGroup.length;
32464         for ( var i = 0; i < setSpan; i++ ) {
32465           this.colYs[ shortColIndex + i ] = setHeight ;
32466         }
32467       
32468         return position;
32469     },
32470     
32471     /**
32472      * @param {Number} colSpan - number of columns the element spans
32473      * @returns {Array} colGroup
32474      */
32475     _getColGroup : function( colSpan )
32476     {
32477         if ( colSpan < 2 ) {
32478           // if brick spans only one column, use all the column Ys
32479           return this.colYs;
32480         }
32481       
32482         var colGroup = [];
32483         // how many different places could this brick fit horizontally
32484         var groupCount = this.cols + 1 - colSpan;
32485         // for each group potential horizontal position
32486         for ( var i = 0; i < groupCount; i++ ) {
32487           // make an array of colY values for that one group
32488           var groupColYs = this.colYs.slice( i, i + colSpan );
32489           // and get the max value of the array
32490           colGroup[i] = Math.max.apply( Math, groupColYs );
32491         }
32492         return colGroup;
32493     },
32494     /*
32495     _manageStamp : function( stamp )
32496     {
32497         var stampSize =  stamp.getSize();
32498         var offset = stamp.getBox();
32499         // get the columns that this stamp affects
32500         var firstX = this.isOriginLeft ? offset.x : offset.right;
32501         var lastX = firstX + stampSize.width;
32502         var firstCol = Math.floor( firstX / this.columnWidth );
32503         firstCol = Math.max( 0, firstCol );
32504         
32505         var lastCol = Math.floor( lastX / this.columnWidth );
32506         // lastCol should not go over if multiple of columnWidth #425
32507         lastCol -= lastX % this.columnWidth ? 0 : 1;
32508         lastCol = Math.min( this.cols - 1, lastCol );
32509         
32510         // set colYs to bottom of the stamp
32511         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32512             stampSize.height;
32513             
32514         for ( var i = firstCol; i <= lastCol; i++ ) {
32515           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32516         }
32517     },
32518     */
32519     
32520     _getContainerSize : function()
32521     {
32522         this.maxY = Math.max.apply( Math, this.colYs );
32523         var size = {
32524             height: this.maxY
32525         };
32526       
32527         if ( this.isFitWidth ) {
32528             size.width = this._getContainerFitWidth();
32529         }
32530       
32531         return size;
32532     },
32533     
32534     _getContainerFitWidth : function()
32535     {
32536         var unusedCols = 0;
32537         // count unused columns
32538         var i = this.cols;
32539         while ( --i ) {
32540           if ( this.colYs[i] !== 0 ) {
32541             break;
32542           }
32543           unusedCols++;
32544         }
32545         // fit container to columns that have been used
32546         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32547     },
32548     
32549     needsResizeLayout : function()
32550     {
32551         var previousWidth = this.containerWidth;
32552         this.getContainerWidth();
32553         return previousWidth !== this.containerWidth;
32554     }
32555  
32556 });
32557
32558  
32559
32560  /*
32561  * - LGPL
32562  *
32563  * element
32564  * 
32565  */
32566
32567 /**
32568  * @class Roo.bootstrap.MasonryBrick
32569  * @extends Roo.bootstrap.Component
32570  * Bootstrap MasonryBrick class
32571  * 
32572  * @constructor
32573  * Create a new MasonryBrick
32574  * @param {Object} config The config object
32575  */
32576
32577 Roo.bootstrap.MasonryBrick = function(config){
32578     
32579     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32580     
32581     Roo.bootstrap.MasonryBrick.register(this);
32582     
32583     this.addEvents({
32584         // raw events
32585         /**
32586          * @event click
32587          * When a MasonryBrick is clcik
32588          * @param {Roo.bootstrap.MasonryBrick} this
32589          * @param {Roo.EventObject} e
32590          */
32591         "click" : true
32592     });
32593 };
32594
32595 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32596     
32597     /**
32598      * @cfg {String} title
32599      */   
32600     title : '',
32601     /**
32602      * @cfg {String} html
32603      */   
32604     html : '',
32605     /**
32606      * @cfg {String} bgimage
32607      */   
32608     bgimage : '',
32609     /**
32610      * @cfg {String} videourl
32611      */   
32612     videourl : '',
32613     /**
32614      * @cfg {String} cls
32615      */   
32616     cls : '',
32617     /**
32618      * @cfg {String} href
32619      */   
32620     href : '',
32621     /**
32622      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32623      */   
32624     size : 'xs',
32625     
32626     /**
32627      * @cfg {String} placetitle (center|bottom)
32628      */   
32629     placetitle : '',
32630     
32631     /**
32632      * @cfg {Boolean} isFitContainer defalut true
32633      */   
32634     isFitContainer : true, 
32635     
32636     /**
32637      * @cfg {Boolean} preventDefault defalut false
32638      */   
32639     preventDefault : false, 
32640     
32641     /**
32642      * @cfg {Boolean} inverse defalut false
32643      */   
32644     maskInverse : false, 
32645     
32646     getAutoCreate : function()
32647     {
32648         if(!this.isFitContainer){
32649             return this.getSplitAutoCreate();
32650         }
32651         
32652         var cls = 'masonry-brick masonry-brick-full';
32653         
32654         if(this.href.length){
32655             cls += ' masonry-brick-link';
32656         }
32657         
32658         if(this.bgimage.length){
32659             cls += ' masonry-brick-image';
32660         }
32661         
32662         if(this.maskInverse){
32663             cls += ' mask-inverse';
32664         }
32665         
32666         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32667             cls += ' enable-mask';
32668         }
32669         
32670         if(this.size){
32671             cls += ' masonry-' + this.size + '-brick';
32672         }
32673         
32674         if(this.placetitle.length){
32675             
32676             switch (this.placetitle) {
32677                 case 'center' :
32678                     cls += ' masonry-center-title';
32679                     break;
32680                 case 'bottom' :
32681                     cls += ' masonry-bottom-title';
32682                     break;
32683                 default:
32684                     break;
32685             }
32686             
32687         } else {
32688             if(!this.html.length && !this.bgimage.length){
32689                 cls += ' masonry-center-title';
32690             }
32691
32692             if(!this.html.length && this.bgimage.length){
32693                 cls += ' masonry-bottom-title';
32694             }
32695         }
32696         
32697         if(this.cls){
32698             cls += ' ' + this.cls;
32699         }
32700         
32701         var cfg = {
32702             tag: (this.href.length) ? 'a' : 'div',
32703             cls: cls,
32704             cn: [
32705                 {
32706                     tag: 'div',
32707                     cls: 'masonry-brick-mask'
32708                 },
32709                 {
32710                     tag: 'div',
32711                     cls: 'masonry-brick-paragraph',
32712                     cn: []
32713                 }
32714             ]
32715         };
32716         
32717         if(this.href.length){
32718             cfg.href = this.href;
32719         }
32720         
32721         var cn = cfg.cn[1].cn;
32722         
32723         if(this.title.length){
32724             cn.push({
32725                 tag: 'h4',
32726                 cls: 'masonry-brick-title',
32727                 html: this.title
32728             });
32729         }
32730         
32731         if(this.html.length){
32732             cn.push({
32733                 tag: 'p',
32734                 cls: 'masonry-brick-text',
32735                 html: this.html
32736             });
32737         }
32738         
32739         if (!this.title.length && !this.html.length) {
32740             cfg.cn[1].cls += ' hide';
32741         }
32742         
32743         if(this.bgimage.length){
32744             cfg.cn.push({
32745                 tag: 'img',
32746                 cls: 'masonry-brick-image-view',
32747                 src: this.bgimage
32748             });
32749         }
32750         
32751         if(this.videourl.length){
32752             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32753             // youtube support only?
32754             cfg.cn.push({
32755                 tag: 'iframe',
32756                 cls: 'masonry-brick-image-view',
32757                 src: vurl,
32758                 frameborder : 0,
32759                 allowfullscreen : true
32760             });
32761         }
32762         
32763         return cfg;
32764         
32765     },
32766     
32767     getSplitAutoCreate : function()
32768     {
32769         var cls = 'masonry-brick masonry-brick-split';
32770         
32771         if(this.href.length){
32772             cls += ' masonry-brick-link';
32773         }
32774         
32775         if(this.bgimage.length){
32776             cls += ' masonry-brick-image';
32777         }
32778         
32779         if(this.size){
32780             cls += ' masonry-' + this.size + '-brick';
32781         }
32782         
32783         switch (this.placetitle) {
32784             case 'center' :
32785                 cls += ' masonry-center-title';
32786                 break;
32787             case 'bottom' :
32788                 cls += ' masonry-bottom-title';
32789                 break;
32790             default:
32791                 if(!this.bgimage.length){
32792                     cls += ' masonry-center-title';
32793                 }
32794
32795                 if(this.bgimage.length){
32796                     cls += ' masonry-bottom-title';
32797                 }
32798                 break;
32799         }
32800         
32801         if(this.cls){
32802             cls += ' ' + this.cls;
32803         }
32804         
32805         var cfg = {
32806             tag: (this.href.length) ? 'a' : 'div',
32807             cls: cls,
32808             cn: [
32809                 {
32810                     tag: 'div',
32811                     cls: 'masonry-brick-split-head',
32812                     cn: [
32813                         {
32814                             tag: 'div',
32815                             cls: 'masonry-brick-paragraph',
32816                             cn: []
32817                         }
32818                     ]
32819                 },
32820                 {
32821                     tag: 'div',
32822                     cls: 'masonry-brick-split-body',
32823                     cn: []
32824                 }
32825             ]
32826         };
32827         
32828         if(this.href.length){
32829             cfg.href = this.href;
32830         }
32831         
32832         if(this.title.length){
32833             cfg.cn[0].cn[0].cn.push({
32834                 tag: 'h4',
32835                 cls: 'masonry-brick-title',
32836                 html: this.title
32837             });
32838         }
32839         
32840         if(this.html.length){
32841             cfg.cn[1].cn.push({
32842                 tag: 'p',
32843                 cls: 'masonry-brick-text',
32844                 html: this.html
32845             });
32846         }
32847
32848         if(this.bgimage.length){
32849             cfg.cn[0].cn.push({
32850                 tag: 'img',
32851                 cls: 'masonry-brick-image-view',
32852                 src: this.bgimage
32853             });
32854         }
32855         
32856         if(this.videourl.length){
32857             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32858             // youtube support only?
32859             cfg.cn[0].cn.cn.push({
32860                 tag: 'iframe',
32861                 cls: 'masonry-brick-image-view',
32862                 src: vurl,
32863                 frameborder : 0,
32864                 allowfullscreen : true
32865             });
32866         }
32867         
32868         return cfg;
32869     },
32870     
32871     initEvents: function() 
32872     {
32873         switch (this.size) {
32874             case 'xs' :
32875                 this.x = 1;
32876                 this.y = 1;
32877                 break;
32878             case 'sm' :
32879                 this.x = 2;
32880                 this.y = 2;
32881                 break;
32882             case 'md' :
32883             case 'md-left' :
32884             case 'md-right' :
32885                 this.x = 3;
32886                 this.y = 3;
32887                 break;
32888             case 'tall' :
32889                 this.x = 2;
32890                 this.y = 3;
32891                 break;
32892             case 'wide' :
32893                 this.x = 3;
32894                 this.y = 2;
32895                 break;
32896             case 'wide-thin' :
32897                 this.x = 3;
32898                 this.y = 1;
32899                 break;
32900                         
32901             default :
32902                 break;
32903         }
32904         
32905         if(Roo.isTouch){
32906             this.el.on('touchstart', this.onTouchStart, this);
32907             this.el.on('touchmove', this.onTouchMove, this);
32908             this.el.on('touchend', this.onTouchEnd, this);
32909             this.el.on('contextmenu', this.onContextMenu, this);
32910         } else {
32911             this.el.on('mouseenter'  ,this.enter, this);
32912             this.el.on('mouseleave', this.leave, this);
32913             this.el.on('click', this.onClick, this);
32914         }
32915         
32916         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32917             this.parent().bricks.push(this);   
32918         }
32919         
32920     },
32921     
32922     onClick: function(e, el)
32923     {
32924         var time = this.endTimer - this.startTimer;
32925         // Roo.log(e.preventDefault());
32926         if(Roo.isTouch){
32927             if(time > 1000){
32928                 e.preventDefault();
32929                 return;
32930             }
32931         }
32932         
32933         if(!this.preventDefault){
32934             return;
32935         }
32936         
32937         e.preventDefault();
32938         
32939         if (this.activeClass != '') {
32940             this.selectBrick();
32941         }
32942         
32943         this.fireEvent('click', this, e);
32944     },
32945     
32946     enter: function(e, el)
32947     {
32948         e.preventDefault();
32949         
32950         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32951             return;
32952         }
32953         
32954         if(this.bgimage.length && this.html.length){
32955             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32956         }
32957     },
32958     
32959     leave: function(e, el)
32960     {
32961         e.preventDefault();
32962         
32963         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32964             return;
32965         }
32966         
32967         if(this.bgimage.length && this.html.length){
32968             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32969         }
32970     },
32971     
32972     onTouchStart: function(e, el)
32973     {
32974 //        e.preventDefault();
32975         
32976         this.touchmoved = false;
32977         
32978         if(!this.isFitContainer){
32979             return;
32980         }
32981         
32982         if(!this.bgimage.length || !this.html.length){
32983             return;
32984         }
32985         
32986         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32987         
32988         this.timer = new Date().getTime();
32989         
32990     },
32991     
32992     onTouchMove: function(e, el)
32993     {
32994         this.touchmoved = true;
32995     },
32996     
32997     onContextMenu : function(e,el)
32998     {
32999         e.preventDefault();
33000         e.stopPropagation();
33001         return false;
33002     },
33003     
33004     onTouchEnd: function(e, el)
33005     {
33006 //        e.preventDefault();
33007         
33008         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33009         
33010             this.leave(e,el);
33011             
33012             return;
33013         }
33014         
33015         if(!this.bgimage.length || !this.html.length){
33016             
33017             if(this.href.length){
33018                 window.location.href = this.href;
33019             }
33020             
33021             return;
33022         }
33023         
33024         if(!this.isFitContainer){
33025             return;
33026         }
33027         
33028         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33029         
33030         window.location.href = this.href;
33031     },
33032     
33033     //selection on single brick only
33034     selectBrick : function() {
33035         
33036         if (!this.parentId) {
33037             return;
33038         }
33039         
33040         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33041         var index = m.selectedBrick.indexOf(this.id);
33042         
33043         if ( index > -1) {
33044             m.selectedBrick.splice(index,1);
33045             this.el.removeClass(this.activeClass);
33046             return;
33047         }
33048         
33049         for(var i = 0; i < m.selectedBrick.length; i++) {
33050             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33051             b.el.removeClass(b.activeClass);
33052         }
33053         
33054         m.selectedBrick = [];
33055         
33056         m.selectedBrick.push(this.id);
33057         this.el.addClass(this.activeClass);
33058         return;
33059     },
33060     
33061     isSelected : function(){
33062         return this.el.hasClass(this.activeClass);
33063         
33064     }
33065 });
33066
33067 Roo.apply(Roo.bootstrap.MasonryBrick, {
33068     
33069     //groups: {},
33070     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33071      /**
33072     * register a Masonry Brick
33073     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33074     */
33075     
33076     register : function(brick)
33077     {
33078         //this.groups[brick.id] = brick;
33079         this.groups.add(brick.id, brick);
33080     },
33081     /**
33082     * fetch a  masonry brick based on the masonry brick ID
33083     * @param {string} the masonry brick to add
33084     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33085     */
33086     
33087     get: function(brick_id) 
33088     {
33089         // if (typeof(this.groups[brick_id]) == 'undefined') {
33090         //     return false;
33091         // }
33092         // return this.groups[brick_id] ;
33093         
33094         if(this.groups.key(brick_id)) {
33095             return this.groups.key(brick_id);
33096         }
33097         
33098         return false;
33099     }
33100     
33101     
33102     
33103 });
33104
33105  /*
33106  * - LGPL
33107  *
33108  * element
33109  * 
33110  */
33111
33112 /**
33113  * @class Roo.bootstrap.Brick
33114  * @extends Roo.bootstrap.Component
33115  * Bootstrap Brick class
33116  * 
33117  * @constructor
33118  * Create a new Brick
33119  * @param {Object} config The config object
33120  */
33121
33122 Roo.bootstrap.Brick = function(config){
33123     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33124     
33125     this.addEvents({
33126         // raw events
33127         /**
33128          * @event click
33129          * When a Brick is click
33130          * @param {Roo.bootstrap.Brick} this
33131          * @param {Roo.EventObject} e
33132          */
33133         "click" : true
33134     });
33135 };
33136
33137 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33138     
33139     /**
33140      * @cfg {String} title
33141      */   
33142     title : '',
33143     /**
33144      * @cfg {String} html
33145      */   
33146     html : '',
33147     /**
33148      * @cfg {String} bgimage
33149      */   
33150     bgimage : '',
33151     /**
33152      * @cfg {String} cls
33153      */   
33154     cls : '',
33155     /**
33156      * @cfg {String} href
33157      */   
33158     href : '',
33159     /**
33160      * @cfg {String} video
33161      */   
33162     video : '',
33163     /**
33164      * @cfg {Boolean} square
33165      */   
33166     square : true,
33167     
33168     getAutoCreate : function()
33169     {
33170         var cls = 'roo-brick';
33171         
33172         if(this.href.length){
33173             cls += ' roo-brick-link';
33174         }
33175         
33176         if(this.bgimage.length){
33177             cls += ' roo-brick-image';
33178         }
33179         
33180         if(!this.html.length && !this.bgimage.length){
33181             cls += ' roo-brick-center-title';
33182         }
33183         
33184         if(!this.html.length && this.bgimage.length){
33185             cls += ' roo-brick-bottom-title';
33186         }
33187         
33188         if(this.cls){
33189             cls += ' ' + this.cls;
33190         }
33191         
33192         var cfg = {
33193             tag: (this.href.length) ? 'a' : 'div',
33194             cls: cls,
33195             cn: [
33196                 {
33197                     tag: 'div',
33198                     cls: 'roo-brick-paragraph',
33199                     cn: []
33200                 }
33201             ]
33202         };
33203         
33204         if(this.href.length){
33205             cfg.href = this.href;
33206         }
33207         
33208         var cn = cfg.cn[0].cn;
33209         
33210         if(this.title.length){
33211             cn.push({
33212                 tag: 'h4',
33213                 cls: 'roo-brick-title',
33214                 html: this.title
33215             });
33216         }
33217         
33218         if(this.html.length){
33219             cn.push({
33220                 tag: 'p',
33221                 cls: 'roo-brick-text',
33222                 html: this.html
33223             });
33224         } else {
33225             cn.cls += ' hide';
33226         }
33227         
33228         if(this.bgimage.length){
33229             cfg.cn.push({
33230                 tag: 'img',
33231                 cls: 'roo-brick-image-view',
33232                 src: this.bgimage
33233             });
33234         }
33235         
33236         return cfg;
33237     },
33238     
33239     initEvents: function() 
33240     {
33241         if(this.title.length || this.html.length){
33242             this.el.on('mouseenter'  ,this.enter, this);
33243             this.el.on('mouseleave', this.leave, this);
33244         }
33245         
33246         Roo.EventManager.onWindowResize(this.resize, this); 
33247         
33248         if(this.bgimage.length){
33249             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33250             this.imageEl.on('load', this.onImageLoad, this);
33251             return;
33252         }
33253         
33254         this.resize();
33255     },
33256     
33257     onImageLoad : function()
33258     {
33259         this.resize();
33260     },
33261     
33262     resize : function()
33263     {
33264         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33265         
33266         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33267         
33268         if(this.bgimage.length){
33269             var image = this.el.select('.roo-brick-image-view', true).first();
33270             
33271             image.setWidth(paragraph.getWidth());
33272             
33273             if(this.square){
33274                 image.setHeight(paragraph.getWidth());
33275             }
33276             
33277             this.el.setHeight(image.getHeight());
33278             paragraph.setHeight(image.getHeight());
33279             
33280         }
33281         
33282     },
33283     
33284     enter: function(e, el)
33285     {
33286         e.preventDefault();
33287         
33288         if(this.bgimage.length){
33289             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33290             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33291         }
33292     },
33293     
33294     leave: function(e, el)
33295     {
33296         e.preventDefault();
33297         
33298         if(this.bgimage.length){
33299             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33300             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33301         }
33302     }
33303     
33304 });
33305
33306  
33307
33308  /*
33309  * - LGPL
33310  *
33311  * Number field 
33312  */
33313
33314 /**
33315  * @class Roo.bootstrap.NumberField
33316  * @extends Roo.bootstrap.Input
33317  * Bootstrap NumberField class
33318  * 
33319  * 
33320  * 
33321  * 
33322  * @constructor
33323  * Create a new NumberField
33324  * @param {Object} config The config object
33325  */
33326
33327 Roo.bootstrap.NumberField = function(config){
33328     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33329 };
33330
33331 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33332     
33333     /**
33334      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33335      */
33336     allowDecimals : true,
33337     /**
33338      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33339      */
33340     decimalSeparator : ".",
33341     /**
33342      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33343      */
33344     decimalPrecision : 2,
33345     /**
33346      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33347      */
33348     allowNegative : true,
33349     
33350     /**
33351      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33352      */
33353     allowZero: true,
33354     /**
33355      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33356      */
33357     minValue : Number.NEGATIVE_INFINITY,
33358     /**
33359      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33360      */
33361     maxValue : Number.MAX_VALUE,
33362     /**
33363      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33364      */
33365     minText : "The minimum value for this field is {0}",
33366     /**
33367      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33368      */
33369     maxText : "The maximum value for this field is {0}",
33370     /**
33371      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33372      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33373      */
33374     nanText : "{0} is not a valid number",
33375     /**
33376      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33377      */
33378     thousandsDelimiter : false,
33379     /**
33380      * @cfg {String} valueAlign alignment of value
33381      */
33382     valueAlign : "left",
33383
33384     getAutoCreate : function()
33385     {
33386         var hiddenInput = {
33387             tag: 'input',
33388             type: 'hidden',
33389             id: Roo.id(),
33390             cls: 'hidden-number-input'
33391         };
33392         
33393         if (this.name) {
33394             hiddenInput.name = this.name;
33395         }
33396         
33397         this.name = '';
33398         
33399         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33400         
33401         this.name = hiddenInput.name;
33402         
33403         if(cfg.cn.length > 0) {
33404             cfg.cn.push(hiddenInput);
33405         }
33406         
33407         return cfg;
33408     },
33409
33410     // private
33411     initEvents : function()
33412     {   
33413         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33414         
33415         var allowed = "0123456789";
33416         
33417         if(this.allowDecimals){
33418             allowed += this.decimalSeparator;
33419         }
33420         
33421         if(this.allowNegative){
33422             allowed += "-";
33423         }
33424         
33425         if(this.thousandsDelimiter) {
33426             allowed += ",";
33427         }
33428         
33429         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33430         
33431         var keyPress = function(e){
33432             
33433             var k = e.getKey();
33434             
33435             var c = e.getCharCode();
33436             
33437             if(
33438                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33439                     allowed.indexOf(String.fromCharCode(c)) === -1
33440             ){
33441                 e.stopEvent();
33442                 return;
33443             }
33444             
33445             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33446                 return;
33447             }
33448             
33449             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33450                 e.stopEvent();
33451             }
33452         };
33453         
33454         this.el.on("keypress", keyPress, this);
33455     },
33456     
33457     validateValue : function(value)
33458     {
33459         
33460         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33461             return false;
33462         }
33463         
33464         var num = this.parseValue(value);
33465         
33466         if(isNaN(num)){
33467             this.markInvalid(String.format(this.nanText, value));
33468             return false;
33469         }
33470         
33471         if(num < this.minValue){
33472             this.markInvalid(String.format(this.minText, this.minValue));
33473             return false;
33474         }
33475         
33476         if(num > this.maxValue){
33477             this.markInvalid(String.format(this.maxText, this.maxValue));
33478             return false;
33479         }
33480         
33481         return true;
33482     },
33483
33484     getValue : function()
33485     {
33486         var v = this.hiddenEl().getValue();
33487         
33488         return this.fixPrecision(this.parseValue(v));
33489     },
33490
33491     parseValue : function(value)
33492     {
33493         if(this.thousandsDelimiter) {
33494             value += "";
33495             r = new RegExp(",", "g");
33496             value = value.replace(r, "");
33497         }
33498         
33499         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33500         return isNaN(value) ? '' : value;
33501     },
33502
33503     fixPrecision : function(value)
33504     {
33505         if(this.thousandsDelimiter) {
33506             value += "";
33507             r = new RegExp(",", "g");
33508             value = value.replace(r, "");
33509         }
33510         
33511         var nan = isNaN(value);
33512         
33513         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33514             return nan ? '' : value;
33515         }
33516         return parseFloat(value).toFixed(this.decimalPrecision);
33517     },
33518
33519     setValue : function(v)
33520     {
33521         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33522         
33523         this.value = v;
33524         
33525         if(this.rendered){
33526             
33527             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33528             
33529             this.inputEl().dom.value = (v == '') ? '' :
33530                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33531             
33532             if(!this.allowZero && v === '0') {
33533                 this.hiddenEl().dom.value = '';
33534                 this.inputEl().dom.value = '';
33535             }
33536             
33537             this.validate();
33538         }
33539     },
33540
33541     decimalPrecisionFcn : function(v)
33542     {
33543         return Math.floor(v);
33544     },
33545
33546     beforeBlur : function()
33547     {
33548         var v = this.parseValue(this.getRawValue());
33549         
33550         if(v || v === 0 || v === ''){
33551             this.setValue(v);
33552         }
33553     },
33554     
33555     hiddenEl : function()
33556     {
33557         return this.el.select('input.hidden-number-input',true).first();
33558     }
33559     
33560 });
33561
33562  
33563
33564 /*
33565 * Licence: LGPL
33566 */
33567
33568 /**
33569  * @class Roo.bootstrap.DocumentSlider
33570  * @extends Roo.bootstrap.Component
33571  * Bootstrap DocumentSlider class
33572  * 
33573  * @constructor
33574  * Create a new DocumentViewer
33575  * @param {Object} config The config object
33576  */
33577
33578 Roo.bootstrap.DocumentSlider = function(config){
33579     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33580     
33581     this.files = [];
33582     
33583     this.addEvents({
33584         /**
33585          * @event initial
33586          * Fire after initEvent
33587          * @param {Roo.bootstrap.DocumentSlider} this
33588          */
33589         "initial" : true,
33590         /**
33591          * @event update
33592          * Fire after update
33593          * @param {Roo.bootstrap.DocumentSlider} this
33594          */
33595         "update" : true,
33596         /**
33597          * @event click
33598          * Fire after click
33599          * @param {Roo.bootstrap.DocumentSlider} this
33600          */
33601         "click" : true
33602     });
33603 };
33604
33605 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33606     
33607     files : false,
33608     
33609     indicator : 0,
33610     
33611     getAutoCreate : function()
33612     {
33613         var cfg = {
33614             tag : 'div',
33615             cls : 'roo-document-slider',
33616             cn : [
33617                 {
33618                     tag : 'div',
33619                     cls : 'roo-document-slider-header',
33620                     cn : [
33621                         {
33622                             tag : 'div',
33623                             cls : 'roo-document-slider-header-title'
33624                         }
33625                     ]
33626                 },
33627                 {
33628                     tag : 'div',
33629                     cls : 'roo-document-slider-body',
33630                     cn : [
33631                         {
33632                             tag : 'div',
33633                             cls : 'roo-document-slider-prev',
33634                             cn : [
33635                                 {
33636                                     tag : 'i',
33637                                     cls : 'fa fa-chevron-left'
33638                                 }
33639                             ]
33640                         },
33641                         {
33642                             tag : 'div',
33643                             cls : 'roo-document-slider-thumb',
33644                             cn : [
33645                                 {
33646                                     tag : 'img',
33647                                     cls : 'roo-document-slider-image'
33648                                 }
33649                             ]
33650                         },
33651                         {
33652                             tag : 'div',
33653                             cls : 'roo-document-slider-next',
33654                             cn : [
33655                                 {
33656                                     tag : 'i',
33657                                     cls : 'fa fa-chevron-right'
33658                                 }
33659                             ]
33660                         }
33661                     ]
33662                 }
33663             ]
33664         };
33665         
33666         return cfg;
33667     },
33668     
33669     initEvents : function()
33670     {
33671         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33672         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33673         
33674         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33675         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33676         
33677         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33678         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33679         
33680         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33681         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33682         
33683         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33684         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33685         
33686         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33687         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33688         
33689         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33690         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33691         
33692         this.thumbEl.on('click', this.onClick, this);
33693         
33694         this.prevIndicator.on('click', this.prev, this);
33695         
33696         this.nextIndicator.on('click', this.next, this);
33697         
33698     },
33699     
33700     initial : function()
33701     {
33702         if(this.files.length){
33703             this.indicator = 1;
33704             this.update()
33705         }
33706         
33707         this.fireEvent('initial', this);
33708     },
33709     
33710     update : function()
33711     {
33712         this.imageEl.attr('src', this.files[this.indicator - 1]);
33713         
33714         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33715         
33716         this.prevIndicator.show();
33717         
33718         if(this.indicator == 1){
33719             this.prevIndicator.hide();
33720         }
33721         
33722         this.nextIndicator.show();
33723         
33724         if(this.indicator == this.files.length){
33725             this.nextIndicator.hide();
33726         }
33727         
33728         this.thumbEl.scrollTo('top');
33729         
33730         this.fireEvent('update', this);
33731     },
33732     
33733     onClick : function(e)
33734     {
33735         e.preventDefault();
33736         
33737         this.fireEvent('click', this);
33738     },
33739     
33740     prev : function(e)
33741     {
33742         e.preventDefault();
33743         
33744         this.indicator = Math.max(1, this.indicator - 1);
33745         
33746         this.update();
33747     },
33748     
33749     next : function(e)
33750     {
33751         e.preventDefault();
33752         
33753         this.indicator = Math.min(this.files.length, this.indicator + 1);
33754         
33755         this.update();
33756     }
33757 });
33758 /*
33759  * - LGPL
33760  *
33761  * RadioSet
33762  *
33763  *
33764  */
33765
33766 /**
33767  * @class Roo.bootstrap.RadioSet
33768  * @extends Roo.bootstrap.Input
33769  * Bootstrap RadioSet class
33770  * @cfg {String} indicatorpos (left|right) default left
33771  * @cfg {Boolean} inline (true|false) inline the element (default true)
33772  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33773  * @constructor
33774  * Create a new RadioSet
33775  * @param {Object} config The config object
33776  */
33777
33778 Roo.bootstrap.RadioSet = function(config){
33779     
33780     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33781     
33782     this.radioes = [];
33783     
33784     Roo.bootstrap.RadioSet.register(this);
33785     
33786     this.addEvents({
33787         /**
33788         * @event check
33789         * Fires when the element is checked or unchecked.
33790         * @param {Roo.bootstrap.RadioSet} this This radio
33791         * @param {Roo.bootstrap.Radio} item The checked item
33792         */
33793        check : true,
33794        /**
33795         * @event click
33796         * Fires when the element is click.
33797         * @param {Roo.bootstrap.RadioSet} this This radio set
33798         * @param {Roo.bootstrap.Radio} item The checked item
33799         * @param {Roo.EventObject} e The event object
33800         */
33801        click : true
33802     });
33803     
33804 };
33805
33806 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33807
33808     radioes : false,
33809     
33810     inline : true,
33811     
33812     weight : '',
33813     
33814     indicatorpos : 'left',
33815     
33816     getAutoCreate : function()
33817     {
33818         var label = {
33819             tag : 'label',
33820             cls : 'roo-radio-set-label',
33821             cn : [
33822                 {
33823                     tag : 'span',
33824                     html : this.fieldLabel
33825                 }
33826             ]
33827         };
33828         
33829         if(this.indicatorpos == 'left'){
33830             label.cn.unshift({
33831                 tag : 'i',
33832                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33833                 tooltip : 'This field is required'
33834             });
33835         } else {
33836             label.cn.push({
33837                 tag : 'i',
33838                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33839                 tooltip : 'This field is required'
33840             });
33841         }
33842         
33843         var items = {
33844             tag : 'div',
33845             cls : 'roo-radio-set-items'
33846         };
33847         
33848         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33849         
33850         if (align === 'left' && this.fieldLabel.length) {
33851             
33852             items = {
33853                 cls : "roo-radio-set-right", 
33854                 cn: [
33855                     items
33856                 ]
33857             };
33858             
33859             if(this.labelWidth > 12){
33860                 label.style = "width: " + this.labelWidth + 'px';
33861             }
33862             
33863             if(this.labelWidth < 13 && this.labelmd == 0){
33864                 this.labelmd = this.labelWidth;
33865             }
33866             
33867             if(this.labellg > 0){
33868                 label.cls += ' col-lg-' + this.labellg;
33869                 items.cls += ' col-lg-' + (12 - this.labellg);
33870             }
33871             
33872             if(this.labelmd > 0){
33873                 label.cls += ' col-md-' + this.labelmd;
33874                 items.cls += ' col-md-' + (12 - this.labelmd);
33875             }
33876             
33877             if(this.labelsm > 0){
33878                 label.cls += ' col-sm-' + this.labelsm;
33879                 items.cls += ' col-sm-' + (12 - this.labelsm);
33880             }
33881             
33882             if(this.labelxs > 0){
33883                 label.cls += ' col-xs-' + this.labelxs;
33884                 items.cls += ' col-xs-' + (12 - this.labelxs);
33885             }
33886         }
33887         
33888         var cfg = {
33889             tag : 'div',
33890             cls : 'roo-radio-set',
33891             cn : [
33892                 {
33893                     tag : 'input',
33894                     cls : 'roo-radio-set-input',
33895                     type : 'hidden',
33896                     name : this.name,
33897                     value : this.value ? this.value :  ''
33898                 },
33899                 label,
33900                 items
33901             ]
33902         };
33903         
33904         if(this.weight.length){
33905             cfg.cls += ' roo-radio-' + this.weight;
33906         }
33907         
33908         if(this.inline) {
33909             cfg.cls += ' roo-radio-set-inline';
33910         }
33911         
33912         var settings=this;
33913         ['xs','sm','md','lg'].map(function(size){
33914             if (settings[size]) {
33915                 cfg.cls += ' col-' + size + '-' + settings[size];
33916             }
33917         });
33918         
33919         return cfg;
33920         
33921     },
33922
33923     initEvents : function()
33924     {
33925         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33926         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33927         
33928         if(!this.fieldLabel.length){
33929             this.labelEl.hide();
33930         }
33931         
33932         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33933         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33934         
33935         this.indicator = this.indicatorEl();
33936         
33937         if(this.indicator){
33938             this.indicator.addClass('invisible');
33939         }
33940         
33941         this.originalValue = this.getValue();
33942         
33943     },
33944     
33945     inputEl: function ()
33946     {
33947         return this.el.select('.roo-radio-set-input', true).first();
33948     },
33949     
33950     getChildContainer : function()
33951     {
33952         return this.itemsEl;
33953     },
33954     
33955     register : function(item)
33956     {
33957         this.radioes.push(item);
33958         
33959     },
33960     
33961     validate : function()
33962     {   
33963         if(this.getVisibilityEl().hasClass('hidden')){
33964             return true;
33965         }
33966         
33967         var valid = false;
33968         
33969         Roo.each(this.radioes, function(i){
33970             if(!i.checked){
33971                 return;
33972             }
33973             
33974             valid = true;
33975             return false;
33976         });
33977         
33978         if(this.allowBlank) {
33979             return true;
33980         }
33981         
33982         if(this.disabled || valid){
33983             this.markValid();
33984             return true;
33985         }
33986         
33987         this.markInvalid();
33988         return false;
33989         
33990     },
33991     
33992     markValid : function()
33993     {
33994         if(this.labelEl.isVisible(true)){
33995             this.indicatorEl().removeClass('visible');
33996             this.indicatorEl().addClass('invisible');
33997         }
33998         
33999         this.el.removeClass([this.invalidClass, this.validClass]);
34000         this.el.addClass(this.validClass);
34001         
34002         this.fireEvent('valid', this);
34003     },
34004     
34005     markInvalid : function(msg)
34006     {
34007         if(this.allowBlank || this.disabled){
34008             return;
34009         }
34010         
34011         if(this.labelEl.isVisible(true)){
34012             this.indicatorEl().removeClass('invisible');
34013             this.indicatorEl().addClass('visible');
34014         }
34015         
34016         this.el.removeClass([this.invalidClass, this.validClass]);
34017         this.el.addClass(this.invalidClass);
34018         
34019         this.fireEvent('invalid', this, msg);
34020         
34021     },
34022     
34023     setValue : function(v, suppressEvent)
34024     {   
34025         if(this.value === v){
34026             return;
34027         }
34028         
34029         this.value = v;
34030         
34031         if(this.rendered){
34032             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34033         }
34034         
34035         Roo.each(this.radioes, function(i){
34036             i.checked = false;
34037             i.el.removeClass('checked');
34038         });
34039         
34040         Roo.each(this.radioes, function(i){
34041             
34042             if(i.value === v || i.value.toString() === v.toString()){
34043                 i.checked = true;
34044                 i.el.addClass('checked');
34045                 
34046                 if(suppressEvent !== true){
34047                     this.fireEvent('check', this, i);
34048                 }
34049                 
34050                 return false;
34051             }
34052             
34053         }, this);
34054         
34055         this.validate();
34056     },
34057     
34058     clearInvalid : function(){
34059         
34060         if(!this.el || this.preventMark){
34061             return;
34062         }
34063         
34064         this.el.removeClass([this.invalidClass]);
34065         
34066         this.fireEvent('valid', this);
34067     }
34068     
34069 });
34070
34071 Roo.apply(Roo.bootstrap.RadioSet, {
34072     
34073     groups: {},
34074     
34075     register : function(set)
34076     {
34077         this.groups[set.name] = set;
34078     },
34079     
34080     get: function(name) 
34081     {
34082         if (typeof(this.groups[name]) == 'undefined') {
34083             return false;
34084         }
34085         
34086         return this.groups[name] ;
34087     }
34088     
34089 });
34090 /*
34091  * Based on:
34092  * Ext JS Library 1.1.1
34093  * Copyright(c) 2006-2007, Ext JS, LLC.
34094  *
34095  * Originally Released Under LGPL - original licence link has changed is not relivant.
34096  *
34097  * Fork - LGPL
34098  * <script type="text/javascript">
34099  */
34100
34101
34102 /**
34103  * @class Roo.bootstrap.SplitBar
34104  * @extends Roo.util.Observable
34105  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34106  * <br><br>
34107  * Usage:
34108  * <pre><code>
34109 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34110                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34111 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34112 split.minSize = 100;
34113 split.maxSize = 600;
34114 split.animate = true;
34115 split.on('moved', splitterMoved);
34116 </code></pre>
34117  * @constructor
34118  * Create a new SplitBar
34119  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34120  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34121  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34122  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34123                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34124                         position of the SplitBar).
34125  */
34126 Roo.bootstrap.SplitBar = function(cfg){
34127     
34128     /** @private */
34129     
34130     //{
34131     //  dragElement : elm
34132     //  resizingElement: el,
34133         // optional..
34134     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34135     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34136         // existingProxy ???
34137     //}
34138     
34139     this.el = Roo.get(cfg.dragElement, true);
34140     this.el.dom.unselectable = "on";
34141     /** @private */
34142     this.resizingEl = Roo.get(cfg.resizingElement, true);
34143
34144     /**
34145      * @private
34146      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34147      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34148      * @type Number
34149      */
34150     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34151     
34152     /**
34153      * The minimum size of the resizing element. (Defaults to 0)
34154      * @type Number
34155      */
34156     this.minSize = 0;
34157     
34158     /**
34159      * The maximum size of the resizing element. (Defaults to 2000)
34160      * @type Number
34161      */
34162     this.maxSize = 2000;
34163     
34164     /**
34165      * Whether to animate the transition to the new size
34166      * @type Boolean
34167      */
34168     this.animate = false;
34169     
34170     /**
34171      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34172      * @type Boolean
34173      */
34174     this.useShim = false;
34175     
34176     /** @private */
34177     this.shim = null;
34178     
34179     if(!cfg.existingProxy){
34180         /** @private */
34181         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34182     }else{
34183         this.proxy = Roo.get(cfg.existingProxy).dom;
34184     }
34185     /** @private */
34186     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34187     
34188     /** @private */
34189     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34190     
34191     /** @private */
34192     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34193     
34194     /** @private */
34195     this.dragSpecs = {};
34196     
34197     /**
34198      * @private The adapter to use to positon and resize elements
34199      */
34200     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34201     this.adapter.init(this);
34202     
34203     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34204         /** @private */
34205         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34206         this.el.addClass("roo-splitbar-h");
34207     }else{
34208         /** @private */
34209         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34210         this.el.addClass("roo-splitbar-v");
34211     }
34212     
34213     this.addEvents({
34214         /**
34215          * @event resize
34216          * Fires when the splitter is moved (alias for {@link #event-moved})
34217          * @param {Roo.bootstrap.SplitBar} this
34218          * @param {Number} newSize the new width or height
34219          */
34220         "resize" : true,
34221         /**
34222          * @event moved
34223          * Fires when the splitter is moved
34224          * @param {Roo.bootstrap.SplitBar} this
34225          * @param {Number} newSize the new width or height
34226          */
34227         "moved" : true,
34228         /**
34229          * @event beforeresize
34230          * Fires before the splitter is dragged
34231          * @param {Roo.bootstrap.SplitBar} this
34232          */
34233         "beforeresize" : true,
34234
34235         "beforeapply" : true
34236     });
34237
34238     Roo.util.Observable.call(this);
34239 };
34240
34241 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34242     onStartProxyDrag : function(x, y){
34243         this.fireEvent("beforeresize", this);
34244         if(!this.overlay){
34245             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34246             o.unselectable();
34247             o.enableDisplayMode("block");
34248             // all splitbars share the same overlay
34249             Roo.bootstrap.SplitBar.prototype.overlay = o;
34250         }
34251         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34252         this.overlay.show();
34253         Roo.get(this.proxy).setDisplayed("block");
34254         var size = this.adapter.getElementSize(this);
34255         this.activeMinSize = this.getMinimumSize();;
34256         this.activeMaxSize = this.getMaximumSize();;
34257         var c1 = size - this.activeMinSize;
34258         var c2 = Math.max(this.activeMaxSize - size, 0);
34259         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34260             this.dd.resetConstraints();
34261             this.dd.setXConstraint(
34262                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34263                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34264             );
34265             this.dd.setYConstraint(0, 0);
34266         }else{
34267             this.dd.resetConstraints();
34268             this.dd.setXConstraint(0, 0);
34269             this.dd.setYConstraint(
34270                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34271                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34272             );
34273          }
34274         this.dragSpecs.startSize = size;
34275         this.dragSpecs.startPoint = [x, y];
34276         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34277     },
34278     
34279     /** 
34280      * @private Called after the drag operation by the DDProxy
34281      */
34282     onEndProxyDrag : function(e){
34283         Roo.get(this.proxy).setDisplayed(false);
34284         var endPoint = Roo.lib.Event.getXY(e);
34285         if(this.overlay){
34286             this.overlay.hide();
34287         }
34288         var newSize;
34289         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34290             newSize = this.dragSpecs.startSize + 
34291                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34292                     endPoint[0] - this.dragSpecs.startPoint[0] :
34293                     this.dragSpecs.startPoint[0] - endPoint[0]
34294                 );
34295         }else{
34296             newSize = this.dragSpecs.startSize + 
34297                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34298                     endPoint[1] - this.dragSpecs.startPoint[1] :
34299                     this.dragSpecs.startPoint[1] - endPoint[1]
34300                 );
34301         }
34302         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34303         if(newSize != this.dragSpecs.startSize){
34304             if(this.fireEvent('beforeapply', this, newSize) !== false){
34305                 this.adapter.setElementSize(this, newSize);
34306                 this.fireEvent("moved", this, newSize);
34307                 this.fireEvent("resize", this, newSize);
34308             }
34309         }
34310     },
34311     
34312     /**
34313      * Get the adapter this SplitBar uses
34314      * @return The adapter object
34315      */
34316     getAdapter : function(){
34317         return this.adapter;
34318     },
34319     
34320     /**
34321      * Set the adapter this SplitBar uses
34322      * @param {Object} adapter A SplitBar adapter object
34323      */
34324     setAdapter : function(adapter){
34325         this.adapter = adapter;
34326         this.adapter.init(this);
34327     },
34328     
34329     /**
34330      * Gets the minimum size for the resizing element
34331      * @return {Number} The minimum size
34332      */
34333     getMinimumSize : function(){
34334         return this.minSize;
34335     },
34336     
34337     /**
34338      * Sets the minimum size for the resizing element
34339      * @param {Number} minSize The minimum size
34340      */
34341     setMinimumSize : function(minSize){
34342         this.minSize = minSize;
34343     },
34344     
34345     /**
34346      * Gets the maximum size for the resizing element
34347      * @return {Number} The maximum size
34348      */
34349     getMaximumSize : function(){
34350         return this.maxSize;
34351     },
34352     
34353     /**
34354      * Sets the maximum size for the resizing element
34355      * @param {Number} maxSize The maximum size
34356      */
34357     setMaximumSize : function(maxSize){
34358         this.maxSize = maxSize;
34359     },
34360     
34361     /**
34362      * Sets the initialize size for the resizing element
34363      * @param {Number} size The initial size
34364      */
34365     setCurrentSize : function(size){
34366         var oldAnimate = this.animate;
34367         this.animate = false;
34368         this.adapter.setElementSize(this, size);
34369         this.animate = oldAnimate;
34370     },
34371     
34372     /**
34373      * Destroy this splitbar. 
34374      * @param {Boolean} removeEl True to remove the element
34375      */
34376     destroy : function(removeEl){
34377         if(this.shim){
34378             this.shim.remove();
34379         }
34380         this.dd.unreg();
34381         this.proxy.parentNode.removeChild(this.proxy);
34382         if(removeEl){
34383             this.el.remove();
34384         }
34385     }
34386 });
34387
34388 /**
34389  * @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.
34390  */
34391 Roo.bootstrap.SplitBar.createProxy = function(dir){
34392     var proxy = new Roo.Element(document.createElement("div"));
34393     proxy.unselectable();
34394     var cls = 'roo-splitbar-proxy';
34395     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34396     document.body.appendChild(proxy.dom);
34397     return proxy.dom;
34398 };
34399
34400 /** 
34401  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34402  * Default Adapter. It assumes the splitter and resizing element are not positioned
34403  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34404  */
34405 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34406 };
34407
34408 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34409     // do nothing for now
34410     init : function(s){
34411     
34412     },
34413     /**
34414      * Called before drag operations to get the current size of the resizing element. 
34415      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34416      */
34417      getElementSize : function(s){
34418         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34419             return s.resizingEl.getWidth();
34420         }else{
34421             return s.resizingEl.getHeight();
34422         }
34423     },
34424     
34425     /**
34426      * Called after drag operations to set the size of the resizing element.
34427      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34428      * @param {Number} newSize The new size to set
34429      * @param {Function} onComplete A function to be invoked when resizing is complete
34430      */
34431     setElementSize : function(s, newSize, onComplete){
34432         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34433             if(!s.animate){
34434                 s.resizingEl.setWidth(newSize);
34435                 if(onComplete){
34436                     onComplete(s, newSize);
34437                 }
34438             }else{
34439                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34440             }
34441         }else{
34442             
34443             if(!s.animate){
34444                 s.resizingEl.setHeight(newSize);
34445                 if(onComplete){
34446                     onComplete(s, newSize);
34447                 }
34448             }else{
34449                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34450             }
34451         }
34452     }
34453 };
34454
34455 /** 
34456  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34457  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34458  * Adapter that  moves the splitter element to align with the resized sizing element. 
34459  * Used with an absolute positioned SplitBar.
34460  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34461  * document.body, make sure you assign an id to the body element.
34462  */
34463 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34464     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34465     this.container = Roo.get(container);
34466 };
34467
34468 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34469     init : function(s){
34470         this.basic.init(s);
34471     },
34472     
34473     getElementSize : function(s){
34474         return this.basic.getElementSize(s);
34475     },
34476     
34477     setElementSize : function(s, newSize, onComplete){
34478         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34479     },
34480     
34481     moveSplitter : function(s){
34482         var yes = Roo.bootstrap.SplitBar;
34483         switch(s.placement){
34484             case yes.LEFT:
34485                 s.el.setX(s.resizingEl.getRight());
34486                 break;
34487             case yes.RIGHT:
34488                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34489                 break;
34490             case yes.TOP:
34491                 s.el.setY(s.resizingEl.getBottom());
34492                 break;
34493             case yes.BOTTOM:
34494                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34495                 break;
34496         }
34497     }
34498 };
34499
34500 /**
34501  * Orientation constant - Create a vertical SplitBar
34502  * @static
34503  * @type Number
34504  */
34505 Roo.bootstrap.SplitBar.VERTICAL = 1;
34506
34507 /**
34508  * Orientation constant - Create a horizontal SplitBar
34509  * @static
34510  * @type Number
34511  */
34512 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34513
34514 /**
34515  * Placement constant - The resizing element is to the left of the splitter element
34516  * @static
34517  * @type Number
34518  */
34519 Roo.bootstrap.SplitBar.LEFT = 1;
34520
34521 /**
34522  * Placement constant - The resizing element is to the right of the splitter element
34523  * @static
34524  * @type Number
34525  */
34526 Roo.bootstrap.SplitBar.RIGHT = 2;
34527
34528 /**
34529  * Placement constant - The resizing element is positioned above the splitter element
34530  * @static
34531  * @type Number
34532  */
34533 Roo.bootstrap.SplitBar.TOP = 3;
34534
34535 /**
34536  * Placement constant - The resizing element is positioned under splitter element
34537  * @static
34538  * @type Number
34539  */
34540 Roo.bootstrap.SplitBar.BOTTOM = 4;
34541 Roo.namespace("Roo.bootstrap.layout");/*
34542  * Based on:
34543  * Ext JS Library 1.1.1
34544  * Copyright(c) 2006-2007, Ext JS, LLC.
34545  *
34546  * Originally Released Under LGPL - original licence link has changed is not relivant.
34547  *
34548  * Fork - LGPL
34549  * <script type="text/javascript">
34550  */
34551
34552 /**
34553  * @class Roo.bootstrap.layout.Manager
34554  * @extends Roo.bootstrap.Component
34555  * Base class for layout managers.
34556  */
34557 Roo.bootstrap.layout.Manager = function(config)
34558 {
34559     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34560
34561
34562
34563
34564
34565     /** false to disable window resize monitoring @type Boolean */
34566     this.monitorWindowResize = true;
34567     this.regions = {};
34568     this.addEvents({
34569         /**
34570          * @event layout
34571          * Fires when a layout is performed.
34572          * @param {Roo.LayoutManager} this
34573          */
34574         "layout" : true,
34575         /**
34576          * @event regionresized
34577          * Fires when the user resizes a region.
34578          * @param {Roo.LayoutRegion} region The resized region
34579          * @param {Number} newSize The new size (width for east/west, height for north/south)
34580          */
34581         "regionresized" : true,
34582         /**
34583          * @event regioncollapsed
34584          * Fires when a region is collapsed.
34585          * @param {Roo.LayoutRegion} region The collapsed region
34586          */
34587         "regioncollapsed" : true,
34588         /**
34589          * @event regionexpanded
34590          * Fires when a region is expanded.
34591          * @param {Roo.LayoutRegion} region The expanded region
34592          */
34593         "regionexpanded" : true
34594     });
34595     this.updating = false;
34596
34597     if (config.el) {
34598         this.el = Roo.get(config.el);
34599         this.initEvents();
34600     }
34601
34602 };
34603
34604 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34605
34606
34607     regions : null,
34608
34609     monitorWindowResize : true,
34610
34611
34612     updating : false,
34613
34614
34615     onRender : function(ct, position)
34616     {
34617         if(!this.el){
34618             this.el = Roo.get(ct);
34619             this.initEvents();
34620         }
34621         //this.fireEvent('render',this);
34622     },
34623
34624
34625     initEvents: function()
34626     {
34627
34628
34629         // ie scrollbar fix
34630         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34631             document.body.scroll = "no";
34632         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34633             this.el.position('relative');
34634         }
34635         this.id = this.el.id;
34636         this.el.addClass("roo-layout-container");
34637         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34638         if(this.el.dom != document.body ) {
34639             this.el.on('resize', this.layout,this);
34640             this.el.on('show', this.layout,this);
34641         }
34642
34643     },
34644
34645     /**
34646      * Returns true if this layout is currently being updated
34647      * @return {Boolean}
34648      */
34649     isUpdating : function(){
34650         return this.updating;
34651     },
34652
34653     /**
34654      * Suspend the LayoutManager from doing auto-layouts while
34655      * making multiple add or remove calls
34656      */
34657     beginUpdate : function(){
34658         this.updating = true;
34659     },
34660
34661     /**
34662      * Restore auto-layouts and optionally disable the manager from performing a layout
34663      * @param {Boolean} noLayout true to disable a layout update
34664      */
34665     endUpdate : function(noLayout){
34666         this.updating = false;
34667         if(!noLayout){
34668             this.layout();
34669         }
34670     },
34671
34672     layout: function(){
34673         // abstract...
34674     },
34675
34676     onRegionResized : function(region, newSize){
34677         this.fireEvent("regionresized", region, newSize);
34678         this.layout();
34679     },
34680
34681     onRegionCollapsed : function(region){
34682         this.fireEvent("regioncollapsed", region);
34683     },
34684
34685     onRegionExpanded : function(region){
34686         this.fireEvent("regionexpanded", region);
34687     },
34688
34689     /**
34690      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34691      * performs box-model adjustments.
34692      * @return {Object} The size as an object {width: (the width), height: (the height)}
34693      */
34694     getViewSize : function()
34695     {
34696         var size;
34697         if(this.el.dom != document.body){
34698             size = this.el.getSize();
34699         }else{
34700             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34701         }
34702         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34703         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34704         return size;
34705     },
34706
34707     /**
34708      * Returns the Element this layout is bound to.
34709      * @return {Roo.Element}
34710      */
34711     getEl : function(){
34712         return this.el;
34713     },
34714
34715     /**
34716      * Returns the specified region.
34717      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34718      * @return {Roo.LayoutRegion}
34719      */
34720     getRegion : function(target){
34721         return this.regions[target.toLowerCase()];
34722     },
34723
34724     onWindowResize : function(){
34725         if(this.monitorWindowResize){
34726             this.layout();
34727         }
34728     }
34729 });
34730 /*
34731  * Based on:
34732  * Ext JS Library 1.1.1
34733  * Copyright(c) 2006-2007, Ext JS, LLC.
34734  *
34735  * Originally Released Under LGPL - original licence link has changed is not relivant.
34736  *
34737  * Fork - LGPL
34738  * <script type="text/javascript">
34739  */
34740 /**
34741  * @class Roo.bootstrap.layout.Border
34742  * @extends Roo.bootstrap.layout.Manager
34743  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34744  * please see: examples/bootstrap/nested.html<br><br>
34745  
34746 <b>The container the layout is rendered into can be either the body element or any other element.
34747 If it is not the body element, the container needs to either be an absolute positioned element,
34748 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34749 the container size if it is not the body element.</b>
34750
34751 * @constructor
34752 * Create a new Border
34753 * @param {Object} config Configuration options
34754  */
34755 Roo.bootstrap.layout.Border = function(config){
34756     config = config || {};
34757     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34758     
34759     
34760     
34761     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34762         if(config[region]){
34763             config[region].region = region;
34764             this.addRegion(config[region]);
34765         }
34766     },this);
34767     
34768 };
34769
34770 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34771
34772 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34773     /**
34774      * Creates and adds a new region if it doesn't already exist.
34775      * @param {String} target The target region key (north, south, east, west or center).
34776      * @param {Object} config The regions config object
34777      * @return {BorderLayoutRegion} The new region
34778      */
34779     addRegion : function(config)
34780     {
34781         if(!this.regions[config.region]){
34782             var r = this.factory(config);
34783             this.bindRegion(r);
34784         }
34785         return this.regions[config.region];
34786     },
34787
34788     // private (kinda)
34789     bindRegion : function(r){
34790         this.regions[r.config.region] = r;
34791         
34792         r.on("visibilitychange",    this.layout, this);
34793         r.on("paneladded",          this.layout, this);
34794         r.on("panelremoved",        this.layout, this);
34795         r.on("invalidated",         this.layout, this);
34796         r.on("resized",             this.onRegionResized, this);
34797         r.on("collapsed",           this.onRegionCollapsed, this);
34798         r.on("expanded",            this.onRegionExpanded, this);
34799     },
34800
34801     /**
34802      * Performs a layout update.
34803      */
34804     layout : function()
34805     {
34806         if(this.updating) {
34807             return;
34808         }
34809         
34810         // render all the rebions if they have not been done alreayd?
34811         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34812             if(this.regions[region] && !this.regions[region].bodyEl){
34813                 this.regions[region].onRender(this.el)
34814             }
34815         },this);
34816         
34817         var size = this.getViewSize();
34818         var w = size.width;
34819         var h = size.height;
34820         var centerW = w;
34821         var centerH = h;
34822         var centerY = 0;
34823         var centerX = 0;
34824         //var x = 0, y = 0;
34825
34826         var rs = this.regions;
34827         var north = rs["north"];
34828         var south = rs["south"]; 
34829         var west = rs["west"];
34830         var east = rs["east"];
34831         var center = rs["center"];
34832         //if(this.hideOnLayout){ // not supported anymore
34833             //c.el.setStyle("display", "none");
34834         //}
34835         if(north && north.isVisible()){
34836             var b = north.getBox();
34837             var m = north.getMargins();
34838             b.width = w - (m.left+m.right);
34839             b.x = m.left;
34840             b.y = m.top;
34841             centerY = b.height + b.y + m.bottom;
34842             centerH -= centerY;
34843             north.updateBox(this.safeBox(b));
34844         }
34845         if(south && south.isVisible()){
34846             var b = south.getBox();
34847             var m = south.getMargins();
34848             b.width = w - (m.left+m.right);
34849             b.x = m.left;
34850             var totalHeight = (b.height + m.top + m.bottom);
34851             b.y = h - totalHeight + m.top;
34852             centerH -= totalHeight;
34853             south.updateBox(this.safeBox(b));
34854         }
34855         if(west && west.isVisible()){
34856             var b = west.getBox();
34857             var m = west.getMargins();
34858             b.height = centerH - (m.top+m.bottom);
34859             b.x = m.left;
34860             b.y = centerY + m.top;
34861             var totalWidth = (b.width + m.left + m.right);
34862             centerX += totalWidth;
34863             centerW -= totalWidth;
34864             west.updateBox(this.safeBox(b));
34865         }
34866         if(east && east.isVisible()){
34867             var b = east.getBox();
34868             var m = east.getMargins();
34869             b.height = centerH - (m.top+m.bottom);
34870             var totalWidth = (b.width + m.left + m.right);
34871             b.x = w - totalWidth + m.left;
34872             b.y = centerY + m.top;
34873             centerW -= totalWidth;
34874             east.updateBox(this.safeBox(b));
34875         }
34876         if(center){
34877             var m = center.getMargins();
34878             var centerBox = {
34879                 x: centerX + m.left,
34880                 y: centerY + m.top,
34881                 width: centerW - (m.left+m.right),
34882                 height: centerH - (m.top+m.bottom)
34883             };
34884             //if(this.hideOnLayout){
34885                 //center.el.setStyle("display", "block");
34886             //}
34887             center.updateBox(this.safeBox(centerBox));
34888         }
34889         this.el.repaint();
34890         this.fireEvent("layout", this);
34891     },
34892
34893     // private
34894     safeBox : function(box){
34895         box.width = Math.max(0, box.width);
34896         box.height = Math.max(0, box.height);
34897         return box;
34898     },
34899
34900     /**
34901      * Adds a ContentPanel (or subclass) to this layout.
34902      * @param {String} target The target region key (north, south, east, west or center).
34903      * @param {Roo.ContentPanel} panel The panel to add
34904      * @return {Roo.ContentPanel} The added panel
34905      */
34906     add : function(target, panel){
34907          
34908         target = target.toLowerCase();
34909         return this.regions[target].add(panel);
34910     },
34911
34912     /**
34913      * Remove a ContentPanel (or subclass) to this layout.
34914      * @param {String} target The target region key (north, south, east, west or center).
34915      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34916      * @return {Roo.ContentPanel} The removed panel
34917      */
34918     remove : function(target, panel){
34919         target = target.toLowerCase();
34920         return this.regions[target].remove(panel);
34921     },
34922
34923     /**
34924      * Searches all regions for a panel with the specified id
34925      * @param {String} panelId
34926      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34927      */
34928     findPanel : function(panelId){
34929         var rs = this.regions;
34930         for(var target in rs){
34931             if(typeof rs[target] != "function"){
34932                 var p = rs[target].getPanel(panelId);
34933                 if(p){
34934                     return p;
34935                 }
34936             }
34937         }
34938         return null;
34939     },
34940
34941     /**
34942      * Searches all regions for a panel with the specified id and activates (shows) it.
34943      * @param {String/ContentPanel} panelId The panels id or the panel itself
34944      * @return {Roo.ContentPanel} The shown panel or null
34945      */
34946     showPanel : function(panelId) {
34947       var rs = this.regions;
34948       for(var target in rs){
34949          var r = rs[target];
34950          if(typeof r != "function"){
34951             if(r.hasPanel(panelId)){
34952                return r.showPanel(panelId);
34953             }
34954          }
34955       }
34956       return null;
34957    },
34958
34959    /**
34960      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34961      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34962      */
34963    /*
34964     restoreState : function(provider){
34965         if(!provider){
34966             provider = Roo.state.Manager;
34967         }
34968         var sm = new Roo.LayoutStateManager();
34969         sm.init(this, provider);
34970     },
34971 */
34972  
34973  
34974     /**
34975      * Adds a xtype elements to the layout.
34976      * <pre><code>
34977
34978 layout.addxtype({
34979        xtype : 'ContentPanel',
34980        region: 'west',
34981        items: [ .... ]
34982    }
34983 );
34984
34985 layout.addxtype({
34986         xtype : 'NestedLayoutPanel',
34987         region: 'west',
34988         layout: {
34989            center: { },
34990            west: { }   
34991         },
34992         items : [ ... list of content panels or nested layout panels.. ]
34993    }
34994 );
34995 </code></pre>
34996      * @param {Object} cfg Xtype definition of item to add.
34997      */
34998     addxtype : function(cfg)
34999     {
35000         // basically accepts a pannel...
35001         // can accept a layout region..!?!?
35002         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35003         
35004         
35005         // theory?  children can only be panels??
35006         
35007         //if (!cfg.xtype.match(/Panel$/)) {
35008         //    return false;
35009         //}
35010         var ret = false;
35011         
35012         if (typeof(cfg.region) == 'undefined') {
35013             Roo.log("Failed to add Panel, region was not set");
35014             Roo.log(cfg);
35015             return false;
35016         }
35017         var region = cfg.region;
35018         delete cfg.region;
35019         
35020           
35021         var xitems = [];
35022         if (cfg.items) {
35023             xitems = cfg.items;
35024             delete cfg.items;
35025         }
35026         var nb = false;
35027         
35028         switch(cfg.xtype) 
35029         {
35030             case 'Content':  // ContentPanel (el, cfg)
35031             case 'Scroll':  // ContentPanel (el, cfg)
35032             case 'View': 
35033                 cfg.autoCreate = true;
35034                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35035                 //} else {
35036                 //    var el = this.el.createChild();
35037                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35038                 //}
35039                 
35040                 this.add(region, ret);
35041                 break;
35042             
35043             /*
35044             case 'TreePanel': // our new panel!
35045                 cfg.el = this.el.createChild();
35046                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35047                 this.add(region, ret);
35048                 break;
35049             */
35050             
35051             case 'Nest': 
35052                 // create a new Layout (which is  a Border Layout...
35053                 
35054                 var clayout = cfg.layout;
35055                 clayout.el  = this.el.createChild();
35056                 clayout.items   = clayout.items  || [];
35057                 
35058                 delete cfg.layout;
35059                 
35060                 // replace this exitems with the clayout ones..
35061                 xitems = clayout.items;
35062                  
35063                 // force background off if it's in center...
35064                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35065                     cfg.background = false;
35066                 }
35067                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35068                 
35069                 
35070                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35071                 //console.log('adding nested layout panel '  + cfg.toSource());
35072                 this.add(region, ret);
35073                 nb = {}; /// find first...
35074                 break;
35075             
35076             case 'Grid':
35077                 
35078                 // needs grid and region
35079                 
35080                 //var el = this.getRegion(region).el.createChild();
35081                 /*
35082                  *var el = this.el.createChild();
35083                 // create the grid first...
35084                 cfg.grid.container = el;
35085                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35086                 */
35087                 
35088                 if (region == 'center' && this.active ) {
35089                     cfg.background = false;
35090                 }
35091                 
35092                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35093                 
35094                 this.add(region, ret);
35095                 /*
35096                 if (cfg.background) {
35097                     // render grid on panel activation (if panel background)
35098                     ret.on('activate', function(gp) {
35099                         if (!gp.grid.rendered) {
35100                     //        gp.grid.render(el);
35101                         }
35102                     });
35103                 } else {
35104                   //  cfg.grid.render(el);
35105                 }
35106                 */
35107                 break;
35108            
35109            
35110             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35111                 // it was the old xcomponent building that caused this before.
35112                 // espeically if border is the top element in the tree.
35113                 ret = this;
35114                 break; 
35115                 
35116                     
35117                 
35118                 
35119                 
35120             default:
35121                 /*
35122                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35123                     
35124                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35125                     this.add(region, ret);
35126                 } else {
35127                 */
35128                     Roo.log(cfg);
35129                     throw "Can not add '" + cfg.xtype + "' to Border";
35130                     return null;
35131              
35132                                 
35133              
35134         }
35135         this.beginUpdate();
35136         // add children..
35137         var region = '';
35138         var abn = {};
35139         Roo.each(xitems, function(i)  {
35140             region = nb && i.region ? i.region : false;
35141             
35142             var add = ret.addxtype(i);
35143            
35144             if (region) {
35145                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35146                 if (!i.background) {
35147                     abn[region] = nb[region] ;
35148                 }
35149             }
35150             
35151         });
35152         this.endUpdate();
35153
35154         // make the last non-background panel active..
35155         //if (nb) { Roo.log(abn); }
35156         if (nb) {
35157             
35158             for(var r in abn) {
35159                 region = this.getRegion(r);
35160                 if (region) {
35161                     // tried using nb[r], but it does not work..
35162                      
35163                     region.showPanel(abn[r]);
35164                    
35165                 }
35166             }
35167         }
35168         return ret;
35169         
35170     },
35171     
35172     
35173 // private
35174     factory : function(cfg)
35175     {
35176         
35177         var validRegions = Roo.bootstrap.layout.Border.regions;
35178
35179         var target = cfg.region;
35180         cfg.mgr = this;
35181         
35182         var r = Roo.bootstrap.layout;
35183         Roo.log(target);
35184         switch(target){
35185             case "north":
35186                 return new r.North(cfg);
35187             case "south":
35188                 return new r.South(cfg);
35189             case "east":
35190                 return new r.East(cfg);
35191             case "west":
35192                 return new r.West(cfg);
35193             case "center":
35194                 return new r.Center(cfg);
35195         }
35196         throw 'Layout region "'+target+'" not supported.';
35197     }
35198     
35199     
35200 });
35201  /*
35202  * Based on:
35203  * Ext JS Library 1.1.1
35204  * Copyright(c) 2006-2007, Ext JS, LLC.
35205  *
35206  * Originally Released Under LGPL - original licence link has changed is not relivant.
35207  *
35208  * Fork - LGPL
35209  * <script type="text/javascript">
35210  */
35211  
35212 /**
35213  * @class Roo.bootstrap.layout.Basic
35214  * @extends Roo.util.Observable
35215  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35216  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35217  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35218  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35219  * @cfg {string}   region  the region that it inhabits..
35220  * @cfg {bool}   skipConfig skip config?
35221  * 
35222
35223  */
35224 Roo.bootstrap.layout.Basic = function(config){
35225     
35226     this.mgr = config.mgr;
35227     
35228     this.position = config.region;
35229     
35230     var skipConfig = config.skipConfig;
35231     
35232     this.events = {
35233         /**
35234          * @scope Roo.BasicLayoutRegion
35235          */
35236         
35237         /**
35238          * @event beforeremove
35239          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35240          * @param {Roo.LayoutRegion} this
35241          * @param {Roo.ContentPanel} panel The panel
35242          * @param {Object} e The cancel event object
35243          */
35244         "beforeremove" : true,
35245         /**
35246          * @event invalidated
35247          * Fires when the layout for this region is changed.
35248          * @param {Roo.LayoutRegion} this
35249          */
35250         "invalidated" : true,
35251         /**
35252          * @event visibilitychange
35253          * Fires when this region is shown or hidden 
35254          * @param {Roo.LayoutRegion} this
35255          * @param {Boolean} visibility true or false
35256          */
35257         "visibilitychange" : true,
35258         /**
35259          * @event paneladded
35260          * Fires when a panel is added. 
35261          * @param {Roo.LayoutRegion} this
35262          * @param {Roo.ContentPanel} panel The panel
35263          */
35264         "paneladded" : true,
35265         /**
35266          * @event panelremoved
35267          * Fires when a panel is removed. 
35268          * @param {Roo.LayoutRegion} this
35269          * @param {Roo.ContentPanel} panel The panel
35270          */
35271         "panelremoved" : true,
35272         /**
35273          * @event beforecollapse
35274          * Fires when this region before collapse.
35275          * @param {Roo.LayoutRegion} this
35276          */
35277         "beforecollapse" : true,
35278         /**
35279          * @event collapsed
35280          * Fires when this region is collapsed.
35281          * @param {Roo.LayoutRegion} this
35282          */
35283         "collapsed" : true,
35284         /**
35285          * @event expanded
35286          * Fires when this region is expanded.
35287          * @param {Roo.LayoutRegion} this
35288          */
35289         "expanded" : true,
35290         /**
35291          * @event slideshow
35292          * Fires when this region is slid into view.
35293          * @param {Roo.LayoutRegion} this
35294          */
35295         "slideshow" : true,
35296         /**
35297          * @event slidehide
35298          * Fires when this region slides out of view. 
35299          * @param {Roo.LayoutRegion} this
35300          */
35301         "slidehide" : true,
35302         /**
35303          * @event panelactivated
35304          * Fires when a panel is activated. 
35305          * @param {Roo.LayoutRegion} this
35306          * @param {Roo.ContentPanel} panel The activated panel
35307          */
35308         "panelactivated" : true,
35309         /**
35310          * @event resized
35311          * Fires when the user resizes this region. 
35312          * @param {Roo.LayoutRegion} this
35313          * @param {Number} newSize The new size (width for east/west, height for north/south)
35314          */
35315         "resized" : true
35316     };
35317     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35318     this.panels = new Roo.util.MixedCollection();
35319     this.panels.getKey = this.getPanelId.createDelegate(this);
35320     this.box = null;
35321     this.activePanel = null;
35322     // ensure listeners are added...
35323     
35324     if (config.listeners || config.events) {
35325         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35326             listeners : config.listeners || {},
35327             events : config.events || {}
35328         });
35329     }
35330     
35331     if(skipConfig !== true){
35332         this.applyConfig(config);
35333     }
35334 };
35335
35336 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35337 {
35338     getPanelId : function(p){
35339         return p.getId();
35340     },
35341     
35342     applyConfig : function(config){
35343         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35344         this.config = config;
35345         
35346     },
35347     
35348     /**
35349      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35350      * the width, for horizontal (north, south) the height.
35351      * @param {Number} newSize The new width or height
35352      */
35353     resizeTo : function(newSize){
35354         var el = this.el ? this.el :
35355                  (this.activePanel ? this.activePanel.getEl() : null);
35356         if(el){
35357             switch(this.position){
35358                 case "east":
35359                 case "west":
35360                     el.setWidth(newSize);
35361                     this.fireEvent("resized", this, newSize);
35362                 break;
35363                 case "north":
35364                 case "south":
35365                     el.setHeight(newSize);
35366                     this.fireEvent("resized", this, newSize);
35367                 break;                
35368             }
35369         }
35370     },
35371     
35372     getBox : function(){
35373         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35374     },
35375     
35376     getMargins : function(){
35377         return this.margins;
35378     },
35379     
35380     updateBox : function(box){
35381         this.box = box;
35382         var el = this.activePanel.getEl();
35383         el.dom.style.left = box.x + "px";
35384         el.dom.style.top = box.y + "px";
35385         this.activePanel.setSize(box.width, box.height);
35386     },
35387     
35388     /**
35389      * Returns the container element for this region.
35390      * @return {Roo.Element}
35391      */
35392     getEl : function(){
35393         return this.activePanel;
35394     },
35395     
35396     /**
35397      * Returns true if this region is currently visible.
35398      * @return {Boolean}
35399      */
35400     isVisible : function(){
35401         return this.activePanel ? true : false;
35402     },
35403     
35404     setActivePanel : function(panel){
35405         panel = this.getPanel(panel);
35406         if(this.activePanel && this.activePanel != panel){
35407             this.activePanel.setActiveState(false);
35408             this.activePanel.getEl().setLeftTop(-10000,-10000);
35409         }
35410         this.activePanel = panel;
35411         panel.setActiveState(true);
35412         if(this.box){
35413             panel.setSize(this.box.width, this.box.height);
35414         }
35415         this.fireEvent("panelactivated", this, panel);
35416         this.fireEvent("invalidated");
35417     },
35418     
35419     /**
35420      * Show the specified panel.
35421      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35422      * @return {Roo.ContentPanel} The shown panel or null
35423      */
35424     showPanel : function(panel){
35425         panel = this.getPanel(panel);
35426         if(panel){
35427             this.setActivePanel(panel);
35428         }
35429         return panel;
35430     },
35431     
35432     /**
35433      * Get the active panel for this region.
35434      * @return {Roo.ContentPanel} The active panel or null
35435      */
35436     getActivePanel : function(){
35437         return this.activePanel;
35438     },
35439     
35440     /**
35441      * Add the passed ContentPanel(s)
35442      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35443      * @return {Roo.ContentPanel} The panel added (if only one was added)
35444      */
35445     add : function(panel){
35446         if(arguments.length > 1){
35447             for(var i = 0, len = arguments.length; i < len; i++) {
35448                 this.add(arguments[i]);
35449             }
35450             return null;
35451         }
35452         if(this.hasPanel(panel)){
35453             this.showPanel(panel);
35454             return panel;
35455         }
35456         var el = panel.getEl();
35457         if(el.dom.parentNode != this.mgr.el.dom){
35458             this.mgr.el.dom.appendChild(el.dom);
35459         }
35460         if(panel.setRegion){
35461             panel.setRegion(this);
35462         }
35463         this.panels.add(panel);
35464         el.setStyle("position", "absolute");
35465         if(!panel.background){
35466             this.setActivePanel(panel);
35467             if(this.config.initialSize && this.panels.getCount()==1){
35468                 this.resizeTo(this.config.initialSize);
35469             }
35470         }
35471         this.fireEvent("paneladded", this, panel);
35472         return panel;
35473     },
35474     
35475     /**
35476      * Returns true if the panel is in this region.
35477      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35478      * @return {Boolean}
35479      */
35480     hasPanel : function(panel){
35481         if(typeof panel == "object"){ // must be panel obj
35482             panel = panel.getId();
35483         }
35484         return this.getPanel(panel) ? true : false;
35485     },
35486     
35487     /**
35488      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35489      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35490      * @param {Boolean} preservePanel Overrides the config preservePanel option
35491      * @return {Roo.ContentPanel} The panel that was removed
35492      */
35493     remove : function(panel, preservePanel){
35494         panel = this.getPanel(panel);
35495         if(!panel){
35496             return null;
35497         }
35498         var e = {};
35499         this.fireEvent("beforeremove", this, panel, e);
35500         if(e.cancel === true){
35501             return null;
35502         }
35503         var panelId = panel.getId();
35504         this.panels.removeKey(panelId);
35505         return panel;
35506     },
35507     
35508     /**
35509      * Returns the panel specified or null if it's not in this region.
35510      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35511      * @return {Roo.ContentPanel}
35512      */
35513     getPanel : function(id){
35514         if(typeof id == "object"){ // must be panel obj
35515             return id;
35516         }
35517         return this.panels.get(id);
35518     },
35519     
35520     /**
35521      * Returns this regions position (north/south/east/west/center).
35522      * @return {String} 
35523      */
35524     getPosition: function(){
35525         return this.position;    
35526     }
35527 });/*
35528  * Based on:
35529  * Ext JS Library 1.1.1
35530  * Copyright(c) 2006-2007, Ext JS, LLC.
35531  *
35532  * Originally Released Under LGPL - original licence link has changed is not relivant.
35533  *
35534  * Fork - LGPL
35535  * <script type="text/javascript">
35536  */
35537  
35538 /**
35539  * @class Roo.bootstrap.layout.Region
35540  * @extends Roo.bootstrap.layout.Basic
35541  * This class represents a region in a layout manager.
35542  
35543  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35544  * @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})
35545  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35546  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35547  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35548  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35549  * @cfg {String}    title           The title for the region (overrides panel titles)
35550  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35551  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35552  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35553  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35554  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35555  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35556  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35557  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35558  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35559  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35560
35561  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35562  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35563  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35564  * @cfg {Number}    width           For East/West panels
35565  * @cfg {Number}    height          For North/South panels
35566  * @cfg {Boolean}   split           To show the splitter
35567  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35568  * 
35569  * @cfg {string}   cls             Extra CSS classes to add to region
35570  * 
35571  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35572  * @cfg {string}   region  the region that it inhabits..
35573  *
35574
35575  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35576  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35577
35578  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35579  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35580  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35581  */
35582 Roo.bootstrap.layout.Region = function(config)
35583 {
35584     this.applyConfig(config);
35585
35586     var mgr = config.mgr;
35587     var pos = config.region;
35588     config.skipConfig = true;
35589     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35590     
35591     if (mgr.el) {
35592         this.onRender(mgr.el);   
35593     }
35594      
35595     this.visible = true;
35596     this.collapsed = false;
35597     this.unrendered_panels = [];
35598 };
35599
35600 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35601
35602     position: '', // set by wrapper (eg. north/south etc..)
35603     unrendered_panels : null,  // unrendered panels.
35604     createBody : function(){
35605         /** This region's body element 
35606         * @type Roo.Element */
35607         this.bodyEl = this.el.createChild({
35608                 tag: "div",
35609                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35610         });
35611     },
35612
35613     onRender: function(ctr, pos)
35614     {
35615         var dh = Roo.DomHelper;
35616         /** This region's container element 
35617         * @type Roo.Element */
35618         this.el = dh.append(ctr.dom, {
35619                 tag: "div",
35620                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35621             }, true);
35622         /** This region's title element 
35623         * @type Roo.Element */
35624     
35625         this.titleEl = dh.append(this.el.dom,
35626             {
35627                     tag: "div",
35628                     unselectable: "on",
35629                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35630                     children:[
35631                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35632                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35633                     ]}, true);
35634         
35635         this.titleEl.enableDisplayMode();
35636         /** This region's title text element 
35637         * @type HTMLElement */
35638         this.titleTextEl = this.titleEl.dom.firstChild;
35639         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35640         /*
35641         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35642         this.closeBtn.enableDisplayMode();
35643         this.closeBtn.on("click", this.closeClicked, this);
35644         this.closeBtn.hide();
35645     */
35646         this.createBody(this.config);
35647         if(this.config.hideWhenEmpty){
35648             this.hide();
35649             this.on("paneladded", this.validateVisibility, this);
35650             this.on("panelremoved", this.validateVisibility, this);
35651         }
35652         if(this.autoScroll){
35653             this.bodyEl.setStyle("overflow", "auto");
35654         }else{
35655             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35656         }
35657         //if(c.titlebar !== false){
35658             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35659                 this.titleEl.hide();
35660             }else{
35661                 this.titleEl.show();
35662                 if(this.config.title){
35663                     this.titleTextEl.innerHTML = this.config.title;
35664                 }
35665             }
35666         //}
35667         if(this.config.collapsed){
35668             this.collapse(true);
35669         }
35670         if(this.config.hidden){
35671             this.hide();
35672         }
35673         
35674         if (this.unrendered_panels && this.unrendered_panels.length) {
35675             for (var i =0;i< this.unrendered_panels.length; i++) {
35676                 this.add(this.unrendered_panels[i]);
35677             }
35678             this.unrendered_panels = null;
35679             
35680         }
35681         
35682     },
35683     
35684     applyConfig : function(c)
35685     {
35686         /*
35687          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35688             var dh = Roo.DomHelper;
35689             if(c.titlebar !== false){
35690                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35691                 this.collapseBtn.on("click", this.collapse, this);
35692                 this.collapseBtn.enableDisplayMode();
35693                 /*
35694                 if(c.showPin === true || this.showPin){
35695                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35696                     this.stickBtn.enableDisplayMode();
35697                     this.stickBtn.on("click", this.expand, this);
35698                     this.stickBtn.hide();
35699                 }
35700                 
35701             }
35702             */
35703             /** This region's collapsed element
35704             * @type Roo.Element */
35705             /*
35706              *
35707             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35708                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35709             ]}, true);
35710             
35711             if(c.floatable !== false){
35712                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35713                this.collapsedEl.on("click", this.collapseClick, this);
35714             }
35715
35716             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35717                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35718                    id: "message", unselectable: "on", style:{"float":"left"}});
35719                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35720              }
35721             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35722             this.expandBtn.on("click", this.expand, this);
35723             
35724         }
35725         
35726         if(this.collapseBtn){
35727             this.collapseBtn.setVisible(c.collapsible == true);
35728         }
35729         
35730         this.cmargins = c.cmargins || this.cmargins ||
35731                          (this.position == "west" || this.position == "east" ?
35732                              {top: 0, left: 2, right:2, bottom: 0} :
35733                              {top: 2, left: 0, right:0, bottom: 2});
35734         */
35735         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35736         
35737         
35738         this.bottomTabs = c.tabPosition != "top";
35739         
35740         this.autoScroll = c.autoScroll || false;
35741         
35742         
35743        
35744         
35745         this.duration = c.duration || .30;
35746         this.slideDuration = c.slideDuration || .45;
35747         this.config = c;
35748        
35749     },
35750     /**
35751      * Returns true if this region is currently visible.
35752      * @return {Boolean}
35753      */
35754     isVisible : function(){
35755         return this.visible;
35756     },
35757
35758     /**
35759      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35760      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35761      */
35762     //setCollapsedTitle : function(title){
35763     //    title = title || "&#160;";
35764      //   if(this.collapsedTitleTextEl){
35765       //      this.collapsedTitleTextEl.innerHTML = title;
35766        // }
35767     //},
35768
35769     getBox : function(){
35770         var b;
35771       //  if(!this.collapsed){
35772             b = this.el.getBox(false, true);
35773        // }else{
35774           //  b = this.collapsedEl.getBox(false, true);
35775         //}
35776         return b;
35777     },
35778
35779     getMargins : function(){
35780         return this.margins;
35781         //return this.collapsed ? this.cmargins : this.margins;
35782     },
35783 /*
35784     highlight : function(){
35785         this.el.addClass("x-layout-panel-dragover");
35786     },
35787
35788     unhighlight : function(){
35789         this.el.removeClass("x-layout-panel-dragover");
35790     },
35791 */
35792     updateBox : function(box)
35793     {
35794         if (!this.bodyEl) {
35795             return; // not rendered yet..
35796         }
35797         
35798         this.box = box;
35799         if(!this.collapsed){
35800             this.el.dom.style.left = box.x + "px";
35801             this.el.dom.style.top = box.y + "px";
35802             this.updateBody(box.width, box.height);
35803         }else{
35804             this.collapsedEl.dom.style.left = box.x + "px";
35805             this.collapsedEl.dom.style.top = box.y + "px";
35806             this.collapsedEl.setSize(box.width, box.height);
35807         }
35808         if(this.tabs){
35809             this.tabs.autoSizeTabs();
35810         }
35811     },
35812
35813     updateBody : function(w, h)
35814     {
35815         if(w !== null){
35816             this.el.setWidth(w);
35817             w -= this.el.getBorderWidth("rl");
35818             if(this.config.adjustments){
35819                 w += this.config.adjustments[0];
35820             }
35821         }
35822         if(h !== null && h > 0){
35823             this.el.setHeight(h);
35824             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35825             h -= this.el.getBorderWidth("tb");
35826             if(this.config.adjustments){
35827                 h += this.config.adjustments[1];
35828             }
35829             this.bodyEl.setHeight(h);
35830             if(this.tabs){
35831                 h = this.tabs.syncHeight(h);
35832             }
35833         }
35834         if(this.panelSize){
35835             w = w !== null ? w : this.panelSize.width;
35836             h = h !== null ? h : this.panelSize.height;
35837         }
35838         if(this.activePanel){
35839             var el = this.activePanel.getEl();
35840             w = w !== null ? w : el.getWidth();
35841             h = h !== null ? h : el.getHeight();
35842             this.panelSize = {width: w, height: h};
35843             this.activePanel.setSize(w, h);
35844         }
35845         if(Roo.isIE && this.tabs){
35846             this.tabs.el.repaint();
35847         }
35848     },
35849
35850     /**
35851      * Returns the container element for this region.
35852      * @return {Roo.Element}
35853      */
35854     getEl : function(){
35855         return this.el;
35856     },
35857
35858     /**
35859      * Hides this region.
35860      */
35861     hide : function(){
35862         //if(!this.collapsed){
35863             this.el.dom.style.left = "-2000px";
35864             this.el.hide();
35865         //}else{
35866          //   this.collapsedEl.dom.style.left = "-2000px";
35867          //   this.collapsedEl.hide();
35868        // }
35869         this.visible = false;
35870         this.fireEvent("visibilitychange", this, false);
35871     },
35872
35873     /**
35874      * Shows this region if it was previously hidden.
35875      */
35876     show : function(){
35877         //if(!this.collapsed){
35878             this.el.show();
35879         //}else{
35880         //    this.collapsedEl.show();
35881        // }
35882         this.visible = true;
35883         this.fireEvent("visibilitychange", this, true);
35884     },
35885 /*
35886     closeClicked : function(){
35887         if(this.activePanel){
35888             this.remove(this.activePanel);
35889         }
35890     },
35891
35892     collapseClick : function(e){
35893         if(this.isSlid){
35894            e.stopPropagation();
35895            this.slideIn();
35896         }else{
35897            e.stopPropagation();
35898            this.slideOut();
35899         }
35900     },
35901 */
35902     /**
35903      * Collapses this region.
35904      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35905      */
35906     /*
35907     collapse : function(skipAnim, skipCheck = false){
35908         if(this.collapsed) {
35909             return;
35910         }
35911         
35912         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35913             
35914             this.collapsed = true;
35915             if(this.split){
35916                 this.split.el.hide();
35917             }
35918             if(this.config.animate && skipAnim !== true){
35919                 this.fireEvent("invalidated", this);
35920                 this.animateCollapse();
35921             }else{
35922                 this.el.setLocation(-20000,-20000);
35923                 this.el.hide();
35924                 this.collapsedEl.show();
35925                 this.fireEvent("collapsed", this);
35926                 this.fireEvent("invalidated", this);
35927             }
35928         }
35929         
35930     },
35931 */
35932     animateCollapse : function(){
35933         // overridden
35934     },
35935
35936     /**
35937      * Expands this region if it was previously collapsed.
35938      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35939      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35940      */
35941     /*
35942     expand : function(e, skipAnim){
35943         if(e) {
35944             e.stopPropagation();
35945         }
35946         if(!this.collapsed || this.el.hasActiveFx()) {
35947             return;
35948         }
35949         if(this.isSlid){
35950             this.afterSlideIn();
35951             skipAnim = true;
35952         }
35953         this.collapsed = false;
35954         if(this.config.animate && skipAnim !== true){
35955             this.animateExpand();
35956         }else{
35957             this.el.show();
35958             if(this.split){
35959                 this.split.el.show();
35960             }
35961             this.collapsedEl.setLocation(-2000,-2000);
35962             this.collapsedEl.hide();
35963             this.fireEvent("invalidated", this);
35964             this.fireEvent("expanded", this);
35965         }
35966     },
35967 */
35968     animateExpand : function(){
35969         // overridden
35970     },
35971
35972     initTabs : function()
35973     {
35974         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35975         
35976         var ts = new Roo.bootstrap.panel.Tabs({
35977                 el: this.bodyEl.dom,
35978                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35979                 disableTooltips: this.config.disableTabTips,
35980                 toolbar : this.config.toolbar
35981             });
35982         
35983         if(this.config.hideTabs){
35984             ts.stripWrap.setDisplayed(false);
35985         }
35986         this.tabs = ts;
35987         ts.resizeTabs = this.config.resizeTabs === true;
35988         ts.minTabWidth = this.config.minTabWidth || 40;
35989         ts.maxTabWidth = this.config.maxTabWidth || 250;
35990         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35991         ts.monitorResize = false;
35992         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35993         ts.bodyEl.addClass('roo-layout-tabs-body');
35994         this.panels.each(this.initPanelAsTab, this);
35995     },
35996
35997     initPanelAsTab : function(panel){
35998         var ti = this.tabs.addTab(
35999             panel.getEl().id,
36000             panel.getTitle(),
36001             null,
36002             this.config.closeOnTab && panel.isClosable(),
36003             panel.tpl
36004         );
36005         if(panel.tabTip !== undefined){
36006             ti.setTooltip(panel.tabTip);
36007         }
36008         ti.on("activate", function(){
36009               this.setActivePanel(panel);
36010         }, this);
36011         
36012         if(this.config.closeOnTab){
36013             ti.on("beforeclose", function(t, e){
36014                 e.cancel = true;
36015                 this.remove(panel);
36016             }, this);
36017         }
36018         
36019         panel.tabItem = ti;
36020         
36021         return ti;
36022     },
36023
36024     updatePanelTitle : function(panel, title)
36025     {
36026         if(this.activePanel == panel){
36027             this.updateTitle(title);
36028         }
36029         if(this.tabs){
36030             var ti = this.tabs.getTab(panel.getEl().id);
36031             ti.setText(title);
36032             if(panel.tabTip !== undefined){
36033                 ti.setTooltip(panel.tabTip);
36034             }
36035         }
36036     },
36037
36038     updateTitle : function(title){
36039         if(this.titleTextEl && !this.config.title){
36040             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36041         }
36042     },
36043
36044     setActivePanel : function(panel)
36045     {
36046         panel = this.getPanel(panel);
36047         if(this.activePanel && this.activePanel != panel){
36048             if(this.activePanel.setActiveState(false) === false){
36049                 return;
36050             }
36051         }
36052         this.activePanel = panel;
36053         panel.setActiveState(true);
36054         if(this.panelSize){
36055             panel.setSize(this.panelSize.width, this.panelSize.height);
36056         }
36057         if(this.closeBtn){
36058             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36059         }
36060         this.updateTitle(panel.getTitle());
36061         if(this.tabs){
36062             this.fireEvent("invalidated", this);
36063         }
36064         this.fireEvent("panelactivated", this, panel);
36065     },
36066
36067     /**
36068      * Shows the specified panel.
36069      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36070      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36071      */
36072     showPanel : function(panel)
36073     {
36074         panel = this.getPanel(panel);
36075         if(panel){
36076             if(this.tabs){
36077                 var tab = this.tabs.getTab(panel.getEl().id);
36078                 if(tab.isHidden()){
36079                     this.tabs.unhideTab(tab.id);
36080                 }
36081                 tab.activate();
36082             }else{
36083                 this.setActivePanel(panel);
36084             }
36085         }
36086         return panel;
36087     },
36088
36089     /**
36090      * Get the active panel for this region.
36091      * @return {Roo.ContentPanel} The active panel or null
36092      */
36093     getActivePanel : function(){
36094         return this.activePanel;
36095     },
36096
36097     validateVisibility : function(){
36098         if(this.panels.getCount() < 1){
36099             this.updateTitle("&#160;");
36100             this.closeBtn.hide();
36101             this.hide();
36102         }else{
36103             if(!this.isVisible()){
36104                 this.show();
36105             }
36106         }
36107     },
36108
36109     /**
36110      * Adds the passed ContentPanel(s) to this region.
36111      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36112      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36113      */
36114     add : function(panel)
36115     {
36116         if(arguments.length > 1){
36117             for(var i = 0, len = arguments.length; i < len; i++) {
36118                 this.add(arguments[i]);
36119             }
36120             return null;
36121         }
36122         
36123         // if we have not been rendered yet, then we can not really do much of this..
36124         if (!this.bodyEl) {
36125             this.unrendered_panels.push(panel);
36126             return panel;
36127         }
36128         
36129         
36130         
36131         
36132         if(this.hasPanel(panel)){
36133             this.showPanel(panel);
36134             return panel;
36135         }
36136         panel.setRegion(this);
36137         this.panels.add(panel);
36138        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36139             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36140             // and hide them... ???
36141             this.bodyEl.dom.appendChild(panel.getEl().dom);
36142             if(panel.background !== true){
36143                 this.setActivePanel(panel);
36144             }
36145             this.fireEvent("paneladded", this, panel);
36146             return panel;
36147         }
36148         */
36149         if(!this.tabs){
36150             this.initTabs();
36151         }else{
36152             this.initPanelAsTab(panel);
36153         }
36154         
36155         
36156         if(panel.background !== true){
36157             this.tabs.activate(panel.getEl().id);
36158         }
36159         this.fireEvent("paneladded", this, panel);
36160         return panel;
36161     },
36162
36163     /**
36164      * Hides the tab for the specified panel.
36165      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36166      */
36167     hidePanel : function(panel){
36168         if(this.tabs && (panel = this.getPanel(panel))){
36169             this.tabs.hideTab(panel.getEl().id);
36170         }
36171     },
36172
36173     /**
36174      * Unhides the tab for a previously hidden panel.
36175      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36176      */
36177     unhidePanel : function(panel){
36178         if(this.tabs && (panel = this.getPanel(panel))){
36179             this.tabs.unhideTab(panel.getEl().id);
36180         }
36181     },
36182
36183     clearPanels : function(){
36184         while(this.panels.getCount() > 0){
36185              this.remove(this.panels.first());
36186         }
36187     },
36188
36189     /**
36190      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36191      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36192      * @param {Boolean} preservePanel Overrides the config preservePanel option
36193      * @return {Roo.ContentPanel} The panel that was removed
36194      */
36195     remove : function(panel, preservePanel)
36196     {
36197         panel = this.getPanel(panel);
36198         if(!panel){
36199             return null;
36200         }
36201         var e = {};
36202         this.fireEvent("beforeremove", this, panel, e);
36203         if(e.cancel === true){
36204             return null;
36205         }
36206         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36207         var panelId = panel.getId();
36208         this.panels.removeKey(panelId);
36209         if(preservePanel){
36210             document.body.appendChild(panel.getEl().dom);
36211         }
36212         if(this.tabs){
36213             this.tabs.removeTab(panel.getEl().id);
36214         }else if (!preservePanel){
36215             this.bodyEl.dom.removeChild(panel.getEl().dom);
36216         }
36217         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36218             var p = this.panels.first();
36219             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36220             tempEl.appendChild(p.getEl().dom);
36221             this.bodyEl.update("");
36222             this.bodyEl.dom.appendChild(p.getEl().dom);
36223             tempEl = null;
36224             this.updateTitle(p.getTitle());
36225             this.tabs = null;
36226             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36227             this.setActivePanel(p);
36228         }
36229         panel.setRegion(null);
36230         if(this.activePanel == panel){
36231             this.activePanel = null;
36232         }
36233         if(this.config.autoDestroy !== false && preservePanel !== true){
36234             try{panel.destroy();}catch(e){}
36235         }
36236         this.fireEvent("panelremoved", this, panel);
36237         return panel;
36238     },
36239
36240     /**
36241      * Returns the TabPanel component used by this region
36242      * @return {Roo.TabPanel}
36243      */
36244     getTabs : function(){
36245         return this.tabs;
36246     },
36247
36248     createTool : function(parentEl, className){
36249         var btn = Roo.DomHelper.append(parentEl, {
36250             tag: "div",
36251             cls: "x-layout-tools-button",
36252             children: [ {
36253                 tag: "div",
36254                 cls: "roo-layout-tools-button-inner " + className,
36255                 html: "&#160;"
36256             }]
36257         }, true);
36258         btn.addClassOnOver("roo-layout-tools-button-over");
36259         return btn;
36260     }
36261 });/*
36262  * Based on:
36263  * Ext JS Library 1.1.1
36264  * Copyright(c) 2006-2007, Ext JS, LLC.
36265  *
36266  * Originally Released Under LGPL - original licence link has changed is not relivant.
36267  *
36268  * Fork - LGPL
36269  * <script type="text/javascript">
36270  */
36271  
36272
36273
36274 /**
36275  * @class Roo.SplitLayoutRegion
36276  * @extends Roo.LayoutRegion
36277  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36278  */
36279 Roo.bootstrap.layout.Split = function(config){
36280     this.cursor = config.cursor;
36281     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36282 };
36283
36284 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36285 {
36286     splitTip : "Drag to resize.",
36287     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36288     useSplitTips : false,
36289
36290     applyConfig : function(config){
36291         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36292     },
36293     
36294     onRender : function(ctr,pos) {
36295         
36296         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36297         if(!this.config.split){
36298             return;
36299         }
36300         if(!this.split){
36301             
36302             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36303                             tag: "div",
36304                             id: this.el.id + "-split",
36305                             cls: "roo-layout-split roo-layout-split-"+this.position,
36306                             html: "&#160;"
36307             });
36308             /** The SplitBar for this region 
36309             * @type Roo.SplitBar */
36310             // does not exist yet...
36311             Roo.log([this.position, this.orientation]);
36312             
36313             this.split = new Roo.bootstrap.SplitBar({
36314                 dragElement : splitEl,
36315                 resizingElement: this.el,
36316                 orientation : this.orientation
36317             });
36318             
36319             this.split.on("moved", this.onSplitMove, this);
36320             this.split.useShim = this.config.useShim === true;
36321             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36322             if(this.useSplitTips){
36323                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36324             }
36325             //if(config.collapsible){
36326             //    this.split.el.on("dblclick", this.collapse,  this);
36327             //}
36328         }
36329         if(typeof this.config.minSize != "undefined"){
36330             this.split.minSize = this.config.minSize;
36331         }
36332         if(typeof this.config.maxSize != "undefined"){
36333             this.split.maxSize = this.config.maxSize;
36334         }
36335         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36336             this.hideSplitter();
36337         }
36338         
36339     },
36340
36341     getHMaxSize : function(){
36342          var cmax = this.config.maxSize || 10000;
36343          var center = this.mgr.getRegion("center");
36344          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36345     },
36346
36347     getVMaxSize : function(){
36348          var cmax = this.config.maxSize || 10000;
36349          var center = this.mgr.getRegion("center");
36350          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36351     },
36352
36353     onSplitMove : function(split, newSize){
36354         this.fireEvent("resized", this, newSize);
36355     },
36356     
36357     /** 
36358      * Returns the {@link Roo.SplitBar} for this region.
36359      * @return {Roo.SplitBar}
36360      */
36361     getSplitBar : function(){
36362         return this.split;
36363     },
36364     
36365     hide : function(){
36366         this.hideSplitter();
36367         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36368     },
36369
36370     hideSplitter : function(){
36371         if(this.split){
36372             this.split.el.setLocation(-2000,-2000);
36373             this.split.el.hide();
36374         }
36375     },
36376
36377     show : function(){
36378         if(this.split){
36379             this.split.el.show();
36380         }
36381         Roo.bootstrap.layout.Split.superclass.show.call(this);
36382     },
36383     
36384     beforeSlide: function(){
36385         if(Roo.isGecko){// firefox overflow auto bug workaround
36386             this.bodyEl.clip();
36387             if(this.tabs) {
36388                 this.tabs.bodyEl.clip();
36389             }
36390             if(this.activePanel){
36391                 this.activePanel.getEl().clip();
36392                 
36393                 if(this.activePanel.beforeSlide){
36394                     this.activePanel.beforeSlide();
36395                 }
36396             }
36397         }
36398     },
36399     
36400     afterSlide : function(){
36401         if(Roo.isGecko){// firefox overflow auto bug workaround
36402             this.bodyEl.unclip();
36403             if(this.tabs) {
36404                 this.tabs.bodyEl.unclip();
36405             }
36406             if(this.activePanel){
36407                 this.activePanel.getEl().unclip();
36408                 if(this.activePanel.afterSlide){
36409                     this.activePanel.afterSlide();
36410                 }
36411             }
36412         }
36413     },
36414
36415     initAutoHide : function(){
36416         if(this.autoHide !== false){
36417             if(!this.autoHideHd){
36418                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36419                 this.autoHideHd = {
36420                     "mouseout": function(e){
36421                         if(!e.within(this.el, true)){
36422                             st.delay(500);
36423                         }
36424                     },
36425                     "mouseover" : function(e){
36426                         st.cancel();
36427                     },
36428                     scope : this
36429                 };
36430             }
36431             this.el.on(this.autoHideHd);
36432         }
36433     },
36434
36435     clearAutoHide : function(){
36436         if(this.autoHide !== false){
36437             this.el.un("mouseout", this.autoHideHd.mouseout);
36438             this.el.un("mouseover", this.autoHideHd.mouseover);
36439         }
36440     },
36441
36442     clearMonitor : function(){
36443         Roo.get(document).un("click", this.slideInIf, this);
36444     },
36445
36446     // these names are backwards but not changed for compat
36447     slideOut : function(){
36448         if(this.isSlid || this.el.hasActiveFx()){
36449             return;
36450         }
36451         this.isSlid = true;
36452         if(this.collapseBtn){
36453             this.collapseBtn.hide();
36454         }
36455         this.closeBtnState = this.closeBtn.getStyle('display');
36456         this.closeBtn.hide();
36457         if(this.stickBtn){
36458             this.stickBtn.show();
36459         }
36460         this.el.show();
36461         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36462         this.beforeSlide();
36463         this.el.setStyle("z-index", 10001);
36464         this.el.slideIn(this.getSlideAnchor(), {
36465             callback: function(){
36466                 this.afterSlide();
36467                 this.initAutoHide();
36468                 Roo.get(document).on("click", this.slideInIf, this);
36469                 this.fireEvent("slideshow", this);
36470             },
36471             scope: this,
36472             block: true
36473         });
36474     },
36475
36476     afterSlideIn : function(){
36477         this.clearAutoHide();
36478         this.isSlid = false;
36479         this.clearMonitor();
36480         this.el.setStyle("z-index", "");
36481         if(this.collapseBtn){
36482             this.collapseBtn.show();
36483         }
36484         this.closeBtn.setStyle('display', this.closeBtnState);
36485         if(this.stickBtn){
36486             this.stickBtn.hide();
36487         }
36488         this.fireEvent("slidehide", this);
36489     },
36490
36491     slideIn : function(cb){
36492         if(!this.isSlid || this.el.hasActiveFx()){
36493             Roo.callback(cb);
36494             return;
36495         }
36496         this.isSlid = false;
36497         this.beforeSlide();
36498         this.el.slideOut(this.getSlideAnchor(), {
36499             callback: function(){
36500                 this.el.setLeftTop(-10000, -10000);
36501                 this.afterSlide();
36502                 this.afterSlideIn();
36503                 Roo.callback(cb);
36504             },
36505             scope: this,
36506             block: true
36507         });
36508     },
36509     
36510     slideInIf : function(e){
36511         if(!e.within(this.el)){
36512             this.slideIn();
36513         }
36514     },
36515
36516     animateCollapse : function(){
36517         this.beforeSlide();
36518         this.el.setStyle("z-index", 20000);
36519         var anchor = this.getSlideAnchor();
36520         this.el.slideOut(anchor, {
36521             callback : function(){
36522                 this.el.setStyle("z-index", "");
36523                 this.collapsedEl.slideIn(anchor, {duration:.3});
36524                 this.afterSlide();
36525                 this.el.setLocation(-10000,-10000);
36526                 this.el.hide();
36527                 this.fireEvent("collapsed", this);
36528             },
36529             scope: this,
36530             block: true
36531         });
36532     },
36533
36534     animateExpand : function(){
36535         this.beforeSlide();
36536         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36537         this.el.setStyle("z-index", 20000);
36538         this.collapsedEl.hide({
36539             duration:.1
36540         });
36541         this.el.slideIn(this.getSlideAnchor(), {
36542             callback : function(){
36543                 this.el.setStyle("z-index", "");
36544                 this.afterSlide();
36545                 if(this.split){
36546                     this.split.el.show();
36547                 }
36548                 this.fireEvent("invalidated", this);
36549                 this.fireEvent("expanded", this);
36550             },
36551             scope: this,
36552             block: true
36553         });
36554     },
36555
36556     anchors : {
36557         "west" : "left",
36558         "east" : "right",
36559         "north" : "top",
36560         "south" : "bottom"
36561     },
36562
36563     sanchors : {
36564         "west" : "l",
36565         "east" : "r",
36566         "north" : "t",
36567         "south" : "b"
36568     },
36569
36570     canchors : {
36571         "west" : "tl-tr",
36572         "east" : "tr-tl",
36573         "north" : "tl-bl",
36574         "south" : "bl-tl"
36575     },
36576
36577     getAnchor : function(){
36578         return this.anchors[this.position];
36579     },
36580
36581     getCollapseAnchor : function(){
36582         return this.canchors[this.position];
36583     },
36584
36585     getSlideAnchor : function(){
36586         return this.sanchors[this.position];
36587     },
36588
36589     getAlignAdj : function(){
36590         var cm = this.cmargins;
36591         switch(this.position){
36592             case "west":
36593                 return [0, 0];
36594             break;
36595             case "east":
36596                 return [0, 0];
36597             break;
36598             case "north":
36599                 return [0, 0];
36600             break;
36601             case "south":
36602                 return [0, 0];
36603             break;
36604         }
36605     },
36606
36607     getExpandAdj : function(){
36608         var c = this.collapsedEl, cm = this.cmargins;
36609         switch(this.position){
36610             case "west":
36611                 return [-(cm.right+c.getWidth()+cm.left), 0];
36612             break;
36613             case "east":
36614                 return [cm.right+c.getWidth()+cm.left, 0];
36615             break;
36616             case "north":
36617                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36618             break;
36619             case "south":
36620                 return [0, cm.top+cm.bottom+c.getHeight()];
36621             break;
36622         }
36623     }
36624 });/*
36625  * Based on:
36626  * Ext JS Library 1.1.1
36627  * Copyright(c) 2006-2007, Ext JS, LLC.
36628  *
36629  * Originally Released Under LGPL - original licence link has changed is not relivant.
36630  *
36631  * Fork - LGPL
36632  * <script type="text/javascript">
36633  */
36634 /*
36635  * These classes are private internal classes
36636  */
36637 Roo.bootstrap.layout.Center = function(config){
36638     config.region = "center";
36639     Roo.bootstrap.layout.Region.call(this, config);
36640     this.visible = true;
36641     this.minWidth = config.minWidth || 20;
36642     this.minHeight = config.minHeight || 20;
36643 };
36644
36645 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36646     hide : function(){
36647         // center panel can't be hidden
36648     },
36649     
36650     show : function(){
36651         // center panel can't be hidden
36652     },
36653     
36654     getMinWidth: function(){
36655         return this.minWidth;
36656     },
36657     
36658     getMinHeight: function(){
36659         return this.minHeight;
36660     }
36661 });
36662
36663
36664
36665
36666  
36667
36668
36669
36670
36671
36672 Roo.bootstrap.layout.North = function(config)
36673 {
36674     config.region = 'north';
36675     config.cursor = 'n-resize';
36676     
36677     Roo.bootstrap.layout.Split.call(this, config);
36678     
36679     
36680     if(this.split){
36681         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36682         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36683         this.split.el.addClass("roo-layout-split-v");
36684     }
36685     var size = config.initialSize || config.height;
36686     if(typeof size != "undefined"){
36687         this.el.setHeight(size);
36688     }
36689 };
36690 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36691 {
36692     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36693     
36694     
36695     
36696     getBox : function(){
36697         if(this.collapsed){
36698             return this.collapsedEl.getBox();
36699         }
36700         var box = this.el.getBox();
36701         if(this.split){
36702             box.height += this.split.el.getHeight();
36703         }
36704         return box;
36705     },
36706     
36707     updateBox : function(box){
36708         if(this.split && !this.collapsed){
36709             box.height -= this.split.el.getHeight();
36710             this.split.el.setLeft(box.x);
36711             this.split.el.setTop(box.y+box.height);
36712             this.split.el.setWidth(box.width);
36713         }
36714         if(this.collapsed){
36715             this.updateBody(box.width, null);
36716         }
36717         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36718     }
36719 });
36720
36721
36722
36723
36724
36725 Roo.bootstrap.layout.South = function(config){
36726     config.region = 'south';
36727     config.cursor = 's-resize';
36728     Roo.bootstrap.layout.Split.call(this, config);
36729     if(this.split){
36730         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36731         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36732         this.split.el.addClass("roo-layout-split-v");
36733     }
36734     var size = config.initialSize || config.height;
36735     if(typeof size != "undefined"){
36736         this.el.setHeight(size);
36737     }
36738 };
36739
36740 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36741     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36742     getBox : function(){
36743         if(this.collapsed){
36744             return this.collapsedEl.getBox();
36745         }
36746         var box = this.el.getBox();
36747         if(this.split){
36748             var sh = this.split.el.getHeight();
36749             box.height += sh;
36750             box.y -= sh;
36751         }
36752         return box;
36753     },
36754     
36755     updateBox : function(box){
36756         if(this.split && !this.collapsed){
36757             var sh = this.split.el.getHeight();
36758             box.height -= sh;
36759             box.y += sh;
36760             this.split.el.setLeft(box.x);
36761             this.split.el.setTop(box.y-sh);
36762             this.split.el.setWidth(box.width);
36763         }
36764         if(this.collapsed){
36765             this.updateBody(box.width, null);
36766         }
36767         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36768     }
36769 });
36770
36771 Roo.bootstrap.layout.East = function(config){
36772     config.region = "east";
36773     config.cursor = "e-resize";
36774     Roo.bootstrap.layout.Split.call(this, config);
36775     if(this.split){
36776         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36777         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36778         this.split.el.addClass("roo-layout-split-h");
36779     }
36780     var size = config.initialSize || config.width;
36781     if(typeof size != "undefined"){
36782         this.el.setWidth(size);
36783     }
36784 };
36785 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36786     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36787     getBox : function(){
36788         if(this.collapsed){
36789             return this.collapsedEl.getBox();
36790         }
36791         var box = this.el.getBox();
36792         if(this.split){
36793             var sw = this.split.el.getWidth();
36794             box.width += sw;
36795             box.x -= sw;
36796         }
36797         return box;
36798     },
36799
36800     updateBox : function(box){
36801         if(this.split && !this.collapsed){
36802             var sw = this.split.el.getWidth();
36803             box.width -= sw;
36804             this.split.el.setLeft(box.x);
36805             this.split.el.setTop(box.y);
36806             this.split.el.setHeight(box.height);
36807             box.x += sw;
36808         }
36809         if(this.collapsed){
36810             this.updateBody(null, box.height);
36811         }
36812         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36813     }
36814 });
36815
36816 Roo.bootstrap.layout.West = function(config){
36817     config.region = "west";
36818     config.cursor = "w-resize";
36819     
36820     Roo.bootstrap.layout.Split.call(this, config);
36821     if(this.split){
36822         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36823         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36824         this.split.el.addClass("roo-layout-split-h");
36825     }
36826     
36827 };
36828 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36829     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36830     
36831     onRender: function(ctr, pos)
36832     {
36833         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36834         var size = this.config.initialSize || this.config.width;
36835         if(typeof size != "undefined"){
36836             this.el.setWidth(size);
36837         }
36838     },
36839     
36840     getBox : function(){
36841         if(this.collapsed){
36842             return this.collapsedEl.getBox();
36843         }
36844         var box = this.el.getBox();
36845         if(this.split){
36846             box.width += this.split.el.getWidth();
36847         }
36848         return box;
36849     },
36850     
36851     updateBox : function(box){
36852         if(this.split && !this.collapsed){
36853             var sw = this.split.el.getWidth();
36854             box.width -= sw;
36855             this.split.el.setLeft(box.x+box.width);
36856             this.split.el.setTop(box.y);
36857             this.split.el.setHeight(box.height);
36858         }
36859         if(this.collapsed){
36860             this.updateBody(null, box.height);
36861         }
36862         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36863     }
36864 });
36865 Roo.namespace("Roo.bootstrap.panel");/*
36866  * Based on:
36867  * Ext JS Library 1.1.1
36868  * Copyright(c) 2006-2007, Ext JS, LLC.
36869  *
36870  * Originally Released Under LGPL - original licence link has changed is not relivant.
36871  *
36872  * Fork - LGPL
36873  * <script type="text/javascript">
36874  */
36875 /**
36876  * @class Roo.ContentPanel
36877  * @extends Roo.util.Observable
36878  * A basic ContentPanel element.
36879  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36880  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36881  * @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
36882  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36883  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36884  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36885  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36886  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36887  * @cfg {String} title          The title for this panel
36888  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36889  * @cfg {String} url            Calls {@link #setUrl} with this value
36890  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36891  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36892  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36893  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36894  * @cfg {Boolean} badges render the badges
36895
36896  * @constructor
36897  * Create a new ContentPanel.
36898  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36899  * @param {String/Object} config A string to set only the title or a config object
36900  * @param {String} content (optional) Set the HTML content for this panel
36901  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36902  */
36903 Roo.bootstrap.panel.Content = function( config){
36904     
36905     this.tpl = config.tpl || false;
36906     
36907     var el = config.el;
36908     var content = config.content;
36909
36910     if(config.autoCreate){ // xtype is available if this is called from factory
36911         el = Roo.id();
36912     }
36913     this.el = Roo.get(el);
36914     if(!this.el && config && config.autoCreate){
36915         if(typeof config.autoCreate == "object"){
36916             if(!config.autoCreate.id){
36917                 config.autoCreate.id = config.id||el;
36918             }
36919             this.el = Roo.DomHelper.append(document.body,
36920                         config.autoCreate, true);
36921         }else{
36922             var elcfg =  {   tag: "div",
36923                             cls: "roo-layout-inactive-content",
36924                             id: config.id||el
36925                             };
36926             if (config.html) {
36927                 elcfg.html = config.html;
36928                 
36929             }
36930                         
36931             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36932         }
36933     } 
36934     this.closable = false;
36935     this.loaded = false;
36936     this.active = false;
36937    
36938       
36939     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36940         
36941         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36942         
36943         this.wrapEl = this.el; //this.el.wrap();
36944         var ti = [];
36945         if (config.toolbar.items) {
36946             ti = config.toolbar.items ;
36947             delete config.toolbar.items ;
36948         }
36949         
36950         var nitems = [];
36951         this.toolbar.render(this.wrapEl, 'before');
36952         for(var i =0;i < ti.length;i++) {
36953           //  Roo.log(['add child', items[i]]);
36954             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36955         }
36956         this.toolbar.items = nitems;
36957         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36958         delete config.toolbar;
36959         
36960     }
36961     /*
36962     // xtype created footer. - not sure if will work as we normally have to render first..
36963     if (this.footer && !this.footer.el && this.footer.xtype) {
36964         if (!this.wrapEl) {
36965             this.wrapEl = this.el.wrap();
36966         }
36967     
36968         this.footer.container = this.wrapEl.createChild();
36969          
36970         this.footer = Roo.factory(this.footer, Roo);
36971         
36972     }
36973     */
36974     
36975      if(typeof config == "string"){
36976         this.title = config;
36977     }else{
36978         Roo.apply(this, config);
36979     }
36980     
36981     if(this.resizeEl){
36982         this.resizeEl = Roo.get(this.resizeEl, true);
36983     }else{
36984         this.resizeEl = this.el;
36985     }
36986     // handle view.xtype
36987     
36988  
36989     
36990     
36991     this.addEvents({
36992         /**
36993          * @event activate
36994          * Fires when this panel is activated. 
36995          * @param {Roo.ContentPanel} this
36996          */
36997         "activate" : true,
36998         /**
36999          * @event deactivate
37000          * Fires when this panel is activated. 
37001          * @param {Roo.ContentPanel} this
37002          */
37003         "deactivate" : true,
37004
37005         /**
37006          * @event resize
37007          * Fires when this panel is resized if fitToFrame is true.
37008          * @param {Roo.ContentPanel} this
37009          * @param {Number} width The width after any component adjustments
37010          * @param {Number} height The height after any component adjustments
37011          */
37012         "resize" : true,
37013         
37014          /**
37015          * @event render
37016          * Fires when this tab is created
37017          * @param {Roo.ContentPanel} this
37018          */
37019         "render" : true
37020         
37021         
37022         
37023     });
37024     
37025
37026     
37027     
37028     if(this.autoScroll){
37029         this.resizeEl.setStyle("overflow", "auto");
37030     } else {
37031         // fix randome scrolling
37032         //this.el.on('scroll', function() {
37033         //    Roo.log('fix random scolling');
37034         //    this.scrollTo('top',0); 
37035         //});
37036     }
37037     content = content || this.content;
37038     if(content){
37039         this.setContent(content);
37040     }
37041     if(config && config.url){
37042         this.setUrl(this.url, this.params, this.loadOnce);
37043     }
37044     
37045     
37046     
37047     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37048     
37049     if (this.view && typeof(this.view.xtype) != 'undefined') {
37050         this.view.el = this.el.appendChild(document.createElement("div"));
37051         this.view = Roo.factory(this.view); 
37052         this.view.render  &&  this.view.render(false, '');  
37053     }
37054     
37055     
37056     this.fireEvent('render', this);
37057 };
37058
37059 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37060     
37061     tabTip : '',
37062     
37063     setRegion : function(region){
37064         this.region = region;
37065         this.setActiveClass(region && !this.background);
37066     },
37067     
37068     
37069     setActiveClass: function(state)
37070     {
37071         if(state){
37072            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37073            this.el.setStyle('position','relative');
37074         }else{
37075            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37076            this.el.setStyle('position', 'absolute');
37077         } 
37078     },
37079     
37080     /**
37081      * Returns the toolbar for this Panel if one was configured. 
37082      * @return {Roo.Toolbar} 
37083      */
37084     getToolbar : function(){
37085         return this.toolbar;
37086     },
37087     
37088     setActiveState : function(active)
37089     {
37090         this.active = active;
37091         this.setActiveClass(active);
37092         if(!active){
37093             if(this.fireEvent("deactivate", this) === false){
37094                 return false;
37095             }
37096             return true;
37097         }
37098         this.fireEvent("activate", this);
37099         return true;
37100     },
37101     /**
37102      * Updates this panel's element
37103      * @param {String} content The new content
37104      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37105     */
37106     setContent : function(content, loadScripts){
37107         this.el.update(content, loadScripts);
37108     },
37109
37110     ignoreResize : function(w, h){
37111         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37112             return true;
37113         }else{
37114             this.lastSize = {width: w, height: h};
37115             return false;
37116         }
37117     },
37118     /**
37119      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37120      * @return {Roo.UpdateManager} The UpdateManager
37121      */
37122     getUpdateManager : function(){
37123         return this.el.getUpdateManager();
37124     },
37125      /**
37126      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37127      * @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:
37128 <pre><code>
37129 panel.load({
37130     url: "your-url.php",
37131     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37132     callback: yourFunction,
37133     scope: yourObject, //(optional scope)
37134     discardUrl: false,
37135     nocache: false,
37136     text: "Loading...",
37137     timeout: 30,
37138     scripts: false
37139 });
37140 </code></pre>
37141      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37142      * 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.
37143      * @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}
37144      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37145      * @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.
37146      * @return {Roo.ContentPanel} this
37147      */
37148     load : function(){
37149         var um = this.el.getUpdateManager();
37150         um.update.apply(um, arguments);
37151         return this;
37152     },
37153
37154
37155     /**
37156      * 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.
37157      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37158      * @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)
37159      * @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)
37160      * @return {Roo.UpdateManager} The UpdateManager
37161      */
37162     setUrl : function(url, params, loadOnce){
37163         if(this.refreshDelegate){
37164             this.removeListener("activate", this.refreshDelegate);
37165         }
37166         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37167         this.on("activate", this.refreshDelegate);
37168         return this.el.getUpdateManager();
37169     },
37170     
37171     _handleRefresh : function(url, params, loadOnce){
37172         if(!loadOnce || !this.loaded){
37173             var updater = this.el.getUpdateManager();
37174             updater.update(url, params, this._setLoaded.createDelegate(this));
37175         }
37176     },
37177     
37178     _setLoaded : function(){
37179         this.loaded = true;
37180     }, 
37181     
37182     /**
37183      * Returns this panel's id
37184      * @return {String} 
37185      */
37186     getId : function(){
37187         return this.el.id;
37188     },
37189     
37190     /** 
37191      * Returns this panel's element - used by regiosn to add.
37192      * @return {Roo.Element} 
37193      */
37194     getEl : function(){
37195         return this.wrapEl || this.el;
37196     },
37197     
37198    
37199     
37200     adjustForComponents : function(width, height)
37201     {
37202         //Roo.log('adjustForComponents ');
37203         if(this.resizeEl != this.el){
37204             width -= this.el.getFrameWidth('lr');
37205             height -= this.el.getFrameWidth('tb');
37206         }
37207         if(this.toolbar){
37208             var te = this.toolbar.getEl();
37209             te.setWidth(width);
37210             height -= te.getHeight();
37211         }
37212         if(this.footer){
37213             var te = this.footer.getEl();
37214             te.setWidth(width);
37215             height -= te.getHeight();
37216         }
37217         
37218         
37219         if(this.adjustments){
37220             width += this.adjustments[0];
37221             height += this.adjustments[1];
37222         }
37223         return {"width": width, "height": height};
37224     },
37225     
37226     setSize : function(width, height){
37227         if(this.fitToFrame && !this.ignoreResize(width, height)){
37228             if(this.fitContainer && this.resizeEl != this.el){
37229                 this.el.setSize(width, height);
37230             }
37231             var size = this.adjustForComponents(width, height);
37232             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37233             this.fireEvent('resize', this, size.width, size.height);
37234         }
37235     },
37236     
37237     /**
37238      * Returns this panel's title
37239      * @return {String} 
37240      */
37241     getTitle : function(){
37242         
37243         if (typeof(this.title) != 'object') {
37244             return this.title;
37245         }
37246         
37247         var t = '';
37248         for (var k in this.title) {
37249             if (!this.title.hasOwnProperty(k)) {
37250                 continue;
37251             }
37252             
37253             if (k.indexOf('-') >= 0) {
37254                 var s = k.split('-');
37255                 for (var i = 0; i<s.length; i++) {
37256                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37257                 }
37258             } else {
37259                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37260             }
37261         }
37262         return t;
37263     },
37264     
37265     /**
37266      * Set this panel's title
37267      * @param {String} title
37268      */
37269     setTitle : function(title){
37270         this.title = title;
37271         if(this.region){
37272             this.region.updatePanelTitle(this, title);
37273         }
37274     },
37275     
37276     /**
37277      * Returns true is this panel was configured to be closable
37278      * @return {Boolean} 
37279      */
37280     isClosable : function(){
37281         return this.closable;
37282     },
37283     
37284     beforeSlide : function(){
37285         this.el.clip();
37286         this.resizeEl.clip();
37287     },
37288     
37289     afterSlide : function(){
37290         this.el.unclip();
37291         this.resizeEl.unclip();
37292     },
37293     
37294     /**
37295      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37296      *   Will fail silently if the {@link #setUrl} method has not been called.
37297      *   This does not activate the panel, just updates its content.
37298      */
37299     refresh : function(){
37300         if(this.refreshDelegate){
37301            this.loaded = false;
37302            this.refreshDelegate();
37303         }
37304     },
37305     
37306     /**
37307      * Destroys this panel
37308      */
37309     destroy : function(){
37310         this.el.removeAllListeners();
37311         var tempEl = document.createElement("span");
37312         tempEl.appendChild(this.el.dom);
37313         tempEl.innerHTML = "";
37314         this.el.remove();
37315         this.el = null;
37316     },
37317     
37318     /**
37319      * form - if the content panel contains a form - this is a reference to it.
37320      * @type {Roo.form.Form}
37321      */
37322     form : false,
37323     /**
37324      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37325      *    This contains a reference to it.
37326      * @type {Roo.View}
37327      */
37328     view : false,
37329     
37330       /**
37331      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37332      * <pre><code>
37333
37334 layout.addxtype({
37335        xtype : 'Form',
37336        items: [ .... ]
37337    }
37338 );
37339
37340 </code></pre>
37341      * @param {Object} cfg Xtype definition of item to add.
37342      */
37343     
37344     
37345     getChildContainer: function () {
37346         return this.getEl();
37347     }
37348     
37349     
37350     /*
37351         var  ret = new Roo.factory(cfg);
37352         return ret;
37353         
37354         
37355         // add form..
37356         if (cfg.xtype.match(/^Form$/)) {
37357             
37358             var el;
37359             //if (this.footer) {
37360             //    el = this.footer.container.insertSibling(false, 'before');
37361             //} else {
37362                 el = this.el.createChild();
37363             //}
37364
37365             this.form = new  Roo.form.Form(cfg);
37366             
37367             
37368             if ( this.form.allItems.length) {
37369                 this.form.render(el.dom);
37370             }
37371             return this.form;
37372         }
37373         // should only have one of theses..
37374         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37375             // views.. should not be just added - used named prop 'view''
37376             
37377             cfg.el = this.el.appendChild(document.createElement("div"));
37378             // factory?
37379             
37380             var ret = new Roo.factory(cfg);
37381              
37382              ret.render && ret.render(false, ''); // render blank..
37383             this.view = ret;
37384             return ret;
37385         }
37386         return false;
37387     }
37388     \*/
37389 });
37390  
37391 /**
37392  * @class Roo.bootstrap.panel.Grid
37393  * @extends Roo.bootstrap.panel.Content
37394  * @constructor
37395  * Create a new GridPanel.
37396  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37397  * @param {Object} config A the config object
37398   
37399  */
37400
37401
37402
37403 Roo.bootstrap.panel.Grid = function(config)
37404 {
37405     
37406       
37407     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37408         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37409
37410     config.el = this.wrapper;
37411     //this.el = this.wrapper;
37412     
37413       if (config.container) {
37414         // ctor'ed from a Border/panel.grid
37415         
37416         
37417         this.wrapper.setStyle("overflow", "hidden");
37418         this.wrapper.addClass('roo-grid-container');
37419
37420     }
37421     
37422     
37423     if(config.toolbar){
37424         var tool_el = this.wrapper.createChild();    
37425         this.toolbar = Roo.factory(config.toolbar);
37426         var ti = [];
37427         if (config.toolbar.items) {
37428             ti = config.toolbar.items ;
37429             delete config.toolbar.items ;
37430         }
37431         
37432         var nitems = [];
37433         this.toolbar.render(tool_el);
37434         for(var i =0;i < ti.length;i++) {
37435           //  Roo.log(['add child', items[i]]);
37436             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37437         }
37438         this.toolbar.items = nitems;
37439         
37440         delete config.toolbar;
37441     }
37442     
37443     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37444     config.grid.scrollBody = true;;
37445     config.grid.monitorWindowResize = false; // turn off autosizing
37446     config.grid.autoHeight = false;
37447     config.grid.autoWidth = false;
37448     
37449     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37450     
37451     if (config.background) {
37452         // render grid on panel activation (if panel background)
37453         this.on('activate', function(gp) {
37454             if (!gp.grid.rendered) {
37455                 gp.grid.render(this.wrapper);
37456                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37457             }
37458         });
37459             
37460     } else {
37461         this.grid.render(this.wrapper);
37462         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37463
37464     }
37465     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37466     // ??? needed ??? config.el = this.wrapper;
37467     
37468     
37469     
37470   
37471     // xtype created footer. - not sure if will work as we normally have to render first..
37472     if (this.footer && !this.footer.el && this.footer.xtype) {
37473         
37474         var ctr = this.grid.getView().getFooterPanel(true);
37475         this.footer.dataSource = this.grid.dataSource;
37476         this.footer = Roo.factory(this.footer, Roo);
37477         this.footer.render(ctr);
37478         
37479     }
37480     
37481     
37482     
37483     
37484      
37485 };
37486
37487 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37488     getId : function(){
37489         return this.grid.id;
37490     },
37491     
37492     /**
37493      * Returns the grid for this panel
37494      * @return {Roo.bootstrap.Table} 
37495      */
37496     getGrid : function(){
37497         return this.grid;    
37498     },
37499     
37500     setSize : function(width, height){
37501         if(!this.ignoreResize(width, height)){
37502             var grid = this.grid;
37503             var size = this.adjustForComponents(width, height);
37504             var gridel = grid.getGridEl();
37505             gridel.setSize(size.width, size.height);
37506             /*
37507             var thd = grid.getGridEl().select('thead',true).first();
37508             var tbd = grid.getGridEl().select('tbody', true).first();
37509             if (tbd) {
37510                 tbd.setSize(width, height - thd.getHeight());
37511             }
37512             */
37513             grid.autoSize();
37514         }
37515     },
37516      
37517     
37518     
37519     beforeSlide : function(){
37520         this.grid.getView().scroller.clip();
37521     },
37522     
37523     afterSlide : function(){
37524         this.grid.getView().scroller.unclip();
37525     },
37526     
37527     destroy : function(){
37528         this.grid.destroy();
37529         delete this.grid;
37530         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37531     }
37532 });
37533
37534 /**
37535  * @class Roo.bootstrap.panel.Nest
37536  * @extends Roo.bootstrap.panel.Content
37537  * @constructor
37538  * Create a new Panel, that can contain a layout.Border.
37539  * 
37540  * 
37541  * @param {Roo.BorderLayout} layout The layout for this panel
37542  * @param {String/Object} config A string to set only the title or a config object
37543  */
37544 Roo.bootstrap.panel.Nest = function(config)
37545 {
37546     // construct with only one argument..
37547     /* FIXME - implement nicer consturctors
37548     if (layout.layout) {
37549         config = layout;
37550         layout = config.layout;
37551         delete config.layout;
37552     }
37553     if (layout.xtype && !layout.getEl) {
37554         // then layout needs constructing..
37555         layout = Roo.factory(layout, Roo);
37556     }
37557     */
37558     
37559     config.el =  config.layout.getEl();
37560     
37561     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37562     
37563     config.layout.monitorWindowResize = false; // turn off autosizing
37564     this.layout = config.layout;
37565     this.layout.getEl().addClass("roo-layout-nested-layout");
37566     
37567     
37568     
37569     
37570 };
37571
37572 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37573
37574     setSize : function(width, height){
37575         if(!this.ignoreResize(width, height)){
37576             var size = this.adjustForComponents(width, height);
37577             var el = this.layout.getEl();
37578             if (size.height < 1) {
37579                 el.setWidth(size.width);   
37580             } else {
37581                 el.setSize(size.width, size.height);
37582             }
37583             var touch = el.dom.offsetWidth;
37584             this.layout.layout();
37585             // ie requires a double layout on the first pass
37586             if(Roo.isIE && !this.initialized){
37587                 this.initialized = true;
37588                 this.layout.layout();
37589             }
37590         }
37591     },
37592     
37593     // activate all subpanels if not currently active..
37594     
37595     setActiveState : function(active){
37596         this.active = active;
37597         this.setActiveClass(active);
37598         
37599         if(!active){
37600             this.fireEvent("deactivate", this);
37601             return;
37602         }
37603         
37604         this.fireEvent("activate", this);
37605         // not sure if this should happen before or after..
37606         if (!this.layout) {
37607             return; // should not happen..
37608         }
37609         var reg = false;
37610         for (var r in this.layout.regions) {
37611             reg = this.layout.getRegion(r);
37612             if (reg.getActivePanel()) {
37613                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37614                 reg.setActivePanel(reg.getActivePanel());
37615                 continue;
37616             }
37617             if (!reg.panels.length) {
37618                 continue;
37619             }
37620             reg.showPanel(reg.getPanel(0));
37621         }
37622         
37623         
37624         
37625         
37626     },
37627     
37628     /**
37629      * Returns the nested BorderLayout for this panel
37630      * @return {Roo.BorderLayout} 
37631      */
37632     getLayout : function(){
37633         return this.layout;
37634     },
37635     
37636      /**
37637      * Adds a xtype elements to the layout of the nested panel
37638      * <pre><code>
37639
37640 panel.addxtype({
37641        xtype : 'ContentPanel',
37642        region: 'west',
37643        items: [ .... ]
37644    }
37645 );
37646
37647 panel.addxtype({
37648         xtype : 'NestedLayoutPanel',
37649         region: 'west',
37650         layout: {
37651            center: { },
37652            west: { }   
37653         },
37654         items : [ ... list of content panels or nested layout panels.. ]
37655    }
37656 );
37657 </code></pre>
37658      * @param {Object} cfg Xtype definition of item to add.
37659      */
37660     addxtype : function(cfg) {
37661         return this.layout.addxtype(cfg);
37662     
37663     }
37664 });        /*
37665  * Based on:
37666  * Ext JS Library 1.1.1
37667  * Copyright(c) 2006-2007, Ext JS, LLC.
37668  *
37669  * Originally Released Under LGPL - original licence link has changed is not relivant.
37670  *
37671  * Fork - LGPL
37672  * <script type="text/javascript">
37673  */
37674 /**
37675  * @class Roo.TabPanel
37676  * @extends Roo.util.Observable
37677  * A lightweight tab container.
37678  * <br><br>
37679  * Usage:
37680  * <pre><code>
37681 // basic tabs 1, built from existing content
37682 var tabs = new Roo.TabPanel("tabs1");
37683 tabs.addTab("script", "View Script");
37684 tabs.addTab("markup", "View Markup");
37685 tabs.activate("script");
37686
37687 // more advanced tabs, built from javascript
37688 var jtabs = new Roo.TabPanel("jtabs");
37689 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37690
37691 // set up the UpdateManager
37692 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37693 var updater = tab2.getUpdateManager();
37694 updater.setDefaultUrl("ajax1.htm");
37695 tab2.on('activate', updater.refresh, updater, true);
37696
37697 // Use setUrl for Ajax loading
37698 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37699 tab3.setUrl("ajax2.htm", null, true);
37700
37701 // Disabled tab
37702 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37703 tab4.disable();
37704
37705 jtabs.activate("jtabs-1");
37706  * </code></pre>
37707  * @constructor
37708  * Create a new TabPanel.
37709  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37710  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37711  */
37712 Roo.bootstrap.panel.Tabs = function(config){
37713     /**
37714     * The container element for this TabPanel.
37715     * @type Roo.Element
37716     */
37717     this.el = Roo.get(config.el);
37718     delete config.el;
37719     if(config){
37720         if(typeof config == "boolean"){
37721             this.tabPosition = config ? "bottom" : "top";
37722         }else{
37723             Roo.apply(this, config);
37724         }
37725     }
37726     
37727     if(this.tabPosition == "bottom"){
37728         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37729         this.el.addClass("roo-tabs-bottom");
37730     }
37731     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37732     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37733     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37734     if(Roo.isIE){
37735         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37736     }
37737     if(this.tabPosition != "bottom"){
37738         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37739          * @type Roo.Element
37740          */
37741         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37742         this.el.addClass("roo-tabs-top");
37743     }
37744     this.items = [];
37745
37746     this.bodyEl.setStyle("position", "relative");
37747
37748     this.active = null;
37749     this.activateDelegate = this.activate.createDelegate(this);
37750
37751     this.addEvents({
37752         /**
37753          * @event tabchange
37754          * Fires when the active tab changes
37755          * @param {Roo.TabPanel} this
37756          * @param {Roo.TabPanelItem} activePanel The new active tab
37757          */
37758         "tabchange": true,
37759         /**
37760          * @event beforetabchange
37761          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37762          * @param {Roo.TabPanel} this
37763          * @param {Object} e Set cancel to true on this object to cancel the tab change
37764          * @param {Roo.TabPanelItem} tab The tab being changed to
37765          */
37766         "beforetabchange" : true
37767     });
37768
37769     Roo.EventManager.onWindowResize(this.onResize, this);
37770     this.cpad = this.el.getPadding("lr");
37771     this.hiddenCount = 0;
37772
37773
37774     // toolbar on the tabbar support...
37775     if (this.toolbar) {
37776         alert("no toolbar support yet");
37777         this.toolbar  = false;
37778         /*
37779         var tcfg = this.toolbar;
37780         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37781         this.toolbar = new Roo.Toolbar(tcfg);
37782         if (Roo.isSafari) {
37783             var tbl = tcfg.container.child('table', true);
37784             tbl.setAttribute('width', '100%');
37785         }
37786         */
37787         
37788     }
37789    
37790
37791
37792     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37793 };
37794
37795 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37796     /*
37797      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37798      */
37799     tabPosition : "top",
37800     /*
37801      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37802      */
37803     currentTabWidth : 0,
37804     /*
37805      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37806      */
37807     minTabWidth : 40,
37808     /*
37809      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37810      */
37811     maxTabWidth : 250,
37812     /*
37813      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37814      */
37815     preferredTabWidth : 175,
37816     /*
37817      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37818      */
37819     resizeTabs : false,
37820     /*
37821      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37822      */
37823     monitorResize : true,
37824     /*
37825      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37826      */
37827     toolbar : false,
37828
37829     /**
37830      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37831      * @param {String} id The id of the div to use <b>or create</b>
37832      * @param {String} text The text for the tab
37833      * @param {String} content (optional) Content to put in the TabPanelItem body
37834      * @param {Boolean} closable (optional) True to create a close icon on the tab
37835      * @return {Roo.TabPanelItem} The created TabPanelItem
37836      */
37837     addTab : function(id, text, content, closable, tpl)
37838     {
37839         var item = new Roo.bootstrap.panel.TabItem({
37840             panel: this,
37841             id : id,
37842             text : text,
37843             closable : closable,
37844             tpl : tpl
37845         });
37846         this.addTabItem(item);
37847         if(content){
37848             item.setContent(content);
37849         }
37850         return item;
37851     },
37852
37853     /**
37854      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37855      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37856      * @return {Roo.TabPanelItem}
37857      */
37858     getTab : function(id){
37859         return this.items[id];
37860     },
37861
37862     /**
37863      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37864      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37865      */
37866     hideTab : function(id){
37867         var t = this.items[id];
37868         if(!t.isHidden()){
37869            t.setHidden(true);
37870            this.hiddenCount++;
37871            this.autoSizeTabs();
37872         }
37873     },
37874
37875     /**
37876      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37877      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37878      */
37879     unhideTab : function(id){
37880         var t = this.items[id];
37881         if(t.isHidden()){
37882            t.setHidden(false);
37883            this.hiddenCount--;
37884            this.autoSizeTabs();
37885         }
37886     },
37887
37888     /**
37889      * Adds an existing {@link Roo.TabPanelItem}.
37890      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37891      */
37892     addTabItem : function(item){
37893         this.items[item.id] = item;
37894         this.items.push(item);
37895       //  if(this.resizeTabs){
37896     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37897   //         this.autoSizeTabs();
37898 //        }else{
37899 //            item.autoSize();
37900        // }
37901     },
37902
37903     /**
37904      * Removes a {@link Roo.TabPanelItem}.
37905      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37906      */
37907     removeTab : function(id){
37908         var items = this.items;
37909         var tab = items[id];
37910         if(!tab) { return; }
37911         var index = items.indexOf(tab);
37912         if(this.active == tab && items.length > 1){
37913             var newTab = this.getNextAvailable(index);
37914             if(newTab) {
37915                 newTab.activate();
37916             }
37917         }
37918         this.stripEl.dom.removeChild(tab.pnode.dom);
37919         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37920             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37921         }
37922         items.splice(index, 1);
37923         delete this.items[tab.id];
37924         tab.fireEvent("close", tab);
37925         tab.purgeListeners();
37926         this.autoSizeTabs();
37927     },
37928
37929     getNextAvailable : function(start){
37930         var items = this.items;
37931         var index = start;
37932         // look for a next tab that will slide over to
37933         // replace the one being removed
37934         while(index < items.length){
37935             var item = items[++index];
37936             if(item && !item.isHidden()){
37937                 return item;
37938             }
37939         }
37940         // if one isn't found select the previous tab (on the left)
37941         index = start;
37942         while(index >= 0){
37943             var item = items[--index];
37944             if(item && !item.isHidden()){
37945                 return item;
37946             }
37947         }
37948         return null;
37949     },
37950
37951     /**
37952      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37953      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37954      */
37955     disableTab : function(id){
37956         var tab = this.items[id];
37957         if(tab && this.active != tab){
37958             tab.disable();
37959         }
37960     },
37961
37962     /**
37963      * Enables a {@link Roo.TabPanelItem} that is disabled.
37964      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37965      */
37966     enableTab : function(id){
37967         var tab = this.items[id];
37968         tab.enable();
37969     },
37970
37971     /**
37972      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37973      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37974      * @return {Roo.TabPanelItem} The TabPanelItem.
37975      */
37976     activate : function(id){
37977         var tab = this.items[id];
37978         if(!tab){
37979             return null;
37980         }
37981         if(tab == this.active || tab.disabled){
37982             return tab;
37983         }
37984         var e = {};
37985         this.fireEvent("beforetabchange", this, e, tab);
37986         if(e.cancel !== true && !tab.disabled){
37987             if(this.active){
37988                 this.active.hide();
37989             }
37990             this.active = this.items[id];
37991             this.active.show();
37992             this.fireEvent("tabchange", this, this.active);
37993         }
37994         return tab;
37995     },
37996
37997     /**
37998      * Gets the active {@link Roo.TabPanelItem}.
37999      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38000      */
38001     getActiveTab : function(){
38002         return this.active;
38003     },
38004
38005     /**
38006      * Updates the tab body element to fit the height of the container element
38007      * for overflow scrolling
38008      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38009      */
38010     syncHeight : function(targetHeight){
38011         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38012         var bm = this.bodyEl.getMargins();
38013         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38014         this.bodyEl.setHeight(newHeight);
38015         return newHeight;
38016     },
38017
38018     onResize : function(){
38019         if(this.monitorResize){
38020             this.autoSizeTabs();
38021         }
38022     },
38023
38024     /**
38025      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38026      */
38027     beginUpdate : function(){
38028         this.updating = true;
38029     },
38030
38031     /**
38032      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38033      */
38034     endUpdate : function(){
38035         this.updating = false;
38036         this.autoSizeTabs();
38037     },
38038
38039     /**
38040      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38041      */
38042     autoSizeTabs : function(){
38043         var count = this.items.length;
38044         var vcount = count - this.hiddenCount;
38045         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38046             return;
38047         }
38048         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38049         var availWidth = Math.floor(w / vcount);
38050         var b = this.stripBody;
38051         if(b.getWidth() > w){
38052             var tabs = this.items;
38053             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38054             if(availWidth < this.minTabWidth){
38055                 /*if(!this.sleft){    // incomplete scrolling code
38056                     this.createScrollButtons();
38057                 }
38058                 this.showScroll();
38059                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38060             }
38061         }else{
38062             if(this.currentTabWidth < this.preferredTabWidth){
38063                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38064             }
38065         }
38066     },
38067
38068     /**
38069      * Returns the number of tabs in this TabPanel.
38070      * @return {Number}
38071      */
38072      getCount : function(){
38073          return this.items.length;
38074      },
38075
38076     /**
38077      * Resizes all the tabs to the passed width
38078      * @param {Number} The new width
38079      */
38080     setTabWidth : function(width){
38081         this.currentTabWidth = width;
38082         for(var i = 0, len = this.items.length; i < len; i++) {
38083                 if(!this.items[i].isHidden()) {
38084                 this.items[i].setWidth(width);
38085             }
38086         }
38087     },
38088
38089     /**
38090      * Destroys this TabPanel
38091      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38092      */
38093     destroy : function(removeEl){
38094         Roo.EventManager.removeResizeListener(this.onResize, this);
38095         for(var i = 0, len = this.items.length; i < len; i++){
38096             this.items[i].purgeListeners();
38097         }
38098         if(removeEl === true){
38099             this.el.update("");
38100             this.el.remove();
38101         }
38102     },
38103     
38104     createStrip : function(container)
38105     {
38106         var strip = document.createElement("nav");
38107         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38108         container.appendChild(strip);
38109         return strip;
38110     },
38111     
38112     createStripList : function(strip)
38113     {
38114         // div wrapper for retard IE
38115         // returns the "tr" element.
38116         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38117         //'<div class="x-tabs-strip-wrap">'+
38118           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38119           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38120         return strip.firstChild; //.firstChild.firstChild.firstChild;
38121     },
38122     createBody : function(container)
38123     {
38124         var body = document.createElement("div");
38125         Roo.id(body, "tab-body");
38126         //Roo.fly(body).addClass("x-tabs-body");
38127         Roo.fly(body).addClass("tab-content");
38128         container.appendChild(body);
38129         return body;
38130     },
38131     createItemBody :function(bodyEl, id){
38132         var body = Roo.getDom(id);
38133         if(!body){
38134             body = document.createElement("div");
38135             body.id = id;
38136         }
38137         //Roo.fly(body).addClass("x-tabs-item-body");
38138         Roo.fly(body).addClass("tab-pane");
38139          bodyEl.insertBefore(body, bodyEl.firstChild);
38140         return body;
38141     },
38142     /** @private */
38143     createStripElements :  function(stripEl, text, closable, tpl)
38144     {
38145         var td = document.createElement("li"); // was td..
38146         
38147         
38148         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38149         
38150         
38151         stripEl.appendChild(td);
38152         /*if(closable){
38153             td.className = "x-tabs-closable";
38154             if(!this.closeTpl){
38155                 this.closeTpl = new Roo.Template(
38156                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38157                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38158                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38159                 );
38160             }
38161             var el = this.closeTpl.overwrite(td, {"text": text});
38162             var close = el.getElementsByTagName("div")[0];
38163             var inner = el.getElementsByTagName("em")[0];
38164             return {"el": el, "close": close, "inner": inner};
38165         } else {
38166         */
38167         // not sure what this is..
38168 //            if(!this.tabTpl){
38169                 //this.tabTpl = new Roo.Template(
38170                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38171                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38172                 //);
38173 //                this.tabTpl = new Roo.Template(
38174 //                   '<a href="#">' +
38175 //                   '<span unselectable="on"' +
38176 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38177 //                            ' >{text}</span></a>'
38178 //                );
38179 //                
38180 //            }
38181
38182
38183             var template = tpl || this.tabTpl || false;
38184             
38185             if(!template){
38186                 
38187                 template = new Roo.Template(
38188                    '<a href="#">' +
38189                    '<span unselectable="on"' +
38190                             (this.disableTooltips ? '' : ' title="{text}"') +
38191                             ' >{text}</span></a>'
38192                 );
38193             }
38194             
38195             switch (typeof(template)) {
38196                 case 'object' :
38197                     break;
38198                 case 'string' :
38199                     template = new Roo.Template(template);
38200                     break;
38201                 default :
38202                     break;
38203             }
38204             
38205             var el = template.overwrite(td, {"text": text});
38206             
38207             var inner = el.getElementsByTagName("span")[0];
38208             
38209             return {"el": el, "inner": inner};
38210             
38211     }
38212         
38213     
38214 });
38215
38216 /**
38217  * @class Roo.TabPanelItem
38218  * @extends Roo.util.Observable
38219  * Represents an individual item (tab plus body) in a TabPanel.
38220  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38221  * @param {String} id The id of this TabPanelItem
38222  * @param {String} text The text for the tab of this TabPanelItem
38223  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38224  */
38225 Roo.bootstrap.panel.TabItem = function(config){
38226     /**
38227      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38228      * @type Roo.TabPanel
38229      */
38230     this.tabPanel = config.panel;
38231     /**
38232      * The id for this TabPanelItem
38233      * @type String
38234      */
38235     this.id = config.id;
38236     /** @private */
38237     this.disabled = false;
38238     /** @private */
38239     this.text = config.text;
38240     /** @private */
38241     this.loaded = false;
38242     this.closable = config.closable;
38243
38244     /**
38245      * The body element for this TabPanelItem.
38246      * @type Roo.Element
38247      */
38248     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38249     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38250     this.bodyEl.setStyle("display", "block");
38251     this.bodyEl.setStyle("zoom", "1");
38252     //this.hideAction();
38253
38254     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38255     /** @private */
38256     this.el = Roo.get(els.el);
38257     this.inner = Roo.get(els.inner, true);
38258     this.textEl = Roo.get(this.el.dom.firstChild, true);
38259     this.pnode = Roo.get(els.el.parentNode, true);
38260 //    this.el.on("mousedown", this.onTabMouseDown, this);
38261     this.el.on("click", this.onTabClick, this);
38262     /** @private */
38263     if(config.closable){
38264         var c = Roo.get(els.close, true);
38265         c.dom.title = this.closeText;
38266         c.addClassOnOver("close-over");
38267         c.on("click", this.closeClick, this);
38268      }
38269
38270     this.addEvents({
38271          /**
38272          * @event activate
38273          * Fires when this tab becomes the active tab.
38274          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38275          * @param {Roo.TabPanelItem} this
38276          */
38277         "activate": true,
38278         /**
38279          * @event beforeclose
38280          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38281          * @param {Roo.TabPanelItem} this
38282          * @param {Object} e Set cancel to true on this object to cancel the close.
38283          */
38284         "beforeclose": true,
38285         /**
38286          * @event close
38287          * Fires when this tab is closed.
38288          * @param {Roo.TabPanelItem} this
38289          */
38290          "close": true,
38291         /**
38292          * @event deactivate
38293          * Fires when this tab is no longer the active tab.
38294          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38295          * @param {Roo.TabPanelItem} this
38296          */
38297          "deactivate" : true
38298     });
38299     this.hidden = false;
38300
38301     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38302 };
38303
38304 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38305            {
38306     purgeListeners : function(){
38307        Roo.util.Observable.prototype.purgeListeners.call(this);
38308        this.el.removeAllListeners();
38309     },
38310     /**
38311      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38312      */
38313     show : function(){
38314         this.pnode.addClass("active");
38315         this.showAction();
38316         if(Roo.isOpera){
38317             this.tabPanel.stripWrap.repaint();
38318         }
38319         this.fireEvent("activate", this.tabPanel, this);
38320     },
38321
38322     /**
38323      * Returns true if this tab is the active tab.
38324      * @return {Boolean}
38325      */
38326     isActive : function(){
38327         return this.tabPanel.getActiveTab() == this;
38328     },
38329
38330     /**
38331      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38332      */
38333     hide : function(){
38334         this.pnode.removeClass("active");
38335         this.hideAction();
38336         this.fireEvent("deactivate", this.tabPanel, this);
38337     },
38338
38339     hideAction : function(){
38340         this.bodyEl.hide();
38341         this.bodyEl.setStyle("position", "absolute");
38342         this.bodyEl.setLeft("-20000px");
38343         this.bodyEl.setTop("-20000px");
38344     },
38345
38346     showAction : function(){
38347         this.bodyEl.setStyle("position", "relative");
38348         this.bodyEl.setTop("");
38349         this.bodyEl.setLeft("");
38350         this.bodyEl.show();
38351     },
38352
38353     /**
38354      * Set the tooltip for the tab.
38355      * @param {String} tooltip The tab's tooltip
38356      */
38357     setTooltip : function(text){
38358         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38359             this.textEl.dom.qtip = text;
38360             this.textEl.dom.removeAttribute('title');
38361         }else{
38362             this.textEl.dom.title = text;
38363         }
38364     },
38365
38366     onTabClick : function(e){
38367         e.preventDefault();
38368         this.tabPanel.activate(this.id);
38369     },
38370
38371     onTabMouseDown : function(e){
38372         e.preventDefault();
38373         this.tabPanel.activate(this.id);
38374     },
38375 /*
38376     getWidth : function(){
38377         return this.inner.getWidth();
38378     },
38379
38380     setWidth : function(width){
38381         var iwidth = width - this.pnode.getPadding("lr");
38382         this.inner.setWidth(iwidth);
38383         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38384         this.pnode.setWidth(width);
38385     },
38386 */
38387     /**
38388      * Show or hide the tab
38389      * @param {Boolean} hidden True to hide or false to show.
38390      */
38391     setHidden : function(hidden){
38392         this.hidden = hidden;
38393         this.pnode.setStyle("display", hidden ? "none" : "");
38394     },
38395
38396     /**
38397      * Returns true if this tab is "hidden"
38398      * @return {Boolean}
38399      */
38400     isHidden : function(){
38401         return this.hidden;
38402     },
38403
38404     /**
38405      * Returns the text for this tab
38406      * @return {String}
38407      */
38408     getText : function(){
38409         return this.text;
38410     },
38411     /*
38412     autoSize : function(){
38413         //this.el.beginMeasure();
38414         this.textEl.setWidth(1);
38415         /*
38416          *  #2804 [new] Tabs in Roojs
38417          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38418          */
38419         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38420         //this.el.endMeasure();
38421     //},
38422
38423     /**
38424      * Sets the text for the tab (Note: this also sets the tooltip text)
38425      * @param {String} text The tab's text and tooltip
38426      */
38427     setText : function(text){
38428         this.text = text;
38429         this.textEl.update(text);
38430         this.setTooltip(text);
38431         //if(!this.tabPanel.resizeTabs){
38432         //    this.autoSize();
38433         //}
38434     },
38435     /**
38436      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38437      */
38438     activate : function(){
38439         this.tabPanel.activate(this.id);
38440     },
38441
38442     /**
38443      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38444      */
38445     disable : function(){
38446         if(this.tabPanel.active != this){
38447             this.disabled = true;
38448             this.pnode.addClass("disabled");
38449         }
38450     },
38451
38452     /**
38453      * Enables this TabPanelItem if it was previously disabled.
38454      */
38455     enable : function(){
38456         this.disabled = false;
38457         this.pnode.removeClass("disabled");
38458     },
38459
38460     /**
38461      * Sets the content for this TabPanelItem.
38462      * @param {String} content The content
38463      * @param {Boolean} loadScripts true to look for and load scripts
38464      */
38465     setContent : function(content, loadScripts){
38466         this.bodyEl.update(content, loadScripts);
38467     },
38468
38469     /**
38470      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38471      * @return {Roo.UpdateManager} The UpdateManager
38472      */
38473     getUpdateManager : function(){
38474         return this.bodyEl.getUpdateManager();
38475     },
38476
38477     /**
38478      * Set a URL to be used to load the content for this TabPanelItem.
38479      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38480      * @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)
38481      * @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)
38482      * @return {Roo.UpdateManager} The UpdateManager
38483      */
38484     setUrl : function(url, params, loadOnce){
38485         if(this.refreshDelegate){
38486             this.un('activate', this.refreshDelegate);
38487         }
38488         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38489         this.on("activate", this.refreshDelegate);
38490         return this.bodyEl.getUpdateManager();
38491     },
38492
38493     /** @private */
38494     _handleRefresh : function(url, params, loadOnce){
38495         if(!loadOnce || !this.loaded){
38496             var updater = this.bodyEl.getUpdateManager();
38497             updater.update(url, params, this._setLoaded.createDelegate(this));
38498         }
38499     },
38500
38501     /**
38502      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38503      *   Will fail silently if the setUrl method has not been called.
38504      *   This does not activate the panel, just updates its content.
38505      */
38506     refresh : function(){
38507         if(this.refreshDelegate){
38508            this.loaded = false;
38509            this.refreshDelegate();
38510         }
38511     },
38512
38513     /** @private */
38514     _setLoaded : function(){
38515         this.loaded = true;
38516     },
38517
38518     /** @private */
38519     closeClick : function(e){
38520         var o = {};
38521         e.stopEvent();
38522         this.fireEvent("beforeclose", this, o);
38523         if(o.cancel !== true){
38524             this.tabPanel.removeTab(this.id);
38525         }
38526     },
38527     /**
38528      * The text displayed in the tooltip for the close icon.
38529      * @type String
38530      */
38531     closeText : "Close this tab"
38532 });
38533 /**
38534 *    This script refer to:
38535 *    Title: International Telephone Input
38536 *    Author: Jack O'Connor
38537 *    Code version:  v12.1.12
38538 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38539 **/
38540
38541 Roo.bootstrap.PhoneInputData = function() {
38542     var d = [
38543       [
38544         "Afghanistan (‫افغانستان‬‎)",
38545         "af",
38546         "93"
38547       ],
38548       [
38549         "Albania (Shqipëri)",
38550         "al",
38551         "355"
38552       ],
38553       [
38554         "Algeria (‫الجزائر‬‎)",
38555         "dz",
38556         "213"
38557       ],
38558       [
38559         "American Samoa",
38560         "as",
38561         "1684"
38562       ],
38563       [
38564         "Andorra",
38565         "ad",
38566         "376"
38567       ],
38568       [
38569         "Angola",
38570         "ao",
38571         "244"
38572       ],
38573       [
38574         "Anguilla",
38575         "ai",
38576         "1264"
38577       ],
38578       [
38579         "Antigua and Barbuda",
38580         "ag",
38581         "1268"
38582       ],
38583       [
38584         "Argentina",
38585         "ar",
38586         "54"
38587       ],
38588       [
38589         "Armenia (Հայաստան)",
38590         "am",
38591         "374"
38592       ],
38593       [
38594         "Aruba",
38595         "aw",
38596         "297"
38597       ],
38598       [
38599         "Australia",
38600         "au",
38601         "61",
38602         0
38603       ],
38604       [
38605         "Austria (Österreich)",
38606         "at",
38607         "43"
38608       ],
38609       [
38610         "Azerbaijan (Azərbaycan)",
38611         "az",
38612         "994"
38613       ],
38614       [
38615         "Bahamas",
38616         "bs",
38617         "1242"
38618       ],
38619       [
38620         "Bahrain (‫البحرين‬‎)",
38621         "bh",
38622         "973"
38623       ],
38624       [
38625         "Bangladesh (বাংলাদেশ)",
38626         "bd",
38627         "880"
38628       ],
38629       [
38630         "Barbados",
38631         "bb",
38632         "1246"
38633       ],
38634       [
38635         "Belarus (Беларусь)",
38636         "by",
38637         "375"
38638       ],
38639       [
38640         "Belgium (België)",
38641         "be",
38642         "32"
38643       ],
38644       [
38645         "Belize",
38646         "bz",
38647         "501"
38648       ],
38649       [
38650         "Benin (Bénin)",
38651         "bj",
38652         "229"
38653       ],
38654       [
38655         "Bermuda",
38656         "bm",
38657         "1441"
38658       ],
38659       [
38660         "Bhutan (འབྲུག)",
38661         "bt",
38662         "975"
38663       ],
38664       [
38665         "Bolivia",
38666         "bo",
38667         "591"
38668       ],
38669       [
38670         "Bosnia and Herzegovina (Босна и Херцеговина)",
38671         "ba",
38672         "387"
38673       ],
38674       [
38675         "Botswana",
38676         "bw",
38677         "267"
38678       ],
38679       [
38680         "Brazil (Brasil)",
38681         "br",
38682         "55"
38683       ],
38684       [
38685         "British Indian Ocean Territory",
38686         "io",
38687         "246"
38688       ],
38689       [
38690         "British Virgin Islands",
38691         "vg",
38692         "1284"
38693       ],
38694       [
38695         "Brunei",
38696         "bn",
38697         "673"
38698       ],
38699       [
38700         "Bulgaria (България)",
38701         "bg",
38702         "359"
38703       ],
38704       [
38705         "Burkina Faso",
38706         "bf",
38707         "226"
38708       ],
38709       [
38710         "Burundi (Uburundi)",
38711         "bi",
38712         "257"
38713       ],
38714       [
38715         "Cambodia (កម្ពុជា)",
38716         "kh",
38717         "855"
38718       ],
38719       [
38720         "Cameroon (Cameroun)",
38721         "cm",
38722         "237"
38723       ],
38724       [
38725         "Canada",
38726         "ca",
38727         "1",
38728         1,
38729         ["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"]
38730       ],
38731       [
38732         "Cape Verde (Kabu Verdi)",
38733         "cv",
38734         "238"
38735       ],
38736       [
38737         "Caribbean Netherlands",
38738         "bq",
38739         "599",
38740         1
38741       ],
38742       [
38743         "Cayman Islands",
38744         "ky",
38745         "1345"
38746       ],
38747       [
38748         "Central African Republic (République centrafricaine)",
38749         "cf",
38750         "236"
38751       ],
38752       [
38753         "Chad (Tchad)",
38754         "td",
38755         "235"
38756       ],
38757       [
38758         "Chile",
38759         "cl",
38760         "56"
38761       ],
38762       [
38763         "China (中国)",
38764         "cn",
38765         "86"
38766       ],
38767       [
38768         "Christmas Island",
38769         "cx",
38770         "61",
38771         2
38772       ],
38773       [
38774         "Cocos (Keeling) Islands",
38775         "cc",
38776         "61",
38777         1
38778       ],
38779       [
38780         "Colombia",
38781         "co",
38782         "57"
38783       ],
38784       [
38785         "Comoros (‫جزر القمر‬‎)",
38786         "km",
38787         "269"
38788       ],
38789       [
38790         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38791         "cd",
38792         "243"
38793       ],
38794       [
38795         "Congo (Republic) (Congo-Brazzaville)",
38796         "cg",
38797         "242"
38798       ],
38799       [
38800         "Cook Islands",
38801         "ck",
38802         "682"
38803       ],
38804       [
38805         "Costa Rica",
38806         "cr",
38807         "506"
38808       ],
38809       [
38810         "Côte d’Ivoire",
38811         "ci",
38812         "225"
38813       ],
38814       [
38815         "Croatia (Hrvatska)",
38816         "hr",
38817         "385"
38818       ],
38819       [
38820         "Cuba",
38821         "cu",
38822         "53"
38823       ],
38824       [
38825         "Curaçao",
38826         "cw",
38827         "599",
38828         0
38829       ],
38830       [
38831         "Cyprus (Κύπρος)",
38832         "cy",
38833         "357"
38834       ],
38835       [
38836         "Czech Republic (Česká republika)",
38837         "cz",
38838         "420"
38839       ],
38840       [
38841         "Denmark (Danmark)",
38842         "dk",
38843         "45"
38844       ],
38845       [
38846         "Djibouti",
38847         "dj",
38848         "253"
38849       ],
38850       [
38851         "Dominica",
38852         "dm",
38853         "1767"
38854       ],
38855       [
38856         "Dominican Republic (República Dominicana)",
38857         "do",
38858         "1",
38859         2,
38860         ["809", "829", "849"]
38861       ],
38862       [
38863         "Ecuador",
38864         "ec",
38865         "593"
38866       ],
38867       [
38868         "Egypt (‫مصر‬‎)",
38869         "eg",
38870         "20"
38871       ],
38872       [
38873         "El Salvador",
38874         "sv",
38875         "503"
38876       ],
38877       [
38878         "Equatorial Guinea (Guinea Ecuatorial)",
38879         "gq",
38880         "240"
38881       ],
38882       [
38883         "Eritrea",
38884         "er",
38885         "291"
38886       ],
38887       [
38888         "Estonia (Eesti)",
38889         "ee",
38890         "372"
38891       ],
38892       [
38893         "Ethiopia",
38894         "et",
38895         "251"
38896       ],
38897       [
38898         "Falkland Islands (Islas Malvinas)",
38899         "fk",
38900         "500"
38901       ],
38902       [
38903         "Faroe Islands (Føroyar)",
38904         "fo",
38905         "298"
38906       ],
38907       [
38908         "Fiji",
38909         "fj",
38910         "679"
38911       ],
38912       [
38913         "Finland (Suomi)",
38914         "fi",
38915         "358",
38916         0
38917       ],
38918       [
38919         "France",
38920         "fr",
38921         "33"
38922       ],
38923       [
38924         "French Guiana (Guyane française)",
38925         "gf",
38926         "594"
38927       ],
38928       [
38929         "French Polynesia (Polynésie française)",
38930         "pf",
38931         "689"
38932       ],
38933       [
38934         "Gabon",
38935         "ga",
38936         "241"
38937       ],
38938       [
38939         "Gambia",
38940         "gm",
38941         "220"
38942       ],
38943       [
38944         "Georgia (საქართველო)",
38945         "ge",
38946         "995"
38947       ],
38948       [
38949         "Germany (Deutschland)",
38950         "de",
38951         "49"
38952       ],
38953       [
38954         "Ghana (Gaana)",
38955         "gh",
38956         "233"
38957       ],
38958       [
38959         "Gibraltar",
38960         "gi",
38961         "350"
38962       ],
38963       [
38964         "Greece (Ελλάδα)",
38965         "gr",
38966         "30"
38967       ],
38968       [
38969         "Greenland (Kalaallit Nunaat)",
38970         "gl",
38971         "299"
38972       ],
38973       [
38974         "Grenada",
38975         "gd",
38976         "1473"
38977       ],
38978       [
38979         "Guadeloupe",
38980         "gp",
38981         "590",
38982         0
38983       ],
38984       [
38985         "Guam",
38986         "gu",
38987         "1671"
38988       ],
38989       [
38990         "Guatemala",
38991         "gt",
38992         "502"
38993       ],
38994       [
38995         "Guernsey",
38996         "gg",
38997         "44",
38998         1
38999       ],
39000       [
39001         "Guinea (Guinée)",
39002         "gn",
39003         "224"
39004       ],
39005       [
39006         "Guinea-Bissau (Guiné Bissau)",
39007         "gw",
39008         "245"
39009       ],
39010       [
39011         "Guyana",
39012         "gy",
39013         "592"
39014       ],
39015       [
39016         "Haiti",
39017         "ht",
39018         "509"
39019       ],
39020       [
39021         "Honduras",
39022         "hn",
39023         "504"
39024       ],
39025       [
39026         "Hong Kong (香港)",
39027         "hk",
39028         "852"
39029       ],
39030       [
39031         "Hungary (Magyarország)",
39032         "hu",
39033         "36"
39034       ],
39035       [
39036         "Iceland (Ísland)",
39037         "is",
39038         "354"
39039       ],
39040       [
39041         "India (भारत)",
39042         "in",
39043         "91"
39044       ],
39045       [
39046         "Indonesia",
39047         "id",
39048         "62"
39049       ],
39050       [
39051         "Iran (‫ایران‬‎)",
39052         "ir",
39053         "98"
39054       ],
39055       [
39056         "Iraq (‫العراق‬‎)",
39057         "iq",
39058         "964"
39059       ],
39060       [
39061         "Ireland",
39062         "ie",
39063         "353"
39064       ],
39065       [
39066         "Isle of Man",
39067         "im",
39068         "44",
39069         2
39070       ],
39071       [
39072         "Israel (‫ישראל‬‎)",
39073         "il",
39074         "972"
39075       ],
39076       [
39077         "Italy (Italia)",
39078         "it",
39079         "39",
39080         0
39081       ],
39082       [
39083         "Jamaica",
39084         "jm",
39085         "1876"
39086       ],
39087       [
39088         "Japan (日本)",
39089         "jp",
39090         "81"
39091       ],
39092       [
39093         "Jersey",
39094         "je",
39095         "44",
39096         3
39097       ],
39098       [
39099         "Jordan (‫الأردن‬‎)",
39100         "jo",
39101         "962"
39102       ],
39103       [
39104         "Kazakhstan (Казахстан)",
39105         "kz",
39106         "7",
39107         1
39108       ],
39109       [
39110         "Kenya",
39111         "ke",
39112         "254"
39113       ],
39114       [
39115         "Kiribati",
39116         "ki",
39117         "686"
39118       ],
39119       [
39120         "Kosovo",
39121         "xk",
39122         "383"
39123       ],
39124       [
39125         "Kuwait (‫الكويت‬‎)",
39126         "kw",
39127         "965"
39128       ],
39129       [
39130         "Kyrgyzstan (Кыргызстан)",
39131         "kg",
39132         "996"
39133       ],
39134       [
39135         "Laos (ລາວ)",
39136         "la",
39137         "856"
39138       ],
39139       [
39140         "Latvia (Latvija)",
39141         "lv",
39142         "371"
39143       ],
39144       [
39145         "Lebanon (‫لبنان‬‎)",
39146         "lb",
39147         "961"
39148       ],
39149       [
39150         "Lesotho",
39151         "ls",
39152         "266"
39153       ],
39154       [
39155         "Liberia",
39156         "lr",
39157         "231"
39158       ],
39159       [
39160         "Libya (‫ليبيا‬‎)",
39161         "ly",
39162         "218"
39163       ],
39164       [
39165         "Liechtenstein",
39166         "li",
39167         "423"
39168       ],
39169       [
39170         "Lithuania (Lietuva)",
39171         "lt",
39172         "370"
39173       ],
39174       [
39175         "Luxembourg",
39176         "lu",
39177         "352"
39178       ],
39179       [
39180         "Macau (澳門)",
39181         "mo",
39182         "853"
39183       ],
39184       [
39185         "Macedonia (FYROM) (Македонија)",
39186         "mk",
39187         "389"
39188       ],
39189       [
39190         "Madagascar (Madagasikara)",
39191         "mg",
39192         "261"
39193       ],
39194       [
39195         "Malawi",
39196         "mw",
39197         "265"
39198       ],
39199       [
39200         "Malaysia",
39201         "my",
39202         "60"
39203       ],
39204       [
39205         "Maldives",
39206         "mv",
39207         "960"
39208       ],
39209       [
39210         "Mali",
39211         "ml",
39212         "223"
39213       ],
39214       [
39215         "Malta",
39216         "mt",
39217         "356"
39218       ],
39219       [
39220         "Marshall Islands",
39221         "mh",
39222         "692"
39223       ],
39224       [
39225         "Martinique",
39226         "mq",
39227         "596"
39228       ],
39229       [
39230         "Mauritania (‫موريتانيا‬‎)",
39231         "mr",
39232         "222"
39233       ],
39234       [
39235         "Mauritius (Moris)",
39236         "mu",
39237         "230"
39238       ],
39239       [
39240         "Mayotte",
39241         "yt",
39242         "262",
39243         1
39244       ],
39245       [
39246         "Mexico (México)",
39247         "mx",
39248         "52"
39249       ],
39250       [
39251         "Micronesia",
39252         "fm",
39253         "691"
39254       ],
39255       [
39256         "Moldova (Republica Moldova)",
39257         "md",
39258         "373"
39259       ],
39260       [
39261         "Monaco",
39262         "mc",
39263         "377"
39264       ],
39265       [
39266         "Mongolia (Монгол)",
39267         "mn",
39268         "976"
39269       ],
39270       [
39271         "Montenegro (Crna Gora)",
39272         "me",
39273         "382"
39274       ],
39275       [
39276         "Montserrat",
39277         "ms",
39278         "1664"
39279       ],
39280       [
39281         "Morocco (‫المغرب‬‎)",
39282         "ma",
39283         "212",
39284         0
39285       ],
39286       [
39287         "Mozambique (Moçambique)",
39288         "mz",
39289         "258"
39290       ],
39291       [
39292         "Myanmar (Burma) (မြန်မာ)",
39293         "mm",
39294         "95"
39295       ],
39296       [
39297         "Namibia (Namibië)",
39298         "na",
39299         "264"
39300       ],
39301       [
39302         "Nauru",
39303         "nr",
39304         "674"
39305       ],
39306       [
39307         "Nepal (नेपाल)",
39308         "np",
39309         "977"
39310       ],
39311       [
39312         "Netherlands (Nederland)",
39313         "nl",
39314         "31"
39315       ],
39316       [
39317         "New Caledonia (Nouvelle-Calédonie)",
39318         "nc",
39319         "687"
39320       ],
39321       [
39322         "New Zealand",
39323         "nz",
39324         "64"
39325       ],
39326       [
39327         "Nicaragua",
39328         "ni",
39329         "505"
39330       ],
39331       [
39332         "Niger (Nijar)",
39333         "ne",
39334         "227"
39335       ],
39336       [
39337         "Nigeria",
39338         "ng",
39339         "234"
39340       ],
39341       [
39342         "Niue",
39343         "nu",
39344         "683"
39345       ],
39346       [
39347         "Norfolk Island",
39348         "nf",
39349         "672"
39350       ],
39351       [
39352         "North Korea (조선 민주주의 인민 공화국)",
39353         "kp",
39354         "850"
39355       ],
39356       [
39357         "Northern Mariana Islands",
39358         "mp",
39359         "1670"
39360       ],
39361       [
39362         "Norway (Norge)",
39363         "no",
39364         "47",
39365         0
39366       ],
39367       [
39368         "Oman (‫عُمان‬‎)",
39369         "om",
39370         "968"
39371       ],
39372       [
39373         "Pakistan (‫پاکستان‬‎)",
39374         "pk",
39375         "92"
39376       ],
39377       [
39378         "Palau",
39379         "pw",
39380         "680"
39381       ],
39382       [
39383         "Palestine (‫فلسطين‬‎)",
39384         "ps",
39385         "970"
39386       ],
39387       [
39388         "Panama (Panamá)",
39389         "pa",
39390         "507"
39391       ],
39392       [
39393         "Papua New Guinea",
39394         "pg",
39395         "675"
39396       ],
39397       [
39398         "Paraguay",
39399         "py",
39400         "595"
39401       ],
39402       [
39403         "Peru (Perú)",
39404         "pe",
39405         "51"
39406       ],
39407       [
39408         "Philippines",
39409         "ph",
39410         "63"
39411       ],
39412       [
39413         "Poland (Polska)",
39414         "pl",
39415         "48"
39416       ],
39417       [
39418         "Portugal",
39419         "pt",
39420         "351"
39421       ],
39422       [
39423         "Puerto Rico",
39424         "pr",
39425         "1",
39426         3,
39427         ["787", "939"]
39428       ],
39429       [
39430         "Qatar (‫قطر‬‎)",
39431         "qa",
39432         "974"
39433       ],
39434       [
39435         "Réunion (La Réunion)",
39436         "re",
39437         "262",
39438         0
39439       ],
39440       [
39441         "Romania (România)",
39442         "ro",
39443         "40"
39444       ],
39445       [
39446         "Russia (Россия)",
39447         "ru",
39448         "7",
39449         0
39450       ],
39451       [
39452         "Rwanda",
39453         "rw",
39454         "250"
39455       ],
39456       [
39457         "Saint Barthélemy",
39458         "bl",
39459         "590",
39460         1
39461       ],
39462       [
39463         "Saint Helena",
39464         "sh",
39465         "290"
39466       ],
39467       [
39468         "Saint Kitts and Nevis",
39469         "kn",
39470         "1869"
39471       ],
39472       [
39473         "Saint Lucia",
39474         "lc",
39475         "1758"
39476       ],
39477       [
39478         "Saint Martin (Saint-Martin (partie française))",
39479         "mf",
39480         "590",
39481         2
39482       ],
39483       [
39484         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39485         "pm",
39486         "508"
39487       ],
39488       [
39489         "Saint Vincent and the Grenadines",
39490         "vc",
39491         "1784"
39492       ],
39493       [
39494         "Samoa",
39495         "ws",
39496         "685"
39497       ],
39498       [
39499         "San Marino",
39500         "sm",
39501         "378"
39502       ],
39503       [
39504         "São Tomé and Príncipe (São Tomé e Príncipe)",
39505         "st",
39506         "239"
39507       ],
39508       [
39509         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39510         "sa",
39511         "966"
39512       ],
39513       [
39514         "Senegal (Sénégal)",
39515         "sn",
39516         "221"
39517       ],
39518       [
39519         "Serbia (Србија)",
39520         "rs",
39521         "381"
39522       ],
39523       [
39524         "Seychelles",
39525         "sc",
39526         "248"
39527       ],
39528       [
39529         "Sierra Leone",
39530         "sl",
39531         "232"
39532       ],
39533       [
39534         "Singapore",
39535         "sg",
39536         "65"
39537       ],
39538       [
39539         "Sint Maarten",
39540         "sx",
39541         "1721"
39542       ],
39543       [
39544         "Slovakia (Slovensko)",
39545         "sk",
39546         "421"
39547       ],
39548       [
39549         "Slovenia (Slovenija)",
39550         "si",
39551         "386"
39552       ],
39553       [
39554         "Solomon Islands",
39555         "sb",
39556         "677"
39557       ],
39558       [
39559         "Somalia (Soomaaliya)",
39560         "so",
39561         "252"
39562       ],
39563       [
39564         "South Africa",
39565         "za",
39566         "27"
39567       ],
39568       [
39569         "South Korea (대한민국)",
39570         "kr",
39571         "82"
39572       ],
39573       [
39574         "South Sudan (‫جنوب السودان‬‎)",
39575         "ss",
39576         "211"
39577       ],
39578       [
39579         "Spain (España)",
39580         "es",
39581         "34"
39582       ],
39583       [
39584         "Sri Lanka (ශ්‍රී ලංකාව)",
39585         "lk",
39586         "94"
39587       ],
39588       [
39589         "Sudan (‫السودان‬‎)",
39590         "sd",
39591         "249"
39592       ],
39593       [
39594         "Suriname",
39595         "sr",
39596         "597"
39597       ],
39598       [
39599         "Svalbard and Jan Mayen",
39600         "sj",
39601         "47",
39602         1
39603       ],
39604       [
39605         "Swaziland",
39606         "sz",
39607         "268"
39608       ],
39609       [
39610         "Sweden (Sverige)",
39611         "se",
39612         "46"
39613       ],
39614       [
39615         "Switzerland (Schweiz)",
39616         "ch",
39617         "41"
39618       ],
39619       [
39620         "Syria (‫سوريا‬‎)",
39621         "sy",
39622         "963"
39623       ],
39624       [
39625         "Taiwan (台灣)",
39626         "tw",
39627         "886"
39628       ],
39629       [
39630         "Tajikistan",
39631         "tj",
39632         "992"
39633       ],
39634       [
39635         "Tanzania",
39636         "tz",
39637         "255"
39638       ],
39639       [
39640         "Thailand (ไทย)",
39641         "th",
39642         "66"
39643       ],
39644       [
39645         "Timor-Leste",
39646         "tl",
39647         "670"
39648       ],
39649       [
39650         "Togo",
39651         "tg",
39652         "228"
39653       ],
39654       [
39655         "Tokelau",
39656         "tk",
39657         "690"
39658       ],
39659       [
39660         "Tonga",
39661         "to",
39662         "676"
39663       ],
39664       [
39665         "Trinidad and Tobago",
39666         "tt",
39667         "1868"
39668       ],
39669       [
39670         "Tunisia (‫تونس‬‎)",
39671         "tn",
39672         "216"
39673       ],
39674       [
39675         "Turkey (Türkiye)",
39676         "tr",
39677         "90"
39678       ],
39679       [
39680         "Turkmenistan",
39681         "tm",
39682         "993"
39683       ],
39684       [
39685         "Turks and Caicos Islands",
39686         "tc",
39687         "1649"
39688       ],
39689       [
39690         "Tuvalu",
39691         "tv",
39692         "688"
39693       ],
39694       [
39695         "U.S. Virgin Islands",
39696         "vi",
39697         "1340"
39698       ],
39699       [
39700         "Uganda",
39701         "ug",
39702         "256"
39703       ],
39704       [
39705         "Ukraine (Україна)",
39706         "ua",
39707         "380"
39708       ],
39709       [
39710         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39711         "ae",
39712         "971"
39713       ],
39714       [
39715         "United Kingdom",
39716         "gb",
39717         "44",
39718         0
39719       ],
39720       [
39721         "United States",
39722         "us",
39723         "1",
39724         0
39725       ],
39726       [
39727         "Uruguay",
39728         "uy",
39729         "598"
39730       ],
39731       [
39732         "Uzbekistan (Oʻzbekiston)",
39733         "uz",
39734         "998"
39735       ],
39736       [
39737         "Vanuatu",
39738         "vu",
39739         "678"
39740       ],
39741       [
39742         "Vatican City (Città del Vaticano)",
39743         "va",
39744         "39",
39745         1
39746       ],
39747       [
39748         "Venezuela",
39749         "ve",
39750         "58"
39751       ],
39752       [
39753         "Vietnam (Việt Nam)",
39754         "vn",
39755         "84"
39756       ],
39757       [
39758         "Wallis and Futuna (Wallis-et-Futuna)",
39759         "wf",
39760         "681"
39761       ],
39762       [
39763         "Western Sahara (‫الصحراء الغربية‬‎)",
39764         "eh",
39765         "212",
39766         1
39767       ],
39768       [
39769         "Yemen (‫اليمن‬‎)",
39770         "ye",
39771         "967"
39772       ],
39773       [
39774         "Zambia",
39775         "zm",
39776         "260"
39777       ],
39778       [
39779         "Zimbabwe",
39780         "zw",
39781         "263"
39782       ],
39783       [
39784         "Åland Islands",
39785         "ax",
39786         "358",
39787         1
39788       ]
39789   ];
39790   
39791   return d;
39792 }/**
39793 *    This script refer to:
39794 *    Title: International Telephone Input
39795 *    Author: Jack O'Connor
39796 *    Code version:  v12.1.12
39797 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39798 **/
39799
39800 /**
39801  * @class Roo.bootstrap.PhoneInput
39802  * @extends Roo.bootstrap.TriggerField
39803  * An input with International dial-code selection
39804  
39805  * @cfg {String} defaultDialCode default '+852'
39806  * @cfg {Array} preferedCountries default []
39807   
39808  * @constructor
39809  * Create a new PhoneInput.
39810  * @param {Object} config Configuration options
39811  */
39812
39813 Roo.bootstrap.PhoneInput = function(config) {
39814     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39815 };
39816
39817 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39818         
39819         listWidth: undefined,
39820         
39821         selectedClass: 'active',
39822         
39823         invalidClass : "has-warning",
39824         
39825         validClass: 'has-success',
39826         
39827         allowed: '0123456789',
39828         
39829         /**
39830          * @cfg {String} defaultDialCode The default dial code when initializing the input
39831          */
39832         defaultDialCode: '+852',
39833         
39834         /**
39835          * @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
39836          */
39837         preferedCountries: false,
39838         
39839         getAutoCreate : function()
39840         {
39841             var data = Roo.bootstrap.PhoneInputData();
39842             var align = this.labelAlign || this.parentLabelAlign();
39843             var id = Roo.id();
39844             
39845             this.allCountries = [];
39846             this.dialCodeMapping = [];
39847             
39848             for (var i = 0; i < data.length; i++) {
39849               var c = data[i];
39850               this.allCountries[i] = {
39851                 name: c[0],
39852                 iso2: c[1],
39853                 dialCode: c[2],
39854                 priority: c[3] || 0,
39855                 areaCodes: c[4] || null
39856               };
39857               this.dialCodeMapping[c[2]] = {
39858                   name: c[0],
39859                   iso2: c[1],
39860                   priority: c[3] || 0,
39861                   areaCodes: c[4] || null
39862               };
39863             }
39864             
39865             var cfg = {
39866                 cls: 'form-group',
39867                 cn: []
39868             };
39869             
39870             var input =  {
39871                 tag: 'input',
39872                 id : id,
39873                 cls : 'form-control tel-input',
39874                 autocomplete: 'new-password'
39875             };
39876             
39877             var hiddenInput = {
39878                 tag: 'input',
39879                 type: 'hidden',
39880                 cls: 'hidden-tel-input'
39881             };
39882             
39883             if (this.name) {
39884                 hiddenInput.name = this.name;
39885             }
39886             
39887             if (this.disabled) {
39888                 input.disabled = true;
39889             }
39890             
39891             var flag_container = {
39892                 tag: 'div',
39893                 cls: 'flag-box',
39894                 cn: [
39895                     {
39896                         tag: 'div',
39897                         cls: 'flag'
39898                     },
39899                     {
39900                         tag: 'div',
39901                         cls: 'caret'
39902                     }
39903                 ]
39904             };
39905             
39906             var box = {
39907                 tag: 'div',
39908                 cls: this.hasFeedback ? 'has-feedback' : '',
39909                 cn: [
39910                     hiddenInput,
39911                     input,
39912                     {
39913                         tag: 'input',
39914                         cls: 'dial-code-holder',
39915                         disabled: true
39916                     }
39917                 ]
39918             };
39919             
39920             var container = {
39921                 cls: 'roo-select2-container input-group',
39922                 cn: [
39923                     flag_container,
39924                     box
39925                 ]
39926             };
39927             
39928             if (this.fieldLabel.length) {
39929                 var indicator = {
39930                     tag: 'i',
39931                     tooltip: 'This field is required'
39932                 };
39933                 
39934                 var label = {
39935                     tag: 'label',
39936                     'for':  id,
39937                     cls: 'control-label',
39938                     cn: []
39939                 };
39940                 
39941                 var label_text = {
39942                     tag: 'span',
39943                     html: this.fieldLabel
39944                 };
39945                 
39946                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39947                 label.cn = [
39948                     indicator,
39949                     label_text
39950                 ];
39951                 
39952                 if(this.indicatorpos == 'right') {
39953                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39954                     label.cn = [
39955                         label_text,
39956                         indicator
39957                     ];
39958                 }
39959                 
39960                 if(align == 'left') {
39961                     container = {
39962                         tag: 'div',
39963                         cn: [
39964                             container
39965                         ]
39966                     };
39967                     
39968                     if(this.labelWidth > 12){
39969                         label.style = "width: " + this.labelWidth + 'px';
39970                     }
39971                     if(this.labelWidth < 13 && this.labelmd == 0){
39972                         this.labelmd = this.labelWidth;
39973                     }
39974                     if(this.labellg > 0){
39975                         label.cls += ' col-lg-' + this.labellg;
39976                         input.cls += ' col-lg-' + (12 - this.labellg);
39977                     }
39978                     if(this.labelmd > 0){
39979                         label.cls += ' col-md-' + this.labelmd;
39980                         container.cls += ' col-md-' + (12 - this.labelmd);
39981                     }
39982                     if(this.labelsm > 0){
39983                         label.cls += ' col-sm-' + this.labelsm;
39984                         container.cls += ' col-sm-' + (12 - this.labelsm);
39985                     }
39986                     if(this.labelxs > 0){
39987                         label.cls += ' col-xs-' + this.labelxs;
39988                         container.cls += ' col-xs-' + (12 - this.labelxs);
39989                     }
39990                 }
39991             }
39992             
39993             cfg.cn = [
39994                 label,
39995                 container
39996             ];
39997             
39998             var settings = this;
39999             
40000             ['xs','sm','md','lg'].map(function(size){
40001                 if (settings[size]) {
40002                     cfg.cls += ' col-' + size + '-' + settings[size];
40003                 }
40004             });
40005             
40006             this.store = new Roo.data.Store({
40007                 proxy : new Roo.data.MemoryProxy({}),
40008                 reader : new Roo.data.JsonReader({
40009                     fields : [
40010                         {
40011                             'name' : 'name',
40012                             'type' : 'string'
40013                         },
40014                         {
40015                             'name' : 'iso2',
40016                             'type' : 'string'
40017                         },
40018                         {
40019                             'name' : 'dialCode',
40020                             'type' : 'string'
40021                         },
40022                         {
40023                             'name' : 'priority',
40024                             'type' : 'string'
40025                         },
40026                         {
40027                             'name' : 'areaCodes',
40028                             'type' : 'string'
40029                         }
40030                     ]
40031                 })
40032             });
40033             
40034             if(!this.preferedCountries) {
40035                 this.preferedCountries = [
40036                     'hk',
40037                     'gb',
40038                     'us'
40039                 ];
40040             }
40041             
40042             var p = this.preferedCountries.reverse();
40043             
40044             if(p) {
40045                 for (var i = 0; i < p.length; i++) {
40046                     for (var j = 0; j < this.allCountries.length; j++) {
40047                         if(this.allCountries[j].iso2 == p[i]) {
40048                             var t = this.allCountries[j];
40049                             this.allCountries.splice(j,1);
40050                             this.allCountries.unshift(t);
40051                         }
40052                     } 
40053                 }
40054             }
40055             
40056             this.store.proxy.data = {
40057                 success: true,
40058                 data: this.allCountries
40059             };
40060             
40061             return cfg;
40062         },
40063         
40064         initEvents : function()
40065         {
40066             this.createList();
40067             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40068             
40069             this.indicator = this.indicatorEl();
40070             this.flag = this.flagEl();
40071             this.dialCodeHolder = this.dialCodeHolderEl();
40072             
40073             this.trigger = this.el.select('div.flag-box',true).first();
40074             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40075             
40076             var _this = this;
40077             
40078             (function(){
40079                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40080                 _this.list.setWidth(lw);
40081             }).defer(100);
40082             
40083             this.list.on('mouseover', this.onViewOver, this);
40084             this.list.on('mousemove', this.onViewMove, this);
40085             this.inputEl().on("keyup", this.onKeyUp, this);
40086             
40087             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40088
40089             this.view = new Roo.View(this.list, this.tpl, {
40090                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40091             });
40092             
40093             this.view.on('click', this.onViewClick, this);
40094             this.setValue(this.defaultDialCode);
40095         },
40096         
40097         onTriggerClick : function(e)
40098         {
40099             Roo.log('trigger click');
40100             if(this.disabled){
40101                 return;
40102             }
40103             
40104             if(this.isExpanded()){
40105                 this.collapse();
40106                 this.hasFocus = false;
40107             }else {
40108                 this.store.load({});
40109                 this.hasFocus = true;
40110                 this.expand();
40111             }
40112         },
40113         
40114         isExpanded : function()
40115         {
40116             return this.list.isVisible();
40117         },
40118         
40119         collapse : function()
40120         {
40121             if(!this.isExpanded()){
40122                 return;
40123             }
40124             this.list.hide();
40125             Roo.get(document).un('mousedown', this.collapseIf, this);
40126             Roo.get(document).un('mousewheel', this.collapseIf, this);
40127             this.fireEvent('collapse', this);
40128             this.validate();
40129         },
40130         
40131         expand : function()
40132         {
40133             Roo.log('expand');
40134
40135             if(this.isExpanded() || !this.hasFocus){
40136                 return;
40137             }
40138             
40139             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40140             this.list.setWidth(lw);
40141             
40142             this.list.show();
40143             this.restrictHeight();
40144             
40145             Roo.get(document).on('mousedown', this.collapseIf, this);
40146             Roo.get(document).on('mousewheel', this.collapseIf, this);
40147             
40148             this.fireEvent('expand', this);
40149         },
40150         
40151         restrictHeight : function()
40152         {
40153             this.list.alignTo(this.inputEl(), this.listAlign);
40154             this.list.alignTo(this.inputEl(), this.listAlign);
40155         },
40156         
40157         onViewOver : function(e, t)
40158         {
40159             if(this.inKeyMode){
40160                 return;
40161             }
40162             var item = this.view.findItemFromChild(t);
40163             
40164             if(item){
40165                 var index = this.view.indexOf(item);
40166                 this.select(index, false);
40167             }
40168         },
40169
40170         // private
40171         onViewClick : function(view, doFocus, el, e)
40172         {
40173             var index = this.view.getSelectedIndexes()[0];
40174             
40175             var r = this.store.getAt(index);
40176             
40177             if(r){
40178                 this.onSelect(r, index);
40179             }
40180             if(doFocus !== false && !this.blockFocus){
40181                 this.inputEl().focus();
40182             }
40183         },
40184         
40185         onViewMove : function(e, t)
40186         {
40187             this.inKeyMode = false;
40188         },
40189         
40190         select : function(index, scrollIntoView)
40191         {
40192             this.selectedIndex = index;
40193             this.view.select(index);
40194             if(scrollIntoView !== false){
40195                 var el = this.view.getNode(index);
40196                 if(el){
40197                     this.list.scrollChildIntoView(el, false);
40198                 }
40199             }
40200         },
40201         
40202         createList : function()
40203         {
40204             this.list = Roo.get(document.body).createChild({
40205                 tag: 'ul',
40206                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40207                 style: 'display:none'
40208             });
40209             
40210             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40211         },
40212         
40213         collapseIf : function(e)
40214         {
40215             var in_combo  = e.within(this.el);
40216             var in_list =  e.within(this.list);
40217             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40218             
40219             if (in_combo || in_list || is_list) {
40220                 return;
40221             }
40222             this.collapse();
40223         },
40224         
40225         onSelect : function(record, index)
40226         {
40227             if(this.fireEvent('beforeselect', this, record, index) !== false){
40228                 
40229                 this.setFlagClass(record.data.iso2);
40230                 this.setDialCode(record.data.dialCode);
40231                 this.hasFocus = false;
40232                 this.collapse();
40233                 this.fireEvent('select', this, record, index);
40234             }
40235         },
40236         
40237         flagEl : function()
40238         {
40239             var flag = this.el.select('div.flag',true).first();
40240             if(!flag){
40241                 return false;
40242             }
40243             return flag;
40244         },
40245         
40246         dialCodeHolderEl : function()
40247         {
40248             var d = this.el.select('input.dial-code-holder',true).first();
40249             if(!d){
40250                 return false;
40251             }
40252             return d;
40253         },
40254         
40255         setDialCode : function(v)
40256         {
40257             this.dialCodeHolder.dom.value = '+'+v;
40258         },
40259         
40260         setFlagClass : function(n)
40261         {
40262             this.flag.dom.className = 'flag '+n;
40263         },
40264         
40265         getValue : function()
40266         {
40267             var v = this.inputEl().getValue();
40268             if(this.dialCodeHolder) {
40269                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40270             }
40271             return v;
40272         },
40273         
40274         setValue : function(v)
40275         {
40276             var d = this.getDialCode(v);
40277             
40278             //invalid dial code
40279             if(v.length == 0 || !d || d.length == 0) {
40280                 if(this.rendered){
40281                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40282                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40283                 }
40284                 return;
40285             }
40286             
40287             //valid dial code
40288             this.setFlagClass(this.dialCodeMapping[d].iso2);
40289             this.setDialCode(d);
40290             this.inputEl().dom.value = v.replace('+'+d,'');
40291             this.hiddenEl().dom.value = this.getValue();
40292             
40293             this.validate();
40294         },
40295         
40296         getDialCode : function(v)
40297         {
40298             v = v ||  '';
40299             
40300             if (v.length == 0) {
40301                 return this.dialCodeHolder.dom.value;
40302             }
40303             
40304             var dialCode = "";
40305             if (v.charAt(0) != "+") {
40306                 return false;
40307             }
40308             var numericChars = "";
40309             for (var i = 1; i < v.length; i++) {
40310               var c = v.charAt(i);
40311               if (!isNaN(c)) {
40312                 numericChars += c;
40313                 if (this.dialCodeMapping[numericChars]) {
40314                   dialCode = v.substr(1, i);
40315                 }
40316                 if (numericChars.length == 4) {
40317                   break;
40318                 }
40319               }
40320             }
40321             return dialCode;
40322         },
40323         
40324         reset : function()
40325         {
40326             this.setValue(this.defaultDialCode);
40327             this.validate();
40328         },
40329         
40330         hiddenEl : function()
40331         {
40332             return this.el.select('input.hidden-tel-input',true).first();
40333         },
40334         
40335         onKeyUp : function(e){
40336             
40337             var k = e.getKey();
40338             var c = e.getCharCode();
40339             
40340             if(
40341                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40342                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40343             ){
40344                 e.stopEvent();
40345             }
40346             
40347             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40348             //     return;
40349             // }
40350             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40351                 e.stopEvent();
40352             }
40353             
40354             this.setValue(this.getValue());
40355         }
40356         
40357 });
40358 /**
40359  * @class Roo.bootstrap.MoneyField
40360  * @extends Roo.bootstrap.ComboBox
40361  * Bootstrap MoneyField class
40362  * 
40363  * @constructor
40364  * Create a new MoneyField.
40365  * @param {Object} config Configuration options
40366  */
40367
40368 Roo.bootstrap.MoneyField = function(config) {
40369     
40370     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40371     
40372 };
40373
40374 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40375     
40376     /**
40377      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40378      */
40379     allowDecimals : true,
40380     /**
40381      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40382      */
40383     decimalSeparator : ".",
40384     /**
40385      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40386      */
40387     decimalPrecision : 0,
40388     /**
40389      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40390      */
40391     allowNegative : true,
40392     /**
40393      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40394      */
40395     allowZero: true,
40396     /**
40397      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40398      */
40399     minValue : Number.NEGATIVE_INFINITY,
40400     /**
40401      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40402      */
40403     maxValue : Number.MAX_VALUE,
40404     /**
40405      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40406      */
40407     minText : "The minimum value for this field is {0}",
40408     /**
40409      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40410      */
40411     maxText : "The maximum value for this field is {0}",
40412     /**
40413      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40414      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40415      */
40416     nanText : "{0} is not a valid number",
40417     /**
40418      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40419      */
40420     castInt : true,
40421     /**
40422      * @cfg {String} defaults currency of the MoneyField
40423      * value should be in lkey
40424      */
40425     defaultCurrency : false,
40426     /**
40427      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40428      */
40429     thousandsDelimiter : false,
40430     
40431     
40432     inputlg : 9,
40433     inputmd : 9,
40434     inputsm : 9,
40435     inputxs : 6,
40436     
40437     store : false,
40438     
40439     getAutoCreate : function()
40440     {
40441         var align = this.labelAlign || this.parentLabelAlign();
40442         
40443         var id = Roo.id();
40444
40445         var cfg = {
40446             cls: 'form-group',
40447             cn: []
40448         };
40449
40450         var input =  {
40451             tag: 'input',
40452             id : id,
40453             cls : 'form-control roo-money-amount-input',
40454             autocomplete: 'new-password'
40455         };
40456         
40457         var hiddenInput = {
40458             tag: 'input',
40459             type: 'hidden',
40460             id: Roo.id(),
40461             cls: 'hidden-number-input'
40462         };
40463         
40464         if (this.name) {
40465             hiddenInput.name = this.name;
40466         }
40467
40468         if (this.disabled) {
40469             input.disabled = true;
40470         }
40471
40472         var clg = 12 - this.inputlg;
40473         var cmd = 12 - this.inputmd;
40474         var csm = 12 - this.inputsm;
40475         var cxs = 12 - this.inputxs;
40476         
40477         var container = {
40478             tag : 'div',
40479             cls : 'row roo-money-field',
40480             cn : [
40481                 {
40482                     tag : 'div',
40483                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40484                     cn : [
40485                         {
40486                             tag : 'div',
40487                             cls: 'roo-select2-container input-group',
40488                             cn: [
40489                                 {
40490                                     tag : 'input',
40491                                     cls : 'form-control roo-money-currency-input',
40492                                     autocomplete: 'new-password',
40493                                     readOnly : 1,
40494                                     name : this.currencyName
40495                                 },
40496                                 {
40497                                     tag :'span',
40498                                     cls : 'input-group-addon',
40499                                     cn : [
40500                                         {
40501                                             tag: 'span',
40502                                             cls: 'caret'
40503                                         }
40504                                     ]
40505                                 }
40506                             ]
40507                         }
40508                     ]
40509                 },
40510                 {
40511                     tag : 'div',
40512                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40513                     cn : [
40514                         {
40515                             tag: 'div',
40516                             cls: this.hasFeedback ? 'has-feedback' : '',
40517                             cn: [
40518                                 input
40519                             ]
40520                         }
40521                     ]
40522                 }
40523             ]
40524             
40525         };
40526         
40527         if (this.fieldLabel.length) {
40528             var indicator = {
40529                 tag: 'i',
40530                 tooltip: 'This field is required'
40531             };
40532
40533             var label = {
40534                 tag: 'label',
40535                 'for':  id,
40536                 cls: 'control-label',
40537                 cn: []
40538             };
40539
40540             var label_text = {
40541                 tag: 'span',
40542                 html: this.fieldLabel
40543             };
40544
40545             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40546             label.cn = [
40547                 indicator,
40548                 label_text
40549             ];
40550
40551             if(this.indicatorpos == 'right') {
40552                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40553                 label.cn = [
40554                     label_text,
40555                     indicator
40556                 ];
40557             }
40558
40559             if(align == 'left') {
40560                 container = {
40561                     tag: 'div',
40562                     cn: [
40563                         container
40564                     ]
40565                 };
40566
40567                 if(this.labelWidth > 12){
40568                     label.style = "width: " + this.labelWidth + 'px';
40569                 }
40570                 if(this.labelWidth < 13 && this.labelmd == 0){
40571                     this.labelmd = this.labelWidth;
40572                 }
40573                 if(this.labellg > 0){
40574                     label.cls += ' col-lg-' + this.labellg;
40575                     input.cls += ' col-lg-' + (12 - this.labellg);
40576                 }
40577                 if(this.labelmd > 0){
40578                     label.cls += ' col-md-' + this.labelmd;
40579                     container.cls += ' col-md-' + (12 - this.labelmd);
40580                 }
40581                 if(this.labelsm > 0){
40582                     label.cls += ' col-sm-' + this.labelsm;
40583                     container.cls += ' col-sm-' + (12 - this.labelsm);
40584                 }
40585                 if(this.labelxs > 0){
40586                     label.cls += ' col-xs-' + this.labelxs;
40587                     container.cls += ' col-xs-' + (12 - this.labelxs);
40588                 }
40589             }
40590         }
40591
40592         cfg.cn = [
40593             label,
40594             container,
40595             hiddenInput
40596         ];
40597         
40598         var settings = this;
40599
40600         ['xs','sm','md','lg'].map(function(size){
40601             if (settings[size]) {
40602                 cfg.cls += ' col-' + size + '-' + settings[size];
40603             }
40604         });
40605         
40606         return cfg;
40607     },
40608     
40609     initEvents : function()
40610     {
40611         this.indicator = this.indicatorEl();
40612         
40613         this.initCurrencyEvent();
40614         
40615         this.initNumberEvent();
40616     },
40617     
40618     initCurrencyEvent : function()
40619     {
40620         if (!this.store) {
40621             throw "can not find store for combo";
40622         }
40623         
40624         this.store = Roo.factory(this.store, Roo.data);
40625         this.store.parent = this;
40626         
40627         this.createList();
40628         
40629         this.triggerEl = this.el.select('.input-group-addon', true).first();
40630         
40631         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40632         
40633         var _this = this;
40634         
40635         (function(){
40636             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40637             _this.list.setWidth(lw);
40638         }).defer(100);
40639         
40640         this.list.on('mouseover', this.onViewOver, this);
40641         this.list.on('mousemove', this.onViewMove, this);
40642         this.list.on('scroll', this.onViewScroll, this);
40643         
40644         if(!this.tpl){
40645             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40646         }
40647         
40648         this.view = new Roo.View(this.list, this.tpl, {
40649             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40650         });
40651         
40652         this.view.on('click', this.onViewClick, this);
40653         
40654         this.store.on('beforeload', this.onBeforeLoad, this);
40655         this.store.on('load', this.onLoad, this);
40656         this.store.on('loadexception', this.onLoadException, this);
40657         
40658         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40659             "up" : function(e){
40660                 this.inKeyMode = true;
40661                 this.selectPrev();
40662             },
40663
40664             "down" : function(e){
40665                 if(!this.isExpanded()){
40666                     this.onTriggerClick();
40667                 }else{
40668                     this.inKeyMode = true;
40669                     this.selectNext();
40670                 }
40671             },
40672
40673             "enter" : function(e){
40674                 this.collapse();
40675                 
40676                 if(this.fireEvent("specialkey", this, e)){
40677                     this.onViewClick(false);
40678                 }
40679                 
40680                 return true;
40681             },
40682
40683             "esc" : function(e){
40684                 this.collapse();
40685             },
40686
40687             "tab" : function(e){
40688                 this.collapse();
40689                 
40690                 if(this.fireEvent("specialkey", this, e)){
40691                     this.onViewClick(false);
40692                 }
40693                 
40694                 return true;
40695             },
40696
40697             scope : this,
40698
40699             doRelay : function(foo, bar, hname){
40700                 if(hname == 'down' || this.scope.isExpanded()){
40701                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40702                 }
40703                 return true;
40704             },
40705
40706             forceKeyDown: true
40707         });
40708         
40709         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40710         
40711     },
40712     
40713     initNumberEvent : function(e)
40714     {
40715         this.inputEl().on("keydown" , this.fireKey,  this);
40716         this.inputEl().on("focus", this.onFocus,  this);
40717         this.inputEl().on("blur", this.onBlur,  this);
40718         
40719         this.inputEl().relayEvent('keyup', this);
40720         
40721         if(this.indicator){
40722             this.indicator.addClass('invisible');
40723         }
40724  
40725         this.originalValue = this.getValue();
40726         
40727         if(this.validationEvent == 'keyup'){
40728             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40729             this.inputEl().on('keyup', this.filterValidation, this);
40730         }
40731         else if(this.validationEvent !== false){
40732             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40733         }
40734         
40735         if(this.selectOnFocus){
40736             this.on("focus", this.preFocus, this);
40737             
40738         }
40739         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40740             this.inputEl().on("keypress", this.filterKeys, this);
40741         } else {
40742             this.inputEl().relayEvent('keypress', this);
40743         }
40744         
40745         var allowed = "0123456789";
40746         
40747         if(this.allowDecimals){
40748             allowed += this.decimalSeparator;
40749         }
40750         
40751         if(this.allowNegative){
40752             allowed += "-";
40753         }
40754         
40755         if(this.thousandsDelimiter) {
40756             allowed += ",";
40757         }
40758         
40759         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40760         
40761         var keyPress = function(e){
40762             
40763             var k = e.getKey();
40764             
40765             var c = e.getCharCode();
40766             
40767             if(
40768                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40769                     allowed.indexOf(String.fromCharCode(c)) === -1
40770             ){
40771                 e.stopEvent();
40772                 return;
40773             }
40774             
40775             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40776                 return;
40777             }
40778             
40779             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40780                 e.stopEvent();
40781             }
40782         };
40783         
40784         this.inputEl().on("keypress", keyPress, this);
40785         
40786     },
40787     
40788     onTriggerClick : function(e)
40789     {   
40790         if(this.disabled){
40791             return;
40792         }
40793         
40794         this.page = 0;
40795         this.loadNext = false;
40796         
40797         if(this.isExpanded()){
40798             this.collapse();
40799             return;
40800         }
40801         
40802         this.hasFocus = true;
40803         
40804         if(this.triggerAction == 'all') {
40805             this.doQuery(this.allQuery, true);
40806             return;
40807         }
40808         
40809         this.doQuery(this.getRawValue());
40810     },
40811     
40812     getCurrency : function()
40813     {   
40814         var v = this.currencyEl().getValue();
40815         
40816         return v;
40817     },
40818     
40819     restrictHeight : function()
40820     {
40821         this.list.alignTo(this.currencyEl(), this.listAlign);
40822         this.list.alignTo(this.currencyEl(), this.listAlign);
40823     },
40824     
40825     onViewClick : function(view, doFocus, el, e)
40826     {
40827         var index = this.view.getSelectedIndexes()[0];
40828         
40829         var r = this.store.getAt(index);
40830         
40831         if(r){
40832             this.onSelect(r, index);
40833         }
40834     },
40835     
40836     onSelect : function(record, index){
40837         
40838         if(this.fireEvent('beforeselect', this, record, index) !== false){
40839         
40840             this.setFromCurrencyData(index > -1 ? record.data : false);
40841             
40842             this.collapse();
40843             
40844             this.fireEvent('select', this, record, index);
40845         }
40846     },
40847     
40848     setFromCurrencyData : function(o)
40849     {
40850         var currency = '';
40851         
40852         this.lastCurrency = o;
40853         
40854         if (this.currencyField) {
40855             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40856         } else {
40857             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40858         }
40859         
40860         this.lastSelectionText = currency;
40861         
40862         //setting default currency
40863         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40864             this.setCurrency(this.defaultCurrency);
40865             return;
40866         }
40867         
40868         this.setCurrency(currency);
40869     },
40870     
40871     setFromData : function(o)
40872     {
40873         var c = {};
40874         
40875         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40876         
40877         this.setFromCurrencyData(c);
40878         
40879         var value = '';
40880         
40881         if (this.name) {
40882             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40883         } else {
40884             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40885         }
40886         
40887         this.setValue(value);
40888         
40889     },
40890     
40891     setCurrency : function(v)
40892     {   
40893         this.currencyValue = v;
40894         
40895         if(this.rendered){
40896             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40897             this.validate();
40898         }
40899     },
40900     
40901     setValue : function(v)
40902     {
40903         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40904         
40905         this.value = v;
40906         
40907         if(this.rendered){
40908             
40909             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40910             
40911             this.inputEl().dom.value = (v == '') ? '' :
40912                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40913             
40914             if(!this.allowZero && v === '0') {
40915                 this.hiddenEl().dom.value = '';
40916                 this.inputEl().dom.value = '';
40917             }
40918             
40919             this.validate();
40920         }
40921     },
40922     
40923     getRawValue : function()
40924     {
40925         var v = this.inputEl().getValue();
40926         
40927         return v;
40928     },
40929     
40930     getValue : function()
40931     {
40932         return this.fixPrecision(this.parseValue(this.getRawValue()));
40933     },
40934     
40935     parseValue : function(value)
40936     {
40937         if(this.thousandsDelimiter) {
40938             value += "";
40939             r = new RegExp(",", "g");
40940             value = value.replace(r, "");
40941         }
40942         
40943         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40944         return isNaN(value) ? '' : value;
40945         
40946     },
40947     
40948     fixPrecision : function(value)
40949     {
40950         if(this.thousandsDelimiter) {
40951             value += "";
40952             r = new RegExp(",", "g");
40953             value = value.replace(r, "");
40954         }
40955         
40956         var nan = isNaN(value);
40957         
40958         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40959             return nan ? '' : value;
40960         }
40961         return parseFloat(value).toFixed(this.decimalPrecision);
40962     },
40963     
40964     decimalPrecisionFcn : function(v)
40965     {
40966         return Math.floor(v);
40967     },
40968     
40969     validateValue : function(value)
40970     {
40971         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40972             return false;
40973         }
40974         
40975         var num = this.parseValue(value);
40976         
40977         if(isNaN(num)){
40978             this.markInvalid(String.format(this.nanText, value));
40979             return false;
40980         }
40981         
40982         if(num < this.minValue){
40983             this.markInvalid(String.format(this.minText, this.minValue));
40984             return false;
40985         }
40986         
40987         if(num > this.maxValue){
40988             this.markInvalid(String.format(this.maxText, this.maxValue));
40989             return false;
40990         }
40991         
40992         return true;
40993     },
40994     
40995     validate : function()
40996     {
40997         if(this.disabled || this.allowBlank){
40998             this.markValid();
40999             return true;
41000         }
41001         
41002         var currency = this.getCurrency();
41003         
41004         if(this.validateValue(this.getRawValue()) && currency.length){
41005             this.markValid();
41006             return true;
41007         }
41008         
41009         this.markInvalid();
41010         return false;
41011     },
41012     
41013     getName: function()
41014     {
41015         return this.name;
41016     },
41017     
41018     beforeBlur : function()
41019     {
41020         if(!this.castInt){
41021             return;
41022         }
41023         
41024         var v = this.parseValue(this.getRawValue());
41025         
41026         if(v || v == 0){
41027             this.setValue(v);
41028         }
41029     },
41030     
41031     onBlur : function()
41032     {
41033         this.beforeBlur();
41034         
41035         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41036             //this.el.removeClass(this.focusClass);
41037         }
41038         
41039         this.hasFocus = false;
41040         
41041         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41042             this.validate();
41043         }
41044         
41045         var v = this.getValue();
41046         
41047         if(String(v) !== String(this.startValue)){
41048             this.fireEvent('change', this, v, this.startValue);
41049         }
41050         
41051         this.fireEvent("blur", this);
41052     },
41053     
41054     inputEl : function()
41055     {
41056         return this.el.select('.roo-money-amount-input', true).first();
41057     },
41058     
41059     currencyEl : function()
41060     {
41061         return this.el.select('.roo-money-currency-input', true).first();
41062     },
41063     
41064     hiddenEl : function()
41065     {
41066         return this.el.select('input.hidden-number-input',true).first();
41067     }
41068     
41069 });