Roo/bootstrap/Modal.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(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2826         
2827         if (this.fitwindow) {
2828             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2829             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2830             this.setSize(w,h);
2831         }
2832         
2833         if(!this.fitwindow && this.max_width !== 0){
2834             
2835             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2836             
2837             // for fix height
2838             if(this.height) {
2839                 this.setSize(w, this.height);
2840                 return;
2841             }
2842             
2843             if(!this.fit_content) {
2844                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2845             }
2846             
2847             var body_childs = this.bodyEl.dom.childNodes;
2848             var full_height = this.headerEl.getHeight() + this.footerEl.getHeight();
2849             for(var i = 0; i < body_childs.length; i++) {
2850                 
2851                 // if(body_childs[i].classList.indexOf('roo-layout-region') * 1 != -1) {
2852                 //     var layout_childs = body_childs[i].childNodes;
2853                 //     for(var j = 0; j < layout_childs.length; j++) {
2854                 // 
2855                 //     }
2856                 // }
2857                 
2858                 full_height += body_childs[i].offsetHeight;
2859             }
2860             
2861             this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2862         }
2863         
2864     },
2865
2866     setSize : function(w,h)
2867     {
2868         if (!w && !h) {
2869             return;
2870         }
2871         this.resizeTo(w,h);
2872     },
2873
2874     show : function() {
2875
2876         if (!this.rendered) {
2877             this.render();
2878         }
2879
2880         //this.el.setStyle('display', 'block');
2881         this.el.removeClass('hideing');        
2882         this.el.addClass('show');
2883  
2884         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2885             var _this = this;
2886             (function(){
2887                 this.el.addClass('in');
2888             }).defer(50, this);
2889         }else{
2890             this.el.addClass('in');
2891         }
2892
2893         // not sure how we can show data in here..
2894         //if (this.tmpl) {
2895         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2896         //}
2897
2898         Roo.get(document.body).addClass("x-body-masked");
2899         
2900         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2901         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902         this.maskEl.addClass('show');
2903         
2904         this.resize();
2905         
2906         this.fireEvent('show', this);
2907
2908         // set zindex here - otherwise it appears to be ignored...
2909         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910
2911         (function () {
2912             this.items.forEach( function(e) {
2913                 e.layout ? e.layout() : false;
2914
2915             });
2916         }).defer(100,this);
2917
2918     },
2919     hide : function()
2920     {
2921         if(this.fireEvent("beforehide", this) !== false){
2922             this.maskEl.removeClass('show');
2923             Roo.get(document.body).removeClass("x-body-masked");
2924             this.el.removeClass('in');
2925             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2926
2927             if(this.animate){ // why
2928                 this.el.addClass('hideing');
2929                 (function(){
2930                     if (!this.el.hasClass('hideing')) {
2931                         return; // it's been shown again...
2932                     }
2933                     this.el.removeClass('show');
2934                     this.el.removeClass('hideing');
2935                 }).defer(150,this);
2936                 
2937             }else{
2938                  this.el.removeClass('show');
2939             }
2940             this.fireEvent('hide', this);
2941         }
2942     },
2943     isVisible : function()
2944     {
2945         
2946         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2947         
2948     },
2949
2950     addButton : function(str, cb)
2951     {
2952
2953
2954         var b = Roo.apply({}, { html : str } );
2955         b.xns = b.xns || Roo.bootstrap;
2956         b.xtype = b.xtype || 'Button';
2957         if (typeof(b.listeners) == 'undefined') {
2958             b.listeners = { click : cb.createDelegate(this)  };
2959         }
2960
2961         var btn = Roo.factory(b);
2962
2963         btn.render(this.el.select('.modal-footer div').first());
2964
2965         return btn;
2966
2967     },
2968
2969     setDefaultButton : function(btn)
2970     {
2971         //this.el.select('.modal-footer').()
2972     },
2973     diff : false,
2974
2975     resizeTo: function(w,h)
2976     {
2977         // skip.. ?? why??
2978
2979         this.dialogEl.setWidth(w);
2980         if (this.diff === false) {
2981             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2982         }
2983
2984         this.bodyEl.setHeight(h-this.diff);
2985
2986         this.fireEvent('resize', this);
2987
2988     },
2989     setContentSize  : function(w, h)
2990     {
2991
2992     },
2993     onButtonClick: function(btn,e)
2994     {
2995         //Roo.log([a,b,c]);
2996         this.fireEvent('btnclick', btn.name, e);
2997     },
2998      /**
2999      * Set the title of the Dialog
3000      * @param {String} str new Title
3001      */
3002     setTitle: function(str) {
3003         this.titleEl.dom.innerHTML = str;
3004     },
3005     /**
3006      * Set the body of the Dialog
3007      * @param {String} str new Title
3008      */
3009     setBody: function(str) {
3010         this.bodyEl.dom.innerHTML = str;
3011     },
3012     /**
3013      * Set the body of the Dialog using the template
3014      * @param {Obj} data - apply this data to the template and replace the body contents.
3015      */
3016     applyBody: function(obj)
3017     {
3018         if (!this.tmpl) {
3019             Roo.log("Error - using apply Body without a template");
3020             //code
3021         }
3022         this.tmpl.overwrite(this.bodyEl, obj);
3023     }
3024
3025 });
3026
3027
3028 Roo.apply(Roo.bootstrap.Modal,  {
3029     /**
3030          * Button config that displays a single OK button
3031          * @type Object
3032          */
3033         OK :  [{
3034             name : 'ok',
3035             weight : 'primary',
3036             html : 'OK'
3037         }],
3038         /**
3039          * Button config that displays Yes and No buttons
3040          * @type Object
3041          */
3042         YESNO : [
3043             {
3044                 name  : 'no',
3045                 html : 'No'
3046             },
3047             {
3048                 name  :'yes',
3049                 weight : 'primary',
3050                 html : 'Yes'
3051             }
3052         ],
3053
3054         /**
3055          * Button config that displays OK and Cancel buttons
3056          * @type Object
3057          */
3058         OKCANCEL : [
3059             {
3060                name : 'cancel',
3061                 html : 'Cancel'
3062             },
3063             {
3064                 name : 'ok',
3065                 weight : 'primary',
3066                 html : 'OK'
3067             }
3068         ],
3069         /**
3070          * Button config that displays Yes, No and Cancel buttons
3071          * @type Object
3072          */
3073         YESNOCANCEL : [
3074             {
3075                 name : 'yes',
3076                 weight : 'primary',
3077                 html : 'Yes'
3078             },
3079             {
3080                 name : 'no',
3081                 html : 'No'
3082             },
3083             {
3084                 name : 'cancel',
3085                 html : 'Cancel'
3086             }
3087         ],
3088         
3089         zIndex : 10001
3090 });
3091 /*
3092  * - LGPL
3093  *
3094  * messagebox - can be used as a replace
3095  * 
3096  */
3097 /**
3098  * @class Roo.MessageBox
3099  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3100  * Example usage:
3101  *<pre><code>
3102 // Basic alert:
3103 Roo.Msg.alert('Status', 'Changes saved successfully.');
3104
3105 // Prompt for user data:
3106 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3107     if (btn == 'ok'){
3108         // process text value...
3109     }
3110 });
3111
3112 // Show a dialog using config options:
3113 Roo.Msg.show({
3114    title:'Save Changes?',
3115    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3116    buttons: Roo.Msg.YESNOCANCEL,
3117    fn: processResult,
3118    animEl: 'elId'
3119 });
3120 </code></pre>
3121  * @singleton
3122  */
3123 Roo.bootstrap.MessageBox = function(){
3124     var dlg, opt, mask, waitTimer;
3125     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3126     var buttons, activeTextEl, bwidth;
3127
3128     
3129     // private
3130     var handleButton = function(button){
3131         dlg.hide();
3132         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3133     };
3134
3135     // private
3136     var handleHide = function(){
3137         if(opt && opt.cls){
3138             dlg.el.removeClass(opt.cls);
3139         }
3140         //if(waitTimer){
3141         //    Roo.TaskMgr.stop(waitTimer);
3142         //    waitTimer = null;
3143         //}
3144     };
3145
3146     // private
3147     var updateButtons = function(b){
3148         var width = 0;
3149         if(!b){
3150             buttons["ok"].hide();
3151             buttons["cancel"].hide();
3152             buttons["yes"].hide();
3153             buttons["no"].hide();
3154             //dlg.footer.dom.style.display = 'none';
3155             return width;
3156         }
3157         dlg.footerEl.dom.style.display = '';
3158         for(var k in buttons){
3159             if(typeof buttons[k] != "function"){
3160                 if(b[k]){
3161                     buttons[k].show();
3162                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3163                     width += buttons[k].el.getWidth()+15;
3164                 }else{
3165                     buttons[k].hide();
3166                 }
3167             }
3168         }
3169         return width;
3170     };
3171
3172     // private
3173     var handleEsc = function(d, k, e){
3174         if(opt && opt.closable !== false){
3175             dlg.hide();
3176         }
3177         if(e){
3178             e.stopEvent();
3179         }
3180     };
3181
3182     return {
3183         /**
3184          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3185          * @return {Roo.BasicDialog} The BasicDialog element
3186          */
3187         getDialog : function(){
3188            if(!dlg){
3189                 dlg = new Roo.bootstrap.Modal( {
3190                     //draggable: true,
3191                     //resizable:false,
3192                     //constraintoviewport:false,
3193                     //fixedcenter:true,
3194                     //collapsible : false,
3195                     //shim:true,
3196                     //modal: true,
3197                 //    width: 'auto',
3198                   //  height:100,
3199                     //buttonAlign:"center",
3200                     closeClick : function(){
3201                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3202                             handleButton("no");
3203                         }else{
3204                             handleButton("cancel");
3205                         }
3206                     }
3207                 });
3208                 dlg.render();
3209                 dlg.on("hide", handleHide);
3210                 mask = dlg.mask;
3211                 //dlg.addKeyListener(27, handleEsc);
3212                 buttons = {};
3213                 this.buttons = buttons;
3214                 var bt = this.buttonText;
3215                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3216                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3217                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3218                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3219                 //Roo.log(buttons);
3220                 bodyEl = dlg.bodyEl.createChild({
3221
3222                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3223                         '<textarea class="roo-mb-textarea"></textarea>' +
3224                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3225                 });
3226                 msgEl = bodyEl.dom.firstChild;
3227                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3228                 textboxEl.enableDisplayMode();
3229                 textboxEl.addKeyListener([10,13], function(){
3230                     if(dlg.isVisible() && opt && opt.buttons){
3231                         if(opt.buttons.ok){
3232                             handleButton("ok");
3233                         }else if(opt.buttons.yes){
3234                             handleButton("yes");
3235                         }
3236                     }
3237                 });
3238                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3239                 textareaEl.enableDisplayMode();
3240                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3241                 progressEl.enableDisplayMode();
3242                 
3243                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3244                 var pf = progressEl.dom.firstChild;
3245                 if (pf) {
3246                     pp = Roo.get(pf.firstChild);
3247                     pp.setHeight(pf.offsetHeight);
3248                 }
3249                 
3250             }
3251             return dlg;
3252         },
3253
3254         /**
3255          * Updates the message box body text
3256          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3257          * the XHTML-compliant non-breaking space character '&amp;#160;')
3258          * @return {Roo.MessageBox} This message box
3259          */
3260         updateText : function(text)
3261         {
3262             if(!dlg.isVisible() && !opt.width){
3263                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3264                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3265             }
3266             msgEl.innerHTML = text || '&#160;';
3267       
3268             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3269             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3270             var w = Math.max(
3271                     Math.min(opt.width || cw , this.maxWidth), 
3272                     Math.max(opt.minWidth || this.minWidth, bwidth)
3273             );
3274             if(opt.prompt){
3275                 activeTextEl.setWidth(w);
3276             }
3277             if(dlg.isVisible()){
3278                 dlg.fixedcenter = false;
3279             }
3280             // to big, make it scroll. = But as usual stupid IE does not support
3281             // !important..
3282             
3283             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3284                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3285                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3286             } else {
3287                 bodyEl.dom.style.height = '';
3288                 bodyEl.dom.style.overflowY = '';
3289             }
3290             if (cw > w) {
3291                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3292             } else {
3293                 bodyEl.dom.style.overflowX = '';
3294             }
3295             
3296             dlg.setContentSize(w, bodyEl.getHeight());
3297             if(dlg.isVisible()){
3298                 dlg.fixedcenter = true;
3299             }
3300             return this;
3301         },
3302
3303         /**
3304          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3305          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3306          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3307          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3308          * @return {Roo.MessageBox} This message box
3309          */
3310         updateProgress : function(value, text){
3311             if(text){
3312                 this.updateText(text);
3313             }
3314             
3315             if (pp) { // weird bug on my firefox - for some reason this is not defined
3316                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3317                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3318             }
3319             return this;
3320         },        
3321
3322         /**
3323          * Returns true if the message box is currently displayed
3324          * @return {Boolean} True if the message box is visible, else false
3325          */
3326         isVisible : function(){
3327             return dlg && dlg.isVisible();  
3328         },
3329
3330         /**
3331          * Hides the message box if it is displayed
3332          */
3333         hide : function(){
3334             if(this.isVisible()){
3335                 dlg.hide();
3336             }  
3337         },
3338
3339         /**
3340          * Displays a new message box, or reinitializes an existing message box, based on the config options
3341          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3342          * The following config object properties are supported:
3343          * <pre>
3344 Property    Type             Description
3345 ----------  ---------------  ------------------------------------------------------------------------------------
3346 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3347                                    closes (defaults to undefined)
3348 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3349                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3350 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3351                                    progress and wait dialogs will ignore this property and always hide the
3352                                    close button as they can only be closed programmatically.
3353 cls               String           A custom CSS class to apply to the message box element
3354 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3355                                    displayed (defaults to 75)
3356 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3357                                    function will be btn (the name of the button that was clicked, if applicable,
3358                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3359                                    Progress and wait dialogs will ignore this option since they do not respond to
3360                                    user actions and can only be closed programmatically, so any required function
3361                                    should be called by the same code after it closes the dialog.
3362 icon              String           A CSS class that provides a background image to be used as an icon for
3363                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3364 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3365 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3366 modal             Boolean          False to allow user interaction with the page while the message box is
3367                                    displayed (defaults to true)
3368 msg               String           A string that will replace the existing message box body text (defaults
3369                                    to the XHTML-compliant non-breaking space character '&#160;')
3370 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3371 progress          Boolean          True to display a progress bar (defaults to false)
3372 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3373 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3374 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3375 title             String           The title text
3376 value             String           The string value to set into the active textbox element if displayed
3377 wait              Boolean          True to display a progress bar (defaults to false)
3378 width             Number           The width of the dialog in pixels
3379 </pre>
3380          *
3381          * Example usage:
3382          * <pre><code>
3383 Roo.Msg.show({
3384    title: 'Address',
3385    msg: 'Please enter your address:',
3386    width: 300,
3387    buttons: Roo.MessageBox.OKCANCEL,
3388    multiline: true,
3389    fn: saveAddress,
3390    animEl: 'addAddressBtn'
3391 });
3392 </code></pre>
3393          * @param {Object} config Configuration options
3394          * @return {Roo.MessageBox} This message box
3395          */
3396         show : function(options)
3397         {
3398             
3399             // this causes nightmares if you show one dialog after another
3400             // especially on callbacks..
3401              
3402             if(this.isVisible()){
3403                 
3404                 this.hide();
3405                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3406                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3407                 Roo.log("New Dialog Message:" +  options.msg )
3408                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3409                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3410                 
3411             }
3412             var d = this.getDialog();
3413             opt = options;
3414             d.setTitle(opt.title || "&#160;");
3415             d.closeEl.setDisplayed(opt.closable !== false);
3416             activeTextEl = textboxEl;
3417             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3418             if(opt.prompt){
3419                 if(opt.multiline){
3420                     textboxEl.hide();
3421                     textareaEl.show();
3422                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3423                         opt.multiline : this.defaultTextHeight);
3424                     activeTextEl = textareaEl;
3425                 }else{
3426                     textboxEl.show();
3427                     textareaEl.hide();
3428                 }
3429             }else{
3430                 textboxEl.hide();
3431                 textareaEl.hide();
3432             }
3433             progressEl.setDisplayed(opt.progress === true);
3434             this.updateProgress(0);
3435             activeTextEl.dom.value = opt.value || "";
3436             if(opt.prompt){
3437                 dlg.setDefaultButton(activeTextEl);
3438             }else{
3439                 var bs = opt.buttons;
3440                 var db = null;
3441                 if(bs && bs.ok){
3442                     db = buttons["ok"];
3443                 }else if(bs && bs.yes){
3444                     db = buttons["yes"];
3445                 }
3446                 dlg.setDefaultButton(db);
3447             }
3448             bwidth = updateButtons(opt.buttons);
3449             this.updateText(opt.msg);
3450             if(opt.cls){
3451                 d.el.addClass(opt.cls);
3452             }
3453             d.proxyDrag = opt.proxyDrag === true;
3454             d.modal = opt.modal !== false;
3455             d.mask = opt.modal !== false ? mask : false;
3456             if(!d.isVisible()){
3457                 // force it to the end of the z-index stack so it gets a cursor in FF
3458                 document.body.appendChild(dlg.el.dom);
3459                 d.animateTarget = null;
3460                 d.show(options.animEl);
3461             }
3462             return this;
3463         },
3464
3465         /**
3466          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3467          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3468          * and closing the message box when the process is complete.
3469          * @param {String} title The title bar text
3470          * @param {String} msg The message box body text
3471          * @return {Roo.MessageBox} This message box
3472          */
3473         progress : function(title, msg){
3474             this.show({
3475                 title : title,
3476                 msg : msg,
3477                 buttons: false,
3478                 progress:true,
3479                 closable:false,
3480                 minWidth: this.minProgressWidth,
3481                 modal : true
3482             });
3483             return this;
3484         },
3485
3486         /**
3487          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3488          * If a callback function is passed it will be called after the user clicks the button, and the
3489          * id of the button that was clicked will be passed as the only parameter to the callback
3490          * (could also be the top-right close button).
3491          * @param {String} title The title bar text
3492          * @param {String} msg The message box body text
3493          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3494          * @param {Object} scope (optional) The scope of the callback function
3495          * @return {Roo.MessageBox} This message box
3496          */
3497         alert : function(title, msg, fn, scope)
3498         {
3499             this.show({
3500                 title : title,
3501                 msg : msg,
3502                 buttons: this.OK,
3503                 fn: fn,
3504                 closable : false,
3505                 scope : scope,
3506                 modal : true
3507             });
3508             return this;
3509         },
3510
3511         /**
3512          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3513          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3514          * You are responsible for closing the message box when the process is complete.
3515          * @param {String} msg The message box body text
3516          * @param {String} title (optional) The title bar text
3517          * @return {Roo.MessageBox} This message box
3518          */
3519         wait : function(msg, title){
3520             this.show({
3521                 title : title,
3522                 msg : msg,
3523                 buttons: false,
3524                 closable:false,
3525                 progress:true,
3526                 modal:true,
3527                 width:300,
3528                 wait:true
3529             });
3530             waitTimer = Roo.TaskMgr.start({
3531                 run: function(i){
3532                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3533                 },
3534                 interval: 1000
3535             });
3536             return this;
3537         },
3538
3539         /**
3540          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3541          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3542          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3543          * @param {String} title The title bar text
3544          * @param {String} msg The message box body text
3545          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3546          * @param {Object} scope (optional) The scope of the callback function
3547          * @return {Roo.MessageBox} This message box
3548          */
3549         confirm : function(title, msg, fn, scope){
3550             this.show({
3551                 title : title,
3552                 msg : msg,
3553                 buttons: this.YESNO,
3554                 fn: fn,
3555                 scope : scope,
3556                 modal : true
3557             });
3558             return this;
3559         },
3560
3561         /**
3562          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3563          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3564          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3565          * (could also be the top-right close button) and the text that was entered will be passed as the two
3566          * parameters to the callback.
3567          * @param {String} title The title bar text
3568          * @param {String} msg The message box body text
3569          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3570          * @param {Object} scope (optional) The scope of the callback function
3571          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3572          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3573          * @return {Roo.MessageBox} This message box
3574          */
3575         prompt : function(title, msg, fn, scope, multiline){
3576             this.show({
3577                 title : title,
3578                 msg : msg,
3579                 buttons: this.OKCANCEL,
3580                 fn: fn,
3581                 minWidth:250,
3582                 scope : scope,
3583                 prompt:true,
3584                 multiline: multiline,
3585                 modal : true
3586             });
3587             return this;
3588         },
3589
3590         /**
3591          * Button config that displays a single OK button
3592          * @type Object
3593          */
3594         OK : {ok:true},
3595         /**
3596          * Button config that displays Yes and No buttons
3597          * @type Object
3598          */
3599         YESNO : {yes:true, no:true},
3600         /**
3601          * Button config that displays OK and Cancel buttons
3602          * @type Object
3603          */
3604         OKCANCEL : {ok:true, cancel:true},
3605         /**
3606          * Button config that displays Yes, No and Cancel buttons
3607          * @type Object
3608          */
3609         YESNOCANCEL : {yes:true, no:true, cancel:true},
3610
3611         /**
3612          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3613          * @type Number
3614          */
3615         defaultTextHeight : 75,
3616         /**
3617          * The maximum width in pixels of the message box (defaults to 600)
3618          * @type Number
3619          */
3620         maxWidth : 600,
3621         /**
3622          * The minimum width in pixels of the message box (defaults to 100)
3623          * @type Number
3624          */
3625         minWidth : 100,
3626         /**
3627          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3628          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3629          * @type Number
3630          */
3631         minProgressWidth : 250,
3632         /**
3633          * An object containing the default button text strings that can be overriden for localized language support.
3634          * Supported properties are: ok, cancel, yes and no.
3635          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3636          * @type Object
3637          */
3638         buttonText : {
3639             ok : "OK",
3640             cancel : "Cancel",
3641             yes : "Yes",
3642             no : "No"
3643         }
3644     };
3645 }();
3646
3647 /**
3648  * Shorthand for {@link Roo.MessageBox}
3649  */
3650 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3651 Roo.Msg = Roo.Msg || Roo.MessageBox;
3652 /*
3653  * - LGPL
3654  *
3655  * navbar
3656  * 
3657  */
3658
3659 /**
3660  * @class Roo.bootstrap.Navbar
3661  * @extends Roo.bootstrap.Component
3662  * Bootstrap Navbar class
3663
3664  * @constructor
3665  * Create a new Navbar
3666  * @param {Object} config The config object
3667  */
3668
3669
3670 Roo.bootstrap.Navbar = function(config){
3671     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3672     this.addEvents({
3673         // raw events
3674         /**
3675          * @event beforetoggle
3676          * Fire before toggle the menu
3677          * @param {Roo.EventObject} e
3678          */
3679         "beforetoggle" : true
3680     });
3681 };
3682
3683 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3684     
3685     
3686    
3687     // private
3688     navItems : false,
3689     loadMask : false,
3690     
3691     
3692     getAutoCreate : function(){
3693         
3694         
3695         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3696         
3697     },
3698     
3699     initEvents :function ()
3700     {
3701         //Roo.log(this.el.select('.navbar-toggle',true));
3702         this.el.select('.navbar-toggle',true).on('click', function() {
3703             if(this.fireEvent('beforetoggle', this) !== false){
3704                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3705             }
3706             
3707         }, this);
3708         
3709         var mark = {
3710             tag: "div",
3711             cls:"x-dlg-mask"
3712         };
3713         
3714         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3715         
3716         var size = this.el.getSize();
3717         this.maskEl.setSize(size.width, size.height);
3718         this.maskEl.enableDisplayMode("block");
3719         this.maskEl.hide();
3720         
3721         if(this.loadMask){
3722             this.maskEl.show();
3723         }
3724     },
3725     
3726     
3727     getChildContainer : function()
3728     {
3729         if (this.el.select('.collapse').getCount()) {
3730             return this.el.select('.collapse',true).first();
3731         }
3732         
3733         return this.el;
3734     },
3735     
3736     mask : function()
3737     {
3738         this.maskEl.show();
3739     },
3740     
3741     unmask : function()
3742     {
3743         this.maskEl.hide();
3744     } 
3745     
3746     
3747     
3748     
3749 });
3750
3751
3752
3753  
3754
3755  /*
3756  * - LGPL
3757  *
3758  * navbar
3759  * 
3760  */
3761
3762 /**
3763  * @class Roo.bootstrap.NavSimplebar
3764  * @extends Roo.bootstrap.Navbar
3765  * Bootstrap Sidebar class
3766  *
3767  * @cfg {Boolean} inverse is inverted color
3768  * 
3769  * @cfg {String} type (nav | pills | tabs)
3770  * @cfg {Boolean} arrangement stacked | justified
3771  * @cfg {String} align (left | right) alignment
3772  * 
3773  * @cfg {Boolean} main (true|false) main nav bar? default false
3774  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3775  * 
3776  * @cfg {String} tag (header|footer|nav|div) default is nav 
3777
3778  * 
3779  * 
3780  * 
3781  * @constructor
3782  * Create a new Sidebar
3783  * @param {Object} config The config object
3784  */
3785
3786
3787 Roo.bootstrap.NavSimplebar = function(config){
3788     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3789 };
3790
3791 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3792     
3793     inverse: false,
3794     
3795     type: false,
3796     arrangement: '',
3797     align : false,
3798     
3799     
3800     
3801     main : false,
3802     
3803     
3804     tag : false,
3805     
3806     
3807     getAutoCreate : function(){
3808         
3809         
3810         var cfg = {
3811             tag : this.tag || 'div',
3812             cls : 'navbar'
3813         };
3814           
3815         
3816         cfg.cn = [
3817             {
3818                 cls: 'nav',
3819                 tag : 'ul'
3820             }
3821         ];
3822         
3823          
3824         this.type = this.type || 'nav';
3825         if (['tabs','pills'].indexOf(this.type)!==-1) {
3826             cfg.cn[0].cls += ' nav-' + this.type
3827         
3828         
3829         } else {
3830             if (this.type!=='nav') {
3831                 Roo.log('nav type must be nav/tabs/pills')
3832             }
3833             cfg.cn[0].cls += ' navbar-nav'
3834         }
3835         
3836         
3837         
3838         
3839         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3840             cfg.cn[0].cls += ' nav-' + this.arrangement;
3841         }
3842         
3843         
3844         if (this.align === 'right') {
3845             cfg.cn[0].cls += ' navbar-right';
3846         }
3847         
3848         if (this.inverse) {
3849             cfg.cls += ' navbar-inverse';
3850             
3851         }
3852         
3853         
3854         return cfg;
3855     
3856         
3857     }
3858     
3859     
3860     
3861 });
3862
3863
3864
3865  
3866
3867  
3868        /*
3869  * - LGPL
3870  *
3871  * navbar
3872  * 
3873  */
3874
3875 /**
3876  * @class Roo.bootstrap.NavHeaderbar
3877  * @extends Roo.bootstrap.NavSimplebar
3878  * Bootstrap Sidebar class
3879  *
3880  * @cfg {String} brand what is brand
3881  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3882  * @cfg {String} brand_href href of the brand
3883  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3884  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3885  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3886  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3887  * 
3888  * @constructor
3889  * Create a new Sidebar
3890  * @param {Object} config The config object
3891  */
3892
3893
3894 Roo.bootstrap.NavHeaderbar = function(config){
3895     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3896       
3897 };
3898
3899 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3900     
3901     position: '',
3902     brand: '',
3903     brand_href: false,
3904     srButton : true,
3905     autohide : false,
3906     desktopCenter : false,
3907    
3908     
3909     getAutoCreate : function(){
3910         
3911         var   cfg = {
3912             tag: this.nav || 'nav',
3913             cls: 'navbar',
3914             role: 'navigation',
3915             cn: []
3916         };
3917         
3918         var cn = cfg.cn;
3919         if (this.desktopCenter) {
3920             cn.push({cls : 'container', cn : []});
3921             cn = cn[0].cn;
3922         }
3923         
3924         if(this.srButton){
3925             cn.push({
3926                 tag: 'div',
3927                 cls: 'navbar-header',
3928                 cn: [
3929                     {
3930                         tag: 'button',
3931                         type: 'button',
3932                         cls: 'navbar-toggle',
3933                         'data-toggle': 'collapse',
3934                         cn: [
3935                             {
3936                                 tag: 'span',
3937                                 cls: 'sr-only',
3938                                 html: 'Toggle navigation'
3939                             },
3940                             {
3941                                 tag: 'span',
3942                                 cls: 'icon-bar'
3943                             },
3944                             {
3945                                 tag: 'span',
3946                                 cls: 'icon-bar'
3947                             },
3948                             {
3949                                 tag: 'span',
3950                                 cls: 'icon-bar'
3951                             }
3952                         ]
3953                     }
3954                 ]
3955             });
3956         }
3957         
3958         cn.push({
3959             tag: 'div',
3960             cls: 'collapse navbar-collapse',
3961             cn : []
3962         });
3963         
3964         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3965         
3966         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3967             cfg.cls += ' navbar-' + this.position;
3968             
3969             // tag can override this..
3970             
3971             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3972         }
3973         
3974         if (this.brand !== '') {
3975             cn[0].cn.push({
3976                 tag: 'a',
3977                 href: this.brand_href ? this.brand_href : '#',
3978                 cls: 'navbar-brand',
3979                 cn: [
3980                 this.brand
3981                 ]
3982             });
3983         }
3984         
3985         if(this.main){
3986             cfg.cls += ' main-nav';
3987         }
3988         
3989         
3990         return cfg;
3991
3992         
3993     },
3994     getHeaderChildContainer : function()
3995     {
3996         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3997             return this.el.select('.navbar-header',true).first();
3998         }
3999         
4000         return this.getChildContainer();
4001     },
4002     
4003     
4004     initEvents : function()
4005     {
4006         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4007         
4008         if (this.autohide) {
4009             
4010             var prevScroll = 0;
4011             var ft = this.el;
4012             
4013             Roo.get(document).on('scroll',function(e) {
4014                 var ns = Roo.get(document).getScroll().top;
4015                 var os = prevScroll;
4016                 prevScroll = ns;
4017                 
4018                 if(ns > os){
4019                     ft.removeClass('slideDown');
4020                     ft.addClass('slideUp');
4021                     return;
4022                 }
4023                 ft.removeClass('slideUp');
4024                 ft.addClass('slideDown');
4025                  
4026               
4027           },this);
4028         }
4029     }    
4030     
4031 });
4032
4033
4034
4035  
4036
4037  /*
4038  * - LGPL
4039  *
4040  * navbar
4041  * 
4042  */
4043
4044 /**
4045  * @class Roo.bootstrap.NavSidebar
4046  * @extends Roo.bootstrap.Navbar
4047  * Bootstrap Sidebar class
4048  * 
4049  * @constructor
4050  * Create a new Sidebar
4051  * @param {Object} config The config object
4052  */
4053
4054
4055 Roo.bootstrap.NavSidebar = function(config){
4056     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4057 };
4058
4059 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4060     
4061     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4062     
4063     getAutoCreate : function(){
4064         
4065         
4066         return  {
4067             tag: 'div',
4068             cls: 'sidebar sidebar-nav'
4069         };
4070     
4071         
4072     }
4073     
4074     
4075     
4076 });
4077
4078
4079
4080  
4081
4082  /*
4083  * - LGPL
4084  *
4085  * nav group
4086  * 
4087  */
4088
4089 /**
4090  * @class Roo.bootstrap.NavGroup
4091  * @extends Roo.bootstrap.Component
4092  * Bootstrap NavGroup class
4093  * @cfg {String} align (left|right)
4094  * @cfg {Boolean} inverse
4095  * @cfg {String} type (nav|pills|tab) default nav
4096  * @cfg {String} navId - reference Id for navbar.
4097
4098  * 
4099  * @constructor
4100  * Create a new nav group
4101  * @param {Object} config The config object
4102  */
4103
4104 Roo.bootstrap.NavGroup = function(config){
4105     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4106     this.navItems = [];
4107    
4108     Roo.bootstrap.NavGroup.register(this);
4109      this.addEvents({
4110         /**
4111              * @event changed
4112              * Fires when the active item changes
4113              * @param {Roo.bootstrap.NavGroup} this
4114              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4115              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4116          */
4117         'changed': true
4118      });
4119     
4120 };
4121
4122 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4123     
4124     align: '',
4125     inverse: false,
4126     form: false,
4127     type: 'nav',
4128     navId : '',
4129     // private
4130     
4131     navItems : false, 
4132     
4133     getAutoCreate : function()
4134     {
4135         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4136         
4137         cfg = {
4138             tag : 'ul',
4139             cls: 'nav' 
4140         };
4141         
4142         if (['tabs','pills'].indexOf(this.type)!==-1) {
4143             cfg.cls += ' nav-' + this.type
4144         } else {
4145             if (this.type!=='nav') {
4146                 Roo.log('nav type must be nav/tabs/pills')
4147             }
4148             cfg.cls += ' navbar-nav'
4149         }
4150         
4151         if (this.parent() && this.parent().sidebar) {
4152             cfg = {
4153                 tag: 'ul',
4154                 cls: 'dashboard-menu sidebar-menu'
4155             };
4156             
4157             return cfg;
4158         }
4159         
4160         if (this.form === true) {
4161             cfg = {
4162                 tag: 'form',
4163                 cls: 'navbar-form'
4164             };
4165             
4166             if (this.align === 'right') {
4167                 cfg.cls += ' navbar-right';
4168             } else {
4169                 cfg.cls += ' navbar-left';
4170             }
4171         }
4172         
4173         if (this.align === 'right') {
4174             cfg.cls += ' navbar-right';
4175         }
4176         
4177         if (this.inverse) {
4178             cfg.cls += ' navbar-inverse';
4179             
4180         }
4181         
4182         
4183         return cfg;
4184     },
4185     /**
4186     * sets the active Navigation item
4187     * @param {Roo.bootstrap.NavItem} the new current navitem
4188     */
4189     setActiveItem : function(item)
4190     {
4191         var prev = false;
4192         Roo.each(this.navItems, function(v){
4193             if (v == item) {
4194                 return ;
4195             }
4196             if (v.isActive()) {
4197                 v.setActive(false, true);
4198                 prev = v;
4199                 
4200             }
4201             
4202         });
4203
4204         item.setActive(true, true);
4205         this.fireEvent('changed', this, item, prev);
4206         
4207         
4208     },
4209     /**
4210     * gets the active Navigation item
4211     * @return {Roo.bootstrap.NavItem} the current navitem
4212     */
4213     getActive : function()
4214     {
4215         
4216         var prev = false;
4217         Roo.each(this.navItems, function(v){
4218             
4219             if (v.isActive()) {
4220                 prev = v;
4221                 
4222             }
4223             
4224         });
4225         return prev;
4226     },
4227     
4228     indexOfNav : function()
4229     {
4230         
4231         var prev = false;
4232         Roo.each(this.navItems, function(v,i){
4233             
4234             if (v.isActive()) {
4235                 prev = i;
4236                 
4237             }
4238             
4239         });
4240         return prev;
4241     },
4242     /**
4243     * adds a Navigation item
4244     * @param {Roo.bootstrap.NavItem} the navitem to add
4245     */
4246     addItem : function(cfg)
4247     {
4248         var cn = new Roo.bootstrap.NavItem(cfg);
4249         this.register(cn);
4250         cn.parentId = this.id;
4251         cn.onRender(this.el, null);
4252         return cn;
4253     },
4254     /**
4255     * register a Navigation item
4256     * @param {Roo.bootstrap.NavItem} the navitem to add
4257     */
4258     register : function(item)
4259     {
4260         this.navItems.push( item);
4261         item.navId = this.navId;
4262     
4263     },
4264     
4265     /**
4266     * clear all the Navigation item
4267     */
4268    
4269     clearAll : function()
4270     {
4271         this.navItems = [];
4272         this.el.dom.innerHTML = '';
4273     },
4274     
4275     getNavItem: function(tabId)
4276     {
4277         var ret = false;
4278         Roo.each(this.navItems, function(e) {
4279             if (e.tabId == tabId) {
4280                ret =  e;
4281                return false;
4282             }
4283             return true;
4284             
4285         });
4286         return ret;
4287     },
4288     
4289     setActiveNext : function()
4290     {
4291         var i = this.indexOfNav(this.getActive());
4292         if (i > this.navItems.length) {
4293             return;
4294         }
4295         this.setActiveItem(this.navItems[i+1]);
4296     },
4297     setActivePrev : function()
4298     {
4299         var i = this.indexOfNav(this.getActive());
4300         if (i  < 1) {
4301             return;
4302         }
4303         this.setActiveItem(this.navItems[i-1]);
4304     },
4305     clearWasActive : function(except) {
4306         Roo.each(this.navItems, function(e) {
4307             if (e.tabId != except.tabId && e.was_active) {
4308                e.was_active = false;
4309                return false;
4310             }
4311             return true;
4312             
4313         });
4314     },
4315     getWasActive : function ()
4316     {
4317         var r = false;
4318         Roo.each(this.navItems, function(e) {
4319             if (e.was_active) {
4320                r = e;
4321                return false;
4322             }
4323             return true;
4324             
4325         });
4326         return r;
4327     }
4328     
4329     
4330 });
4331
4332  
4333 Roo.apply(Roo.bootstrap.NavGroup, {
4334     
4335     groups: {},
4336      /**
4337     * register a Navigation Group
4338     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4339     */
4340     register : function(navgrp)
4341     {
4342         this.groups[navgrp.navId] = navgrp;
4343         
4344     },
4345     /**
4346     * fetch a Navigation Group based on the navigation ID
4347     * @param {string} the navgroup to add
4348     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4349     */
4350     get: function(navId) {
4351         if (typeof(this.groups[navId]) == 'undefined') {
4352             return false;
4353             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4354         }
4355         return this.groups[navId] ;
4356     }
4357     
4358     
4359     
4360 });
4361
4362  /*
4363  * - LGPL
4364  *
4365  * row
4366  * 
4367  */
4368
4369 /**
4370  * @class Roo.bootstrap.NavItem
4371  * @extends Roo.bootstrap.Component
4372  * Bootstrap Navbar.NavItem class
4373  * @cfg {String} href  link to
4374  * @cfg {String} html content of button
4375  * @cfg {String} badge text inside badge
4376  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4377  * @cfg {String} glyphicon name of glyphicon
4378  * @cfg {String} icon name of font awesome icon
4379  * @cfg {Boolean} active Is item active
4380  * @cfg {Boolean} disabled Is item disabled
4381  
4382  * @cfg {Boolean} preventDefault (true | false) default false
4383  * @cfg {String} tabId the tab that this item activates.
4384  * @cfg {String} tagtype (a|span) render as a href or span?
4385  * @cfg {Boolean} animateRef (true|false) link to element default false  
4386   
4387  * @constructor
4388  * Create a new Navbar Item
4389  * @param {Object} config The config object
4390  */
4391 Roo.bootstrap.NavItem = function(config){
4392     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4393     this.addEvents({
4394         // raw events
4395         /**
4396          * @event click
4397          * The raw click event for the entire grid.
4398          * @param {Roo.EventObject} e
4399          */
4400         "click" : true,
4401          /**
4402             * @event changed
4403             * Fires when the active item active state changes
4404             * @param {Roo.bootstrap.NavItem} this
4405             * @param {boolean} state the new state
4406              
4407          */
4408         'changed': true,
4409         /**
4410             * @event scrollto
4411             * Fires when scroll to element
4412             * @param {Roo.bootstrap.NavItem} this
4413             * @param {Object} options
4414             * @param {Roo.EventObject} e
4415              
4416          */
4417         'scrollto': true
4418     });
4419    
4420 };
4421
4422 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4423     
4424     href: false,
4425     html: '',
4426     badge: '',
4427     icon: false,
4428     glyphicon: false,
4429     active: false,
4430     preventDefault : false,
4431     tabId : false,
4432     tagtype : 'a',
4433     disabled : false,
4434     animateRef : false,
4435     was_active : false,
4436     
4437     getAutoCreate : function(){
4438          
4439         var cfg = {
4440             tag: 'li',
4441             cls: 'nav-item'
4442             
4443         };
4444         
4445         if (this.active) {
4446             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4447         }
4448         if (this.disabled) {
4449             cfg.cls += ' disabled';
4450         }
4451         
4452         if (this.href || this.html || this.glyphicon || this.icon) {
4453             cfg.cn = [
4454                 {
4455                     tag: this.tagtype,
4456                     href : this.href || "#",
4457                     html: this.html || ''
4458                 }
4459             ];
4460             
4461             if (this.icon) {
4462                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4463             }
4464
4465             if(this.glyphicon) {
4466                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4467             }
4468             
4469             if (this.menu) {
4470                 
4471                 cfg.cn[0].html += " <span class='caret'></span>";
4472              
4473             }
4474             
4475             if (this.badge !== '') {
4476                  
4477                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4478             }
4479         }
4480         
4481         
4482         
4483         return cfg;
4484     },
4485     initEvents: function() 
4486     {
4487         if (typeof (this.menu) != 'undefined') {
4488             this.menu.parentType = this.xtype;
4489             this.menu.triggerEl = this.el;
4490             this.menu = this.addxtype(Roo.apply({}, this.menu));
4491         }
4492         
4493         this.el.select('a',true).on('click', this.onClick, this);
4494         
4495         if(this.tagtype == 'span'){
4496             this.el.select('span',true).on('click', this.onClick, this);
4497         }
4498        
4499         // at this point parent should be available..
4500         this.parent().register(this);
4501     },
4502     
4503     onClick : function(e)
4504     {
4505         if (e.getTarget('.dropdown-menu-item')) {
4506             // did you click on a menu itemm.... - then don't trigger onclick..
4507             return;
4508         }
4509         
4510         if(
4511                 this.preventDefault || 
4512                 this.href == '#' 
4513         ){
4514             Roo.log("NavItem - prevent Default?");
4515             e.preventDefault();
4516         }
4517         
4518         if (this.disabled) {
4519             return;
4520         }
4521         
4522         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4523         if (tg && tg.transition) {
4524             Roo.log("waiting for the transitionend");
4525             return;
4526         }
4527         
4528         
4529         
4530         //Roo.log("fire event clicked");
4531         if(this.fireEvent('click', this, e) === false){
4532             return;
4533         };
4534         
4535         if(this.tagtype == 'span'){
4536             return;
4537         }
4538         
4539         //Roo.log(this.href);
4540         var ael = this.el.select('a',true).first();
4541         //Roo.log(ael);
4542         
4543         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4544             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4545             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4546                 return; // ignore... - it's a 'hash' to another page.
4547             }
4548             Roo.log("NavItem - prevent Default?");
4549             e.preventDefault();
4550             this.scrollToElement(e);
4551         }
4552         
4553         
4554         var p =  this.parent();
4555    
4556         if (['tabs','pills'].indexOf(p.type)!==-1) {
4557             if (typeof(p.setActiveItem) !== 'undefined') {
4558                 p.setActiveItem(this);
4559             }
4560         }
4561         
4562         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4563         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4564             // remove the collapsed menu expand...
4565             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4566         }
4567     },
4568     
4569     isActive: function () {
4570         return this.active
4571     },
4572     setActive : function(state, fire, is_was_active)
4573     {
4574         if (this.active && !state && this.navId) {
4575             this.was_active = true;
4576             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4577             if (nv) {
4578                 nv.clearWasActive(this);
4579             }
4580             
4581         }
4582         this.active = state;
4583         
4584         if (!state ) {
4585             this.el.removeClass('active');
4586         } else if (!this.el.hasClass('active')) {
4587             this.el.addClass('active');
4588         }
4589         if (fire) {
4590             this.fireEvent('changed', this, state);
4591         }
4592         
4593         // show a panel if it's registered and related..
4594         
4595         if (!this.navId || !this.tabId || !state || is_was_active) {
4596             return;
4597         }
4598         
4599         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4600         if (!tg) {
4601             return;
4602         }
4603         var pan = tg.getPanelByName(this.tabId);
4604         if (!pan) {
4605             return;
4606         }
4607         // if we can not flip to new panel - go back to old nav highlight..
4608         if (false == tg.showPanel(pan)) {
4609             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4610             if (nv) {
4611                 var onav = nv.getWasActive();
4612                 if (onav) {
4613                     onav.setActive(true, false, true);
4614                 }
4615             }
4616             
4617         }
4618         
4619         
4620         
4621     },
4622      // this should not be here...
4623     setDisabled : function(state)
4624     {
4625         this.disabled = state;
4626         if (!state ) {
4627             this.el.removeClass('disabled');
4628         } else if (!this.el.hasClass('disabled')) {
4629             this.el.addClass('disabled');
4630         }
4631         
4632     },
4633     
4634     /**
4635      * Fetch the element to display the tooltip on.
4636      * @return {Roo.Element} defaults to this.el
4637      */
4638     tooltipEl : function()
4639     {
4640         return this.el.select('' + this.tagtype + '', true).first();
4641     },
4642     
4643     scrollToElement : function(e)
4644     {
4645         var c = document.body;
4646         
4647         /*
4648          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4649          */
4650         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4651             c = document.documentElement;
4652         }
4653         
4654         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4655         
4656         if(!target){
4657             return;
4658         }
4659
4660         var o = target.calcOffsetsTo(c);
4661         
4662         var options = {
4663             target : target,
4664             value : o[1]
4665         };
4666         
4667         this.fireEvent('scrollto', this, options, e);
4668         
4669         Roo.get(c).scrollTo('top', options.value, true);
4670         
4671         return;
4672     }
4673 });
4674  
4675
4676  /*
4677  * - LGPL
4678  *
4679  * sidebar item
4680  *
4681  *  li
4682  *    <span> icon </span>
4683  *    <span> text </span>
4684  *    <span>badge </span>
4685  */
4686
4687 /**
4688  * @class Roo.bootstrap.NavSidebarItem
4689  * @extends Roo.bootstrap.NavItem
4690  * Bootstrap Navbar.NavSidebarItem class
4691  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4692  * {Boolean} open is the menu open
4693  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4694  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4695  * {String} buttonSize (sm|md|lg)the extra classes for the button
4696  * {Boolean} showArrow show arrow next to the text (default true)
4697  * @constructor
4698  * Create a new Navbar Button
4699  * @param {Object} config The config object
4700  */
4701 Roo.bootstrap.NavSidebarItem = function(config){
4702     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4703     this.addEvents({
4704         // raw events
4705         /**
4706          * @event click
4707          * The raw click event for the entire grid.
4708          * @param {Roo.EventObject} e
4709          */
4710         "click" : true,
4711          /**
4712             * @event changed
4713             * Fires when the active item active state changes
4714             * @param {Roo.bootstrap.NavSidebarItem} this
4715             * @param {boolean} state the new state
4716              
4717          */
4718         'changed': true
4719     });
4720    
4721 };
4722
4723 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4724     
4725     badgeWeight : 'default',
4726     
4727     open: false,
4728     
4729     buttonView : false,
4730     
4731     buttonWeight : 'default',
4732     
4733     buttonSize : 'md',
4734     
4735     showArrow : true,
4736     
4737     getAutoCreate : function(){
4738         
4739         
4740         var a = {
4741                 tag: 'a',
4742                 href : this.href || '#',
4743                 cls: '',
4744                 html : '',
4745                 cn : []
4746         };
4747         
4748         if(this.buttonView){
4749             a = {
4750                 tag: 'button',
4751                 href : this.href || '#',
4752                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4753                 html : this.html,
4754                 cn : []
4755             };
4756         }
4757         
4758         var cfg = {
4759             tag: 'li',
4760             cls: '',
4761             cn: [ a ]
4762         };
4763         
4764         if (this.active) {
4765             cfg.cls += ' active';
4766         }
4767         
4768         if (this.disabled) {
4769             cfg.cls += ' disabled';
4770         }
4771         if (this.open) {
4772             cfg.cls += ' open x-open';
4773         }
4774         // left icon..
4775         if (this.glyphicon || this.icon) {
4776             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4777             a.cn.push({ tag : 'i', cls : c }) ;
4778         }
4779         
4780         if(!this.buttonView){
4781             var span = {
4782                 tag: 'span',
4783                 html : this.html || ''
4784             };
4785
4786             a.cn.push(span);
4787             
4788         }
4789         
4790         if (this.badge !== '') {
4791             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4792         }
4793         
4794         if (this.menu) {
4795             
4796             if(this.showArrow){
4797                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4798             }
4799             
4800             a.cls += ' dropdown-toggle treeview' ;
4801         }
4802         
4803         return cfg;
4804     },
4805     
4806     initEvents : function()
4807     { 
4808         if (typeof (this.menu) != 'undefined') {
4809             this.menu.parentType = this.xtype;
4810             this.menu.triggerEl = this.el;
4811             this.menu = this.addxtype(Roo.apply({}, this.menu));
4812         }
4813         
4814         this.el.on('click', this.onClick, this);
4815         
4816         if(this.badge !== ''){
4817             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4818         }
4819         
4820     },
4821     
4822     onClick : function(e)
4823     {
4824         if(this.disabled){
4825             e.preventDefault();
4826             return;
4827         }
4828         
4829         if(this.preventDefault){
4830             e.preventDefault();
4831         }
4832         
4833         this.fireEvent('click', this);
4834     },
4835     
4836     disable : function()
4837     {
4838         this.setDisabled(true);
4839     },
4840     
4841     enable : function()
4842     {
4843         this.setDisabled(false);
4844     },
4845     
4846     setDisabled : function(state)
4847     {
4848         if(this.disabled == state){
4849             return;
4850         }
4851         
4852         this.disabled = state;
4853         
4854         if (state) {
4855             this.el.addClass('disabled');
4856             return;
4857         }
4858         
4859         this.el.removeClass('disabled');
4860         
4861         return;
4862     },
4863     
4864     setActive : function(state)
4865     {
4866         if(this.active == state){
4867             return;
4868         }
4869         
4870         this.active = state;
4871         
4872         if (state) {
4873             this.el.addClass('active');
4874             return;
4875         }
4876         
4877         this.el.removeClass('active');
4878         
4879         return;
4880     },
4881     
4882     isActive: function () 
4883     {
4884         return this.active;
4885     },
4886     
4887     setBadge : function(str)
4888     {
4889         if(!this.badgeEl){
4890             return;
4891         }
4892         
4893         this.badgeEl.dom.innerHTML = str;
4894     }
4895     
4896    
4897      
4898  
4899 });
4900  
4901
4902  /*
4903  * - LGPL
4904  *
4905  * row
4906  * 
4907  */
4908
4909 /**
4910  * @class Roo.bootstrap.Row
4911  * @extends Roo.bootstrap.Component
4912  * Bootstrap Row class (contains columns...)
4913  * 
4914  * @constructor
4915  * Create a new Row
4916  * @param {Object} config The config object
4917  */
4918
4919 Roo.bootstrap.Row = function(config){
4920     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4921 };
4922
4923 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4924     
4925     getAutoCreate : function(){
4926        return {
4927             cls: 'row clearfix'
4928        };
4929     }
4930     
4931     
4932 });
4933
4934  
4935
4936  /*
4937  * - LGPL
4938  *
4939  * element
4940  * 
4941  */
4942
4943 /**
4944  * @class Roo.bootstrap.Element
4945  * @extends Roo.bootstrap.Component
4946  * Bootstrap Element class
4947  * @cfg {String} html contents of the element
4948  * @cfg {String} tag tag of the element
4949  * @cfg {String} cls class of the element
4950  * @cfg {Boolean} preventDefault (true|false) default false
4951  * @cfg {Boolean} clickable (true|false) default false
4952  * 
4953  * @constructor
4954  * Create a new Element
4955  * @param {Object} config The config object
4956  */
4957
4958 Roo.bootstrap.Element = function(config){
4959     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4960     
4961     this.addEvents({
4962         // raw events
4963         /**
4964          * @event click
4965          * When a element is chick
4966          * @param {Roo.bootstrap.Element} this
4967          * @param {Roo.EventObject} e
4968          */
4969         "click" : true
4970     });
4971 };
4972
4973 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4974     
4975     tag: 'div',
4976     cls: '',
4977     html: '',
4978     preventDefault: false, 
4979     clickable: false,
4980     
4981     getAutoCreate : function(){
4982         
4983         var cfg = {
4984             tag: this.tag,
4985             // cls: this.cls, double assign in parent class Component.js :: onRender
4986             html: this.html
4987         };
4988         
4989         return cfg;
4990     },
4991     
4992     initEvents: function() 
4993     {
4994         Roo.bootstrap.Element.superclass.initEvents.call(this);
4995         
4996         if(this.clickable){
4997             this.el.on('click', this.onClick, this);
4998         }
4999         
5000     },
5001     
5002     onClick : function(e)
5003     {
5004         if(this.preventDefault){
5005             e.preventDefault();
5006         }
5007         
5008         this.fireEvent('click', this, e);
5009     },
5010     
5011     getValue : function()
5012     {
5013         return this.el.dom.innerHTML;
5014     },
5015     
5016     setValue : function(value)
5017     {
5018         this.el.dom.innerHTML = value;
5019     }
5020    
5021 });
5022
5023  
5024
5025  /*
5026  * - LGPL
5027  *
5028  * pagination
5029  * 
5030  */
5031
5032 /**
5033  * @class Roo.bootstrap.Pagination
5034  * @extends Roo.bootstrap.Component
5035  * Bootstrap Pagination class
5036  * @cfg {String} size xs | sm | md | lg
5037  * @cfg {Boolean} inverse false | true
5038  * 
5039  * @constructor
5040  * Create a new Pagination
5041  * @param {Object} config The config object
5042  */
5043
5044 Roo.bootstrap.Pagination = function(config){
5045     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5046 };
5047
5048 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5049     
5050     cls: false,
5051     size: false,
5052     inverse: false,
5053     
5054     getAutoCreate : function(){
5055         var cfg = {
5056             tag: 'ul',
5057                 cls: 'pagination'
5058         };
5059         if (this.inverse) {
5060             cfg.cls += ' inverse';
5061         }
5062         if (this.html) {
5063             cfg.html=this.html;
5064         }
5065         if (this.cls) {
5066             cfg.cls += " " + this.cls;
5067         }
5068         return cfg;
5069     }
5070    
5071 });
5072
5073  
5074
5075  /*
5076  * - LGPL
5077  *
5078  * Pagination item
5079  * 
5080  */
5081
5082
5083 /**
5084  * @class Roo.bootstrap.PaginationItem
5085  * @extends Roo.bootstrap.Component
5086  * Bootstrap PaginationItem class
5087  * @cfg {String} html text
5088  * @cfg {String} href the link
5089  * @cfg {Boolean} preventDefault (true | false) default true
5090  * @cfg {Boolean} active (true | false) default false
5091  * @cfg {Boolean} disabled default false
5092  * 
5093  * 
5094  * @constructor
5095  * Create a new PaginationItem
5096  * @param {Object} config The config object
5097  */
5098
5099
5100 Roo.bootstrap.PaginationItem = function(config){
5101     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5102     this.addEvents({
5103         // raw events
5104         /**
5105          * @event click
5106          * The raw click event for the entire grid.
5107          * @param {Roo.EventObject} e
5108          */
5109         "click" : true
5110     });
5111 };
5112
5113 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5114     
5115     href : false,
5116     html : false,
5117     preventDefault: true,
5118     active : false,
5119     cls : false,
5120     disabled: false,
5121     
5122     getAutoCreate : function(){
5123         var cfg= {
5124             tag: 'li',
5125             cn: [
5126                 {
5127                     tag : 'a',
5128                     href : this.href ? this.href : '#',
5129                     html : this.html ? this.html : ''
5130                 }
5131             ]
5132         };
5133         
5134         if(this.cls){
5135             cfg.cls = this.cls;
5136         }
5137         
5138         if(this.disabled){
5139             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5140         }
5141         
5142         if(this.active){
5143             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5144         }
5145         
5146         return cfg;
5147     },
5148     
5149     initEvents: function() {
5150         
5151         this.el.on('click', this.onClick, this);
5152         
5153     },
5154     onClick : function(e)
5155     {
5156         Roo.log('PaginationItem on click ');
5157         if(this.preventDefault){
5158             e.preventDefault();
5159         }
5160         
5161         if(this.disabled){
5162             return;
5163         }
5164         
5165         this.fireEvent('click', this, e);
5166     }
5167    
5168 });
5169
5170  
5171
5172  /*
5173  * - LGPL
5174  *
5175  * slider
5176  * 
5177  */
5178
5179
5180 /**
5181  * @class Roo.bootstrap.Slider
5182  * @extends Roo.bootstrap.Component
5183  * Bootstrap Slider class
5184  *    
5185  * @constructor
5186  * Create a new Slider
5187  * @param {Object} config The config object
5188  */
5189
5190 Roo.bootstrap.Slider = function(config){
5191     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5192 };
5193
5194 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5195     
5196     getAutoCreate : function(){
5197         
5198         var cfg = {
5199             tag: 'div',
5200             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5201             cn: [
5202                 {
5203                     tag: 'a',
5204                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5205                 }
5206             ]
5207         };
5208         
5209         return cfg;
5210     }
5211    
5212 });
5213
5214  /*
5215  * Based on:
5216  * Ext JS Library 1.1.1
5217  * Copyright(c) 2006-2007, Ext JS, LLC.
5218  *
5219  * Originally Released Under LGPL - original licence link has changed is not relivant.
5220  *
5221  * Fork - LGPL
5222  * <script type="text/javascript">
5223  */
5224  
5225
5226 /**
5227  * @class Roo.grid.ColumnModel
5228  * @extends Roo.util.Observable
5229  * This is the default implementation of a ColumnModel used by the Grid. It defines
5230  * the columns in the grid.
5231  * <br>Usage:<br>
5232  <pre><code>
5233  var colModel = new Roo.grid.ColumnModel([
5234         {header: "Ticker", width: 60, sortable: true, locked: true},
5235         {header: "Company Name", width: 150, sortable: true},
5236         {header: "Market Cap.", width: 100, sortable: true},
5237         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5238         {header: "Employees", width: 100, sortable: true, resizable: false}
5239  ]);
5240  </code></pre>
5241  * <p>
5242  
5243  * The config options listed for this class are options which may appear in each
5244  * individual column definition.
5245  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5246  * @constructor
5247  * @param {Object} config An Array of column config objects. See this class's
5248  * config objects for details.
5249 */
5250 Roo.grid.ColumnModel = function(config){
5251         /**
5252      * The config passed into the constructor
5253      */
5254     this.config = config;
5255     this.lookup = {};
5256
5257     // if no id, create one
5258     // if the column does not have a dataIndex mapping,
5259     // map it to the order it is in the config
5260     for(var i = 0, len = config.length; i < len; i++){
5261         var c = config[i];
5262         if(typeof c.dataIndex == "undefined"){
5263             c.dataIndex = i;
5264         }
5265         if(typeof c.renderer == "string"){
5266             c.renderer = Roo.util.Format[c.renderer];
5267         }
5268         if(typeof c.id == "undefined"){
5269             c.id = Roo.id();
5270         }
5271         if(c.editor && c.editor.xtype){
5272             c.editor  = Roo.factory(c.editor, Roo.grid);
5273         }
5274         if(c.editor && c.editor.isFormField){
5275             c.editor = new Roo.grid.GridEditor(c.editor);
5276         }
5277         this.lookup[c.id] = c;
5278     }
5279
5280     /**
5281      * The width of columns which have no width specified (defaults to 100)
5282      * @type Number
5283      */
5284     this.defaultWidth = 100;
5285
5286     /**
5287      * Default sortable of columns which have no sortable specified (defaults to false)
5288      * @type Boolean
5289      */
5290     this.defaultSortable = false;
5291
5292     this.addEvents({
5293         /**
5294              * @event widthchange
5295              * Fires when the width of a column changes.
5296              * @param {ColumnModel} this
5297              * @param {Number} columnIndex The column index
5298              * @param {Number} newWidth The new width
5299              */
5300             "widthchange": true,
5301         /**
5302              * @event headerchange
5303              * Fires when the text of a header changes.
5304              * @param {ColumnModel} this
5305              * @param {Number} columnIndex The column index
5306              * @param {Number} newText The new header text
5307              */
5308             "headerchange": true,
5309         /**
5310              * @event hiddenchange
5311              * Fires when a column is hidden or "unhidden".
5312              * @param {ColumnModel} this
5313              * @param {Number} columnIndex The column index
5314              * @param {Boolean} hidden true if hidden, false otherwise
5315              */
5316             "hiddenchange": true,
5317             /**
5318          * @event columnmoved
5319          * Fires when a column is moved.
5320          * @param {ColumnModel} this
5321          * @param {Number} oldIndex
5322          * @param {Number} newIndex
5323          */
5324         "columnmoved" : true,
5325         /**
5326          * @event columlockchange
5327          * Fires when a column's locked state is changed
5328          * @param {ColumnModel} this
5329          * @param {Number} colIndex
5330          * @param {Boolean} locked true if locked
5331          */
5332         "columnlockchange" : true
5333     });
5334     Roo.grid.ColumnModel.superclass.constructor.call(this);
5335 };
5336 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5337     /**
5338      * @cfg {String} header The header text to display in the Grid view.
5339      */
5340     /**
5341      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5342      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5343      * specified, the column's index is used as an index into the Record's data Array.
5344      */
5345     /**
5346      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5347      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5348      */
5349     /**
5350      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5351      * Defaults to the value of the {@link #defaultSortable} property.
5352      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5353      */
5354     /**
5355      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5356      */
5357     /**
5358      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5359      */
5360     /**
5361      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5362      */
5363     /**
5364      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5365      */
5366     /**
5367      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5368      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5369      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5370      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5371      */
5372        /**
5373      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5374      */
5375     /**
5376      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5377      */
5378     /**
5379      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5380      */
5381     /**
5382      * @cfg {String} cursor (Optional)
5383      */
5384     /**
5385      * @cfg {String} tooltip (Optional)
5386      */
5387     /**
5388      * @cfg {Number} xs (Optional)
5389      */
5390     /**
5391      * @cfg {Number} sm (Optional)
5392      */
5393     /**
5394      * @cfg {Number} md (Optional)
5395      */
5396     /**
5397      * @cfg {Number} lg (Optional)
5398      */
5399     /**
5400      * Returns the id of the column at the specified index.
5401      * @param {Number} index The column index
5402      * @return {String} the id
5403      */
5404     getColumnId : function(index){
5405         return this.config[index].id;
5406     },
5407
5408     /**
5409      * Returns the column for a specified id.
5410      * @param {String} id The column id
5411      * @return {Object} the column
5412      */
5413     getColumnById : function(id){
5414         return this.lookup[id];
5415     },
5416
5417     
5418     /**
5419      * Returns the column for a specified dataIndex.
5420      * @param {String} dataIndex The column dataIndex
5421      * @return {Object|Boolean} the column or false if not found
5422      */
5423     getColumnByDataIndex: function(dataIndex){
5424         var index = this.findColumnIndex(dataIndex);
5425         return index > -1 ? this.config[index] : false;
5426     },
5427     
5428     /**
5429      * Returns the index for a specified column id.
5430      * @param {String} id The column id
5431      * @return {Number} the index, or -1 if not found
5432      */
5433     getIndexById : function(id){
5434         for(var i = 0, len = this.config.length; i < len; i++){
5435             if(this.config[i].id == id){
5436                 return i;
5437             }
5438         }
5439         return -1;
5440     },
5441     
5442     /**
5443      * Returns the index for a specified column dataIndex.
5444      * @param {String} dataIndex The column dataIndex
5445      * @return {Number} the index, or -1 if not found
5446      */
5447     
5448     findColumnIndex : function(dataIndex){
5449         for(var i = 0, len = this.config.length; i < len; i++){
5450             if(this.config[i].dataIndex == dataIndex){
5451                 return i;
5452             }
5453         }
5454         return -1;
5455     },
5456     
5457     
5458     moveColumn : function(oldIndex, newIndex){
5459         var c = this.config[oldIndex];
5460         this.config.splice(oldIndex, 1);
5461         this.config.splice(newIndex, 0, c);
5462         this.dataMap = null;
5463         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5464     },
5465
5466     isLocked : function(colIndex){
5467         return this.config[colIndex].locked === true;
5468     },
5469
5470     setLocked : function(colIndex, value, suppressEvent){
5471         if(this.isLocked(colIndex) == value){
5472             return;
5473         }
5474         this.config[colIndex].locked = value;
5475         if(!suppressEvent){
5476             this.fireEvent("columnlockchange", this, colIndex, value);
5477         }
5478     },
5479
5480     getTotalLockedWidth : function(){
5481         var totalWidth = 0;
5482         for(var i = 0; i < this.config.length; i++){
5483             if(this.isLocked(i) && !this.isHidden(i)){
5484                 this.totalWidth += this.getColumnWidth(i);
5485             }
5486         }
5487         return totalWidth;
5488     },
5489
5490     getLockedCount : function(){
5491         for(var i = 0, len = this.config.length; i < len; i++){
5492             if(!this.isLocked(i)){
5493                 return i;
5494             }
5495         }
5496         
5497         return this.config.length;
5498     },
5499
5500     /**
5501      * Returns the number of columns.
5502      * @return {Number}
5503      */
5504     getColumnCount : function(visibleOnly){
5505         if(visibleOnly === true){
5506             var c = 0;
5507             for(var i = 0, len = this.config.length; i < len; i++){
5508                 if(!this.isHidden(i)){
5509                     c++;
5510                 }
5511             }
5512             return c;
5513         }
5514         return this.config.length;
5515     },
5516
5517     /**
5518      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5519      * @param {Function} fn
5520      * @param {Object} scope (optional)
5521      * @return {Array} result
5522      */
5523     getColumnsBy : function(fn, scope){
5524         var r = [];
5525         for(var i = 0, len = this.config.length; i < len; i++){
5526             var c = this.config[i];
5527             if(fn.call(scope||this, c, i) === true){
5528                 r[r.length] = c;
5529             }
5530         }
5531         return r;
5532     },
5533
5534     /**
5535      * Returns true if the specified column is sortable.
5536      * @param {Number} col The column index
5537      * @return {Boolean}
5538      */
5539     isSortable : function(col){
5540         if(typeof this.config[col].sortable == "undefined"){
5541             return this.defaultSortable;
5542         }
5543         return this.config[col].sortable;
5544     },
5545
5546     /**
5547      * Returns the rendering (formatting) function defined for the column.
5548      * @param {Number} col The column index.
5549      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5550      */
5551     getRenderer : function(col){
5552         if(!this.config[col].renderer){
5553             return Roo.grid.ColumnModel.defaultRenderer;
5554         }
5555         return this.config[col].renderer;
5556     },
5557
5558     /**
5559      * Sets the rendering (formatting) function for a column.
5560      * @param {Number} col The column index
5561      * @param {Function} fn The function to use to process the cell's raw data
5562      * to return HTML markup for the grid view. The render function is called with
5563      * the following parameters:<ul>
5564      * <li>Data value.</li>
5565      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5566      * <li>css A CSS style string to apply to the table cell.</li>
5567      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5568      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5569      * <li>Row index</li>
5570      * <li>Column index</li>
5571      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5572      */
5573     setRenderer : function(col, fn){
5574         this.config[col].renderer = fn;
5575     },
5576
5577     /**
5578      * Returns the width for the specified column.
5579      * @param {Number} col The column index
5580      * @return {Number}
5581      */
5582     getColumnWidth : function(col){
5583         return this.config[col].width * 1 || this.defaultWidth;
5584     },
5585
5586     /**
5587      * Sets the width for a column.
5588      * @param {Number} col The column index
5589      * @param {Number} width The new width
5590      */
5591     setColumnWidth : function(col, width, suppressEvent){
5592         this.config[col].width = width;
5593         this.totalWidth = null;
5594         if(!suppressEvent){
5595              this.fireEvent("widthchange", this, col, width);
5596         }
5597     },
5598
5599     /**
5600      * Returns the total width of all columns.
5601      * @param {Boolean} includeHidden True to include hidden column widths
5602      * @return {Number}
5603      */
5604     getTotalWidth : function(includeHidden){
5605         if(!this.totalWidth){
5606             this.totalWidth = 0;
5607             for(var i = 0, len = this.config.length; i < len; i++){
5608                 if(includeHidden || !this.isHidden(i)){
5609                     this.totalWidth += this.getColumnWidth(i);
5610                 }
5611             }
5612         }
5613         return this.totalWidth;
5614     },
5615
5616     /**
5617      * Returns the header for the specified column.
5618      * @param {Number} col The column index
5619      * @return {String}
5620      */
5621     getColumnHeader : function(col){
5622         return this.config[col].header;
5623     },
5624
5625     /**
5626      * Sets the header for a column.
5627      * @param {Number} col The column index
5628      * @param {String} header The new header
5629      */
5630     setColumnHeader : function(col, header){
5631         this.config[col].header = header;
5632         this.fireEvent("headerchange", this, col, header);
5633     },
5634
5635     /**
5636      * Returns the tooltip for the specified column.
5637      * @param {Number} col The column index
5638      * @return {String}
5639      */
5640     getColumnTooltip : function(col){
5641             return this.config[col].tooltip;
5642     },
5643     /**
5644      * Sets the tooltip for a column.
5645      * @param {Number} col The column index
5646      * @param {String} tooltip The new tooltip
5647      */
5648     setColumnTooltip : function(col, tooltip){
5649             this.config[col].tooltip = tooltip;
5650     },
5651
5652     /**
5653      * Returns the dataIndex for the specified column.
5654      * @param {Number} col The column index
5655      * @return {Number}
5656      */
5657     getDataIndex : function(col){
5658         return this.config[col].dataIndex;
5659     },
5660
5661     /**
5662      * Sets the dataIndex for a column.
5663      * @param {Number} col The column index
5664      * @param {Number} dataIndex The new dataIndex
5665      */
5666     setDataIndex : function(col, dataIndex){
5667         this.config[col].dataIndex = dataIndex;
5668     },
5669
5670     
5671     
5672     /**
5673      * Returns true if the cell is editable.
5674      * @param {Number} colIndex The column index
5675      * @param {Number} rowIndex The row index - this is nto actually used..?
5676      * @return {Boolean}
5677      */
5678     isCellEditable : function(colIndex, rowIndex){
5679         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5680     },
5681
5682     /**
5683      * Returns the editor defined for the cell/column.
5684      * return false or null to disable editing.
5685      * @param {Number} colIndex The column index
5686      * @param {Number} rowIndex The row index
5687      * @return {Object}
5688      */
5689     getCellEditor : function(colIndex, rowIndex){
5690         return this.config[colIndex].editor;
5691     },
5692
5693     /**
5694      * Sets if a column is editable.
5695      * @param {Number} col The column index
5696      * @param {Boolean} editable True if the column is editable
5697      */
5698     setEditable : function(col, editable){
5699         this.config[col].editable = editable;
5700     },
5701
5702
5703     /**
5704      * Returns true if the column is hidden.
5705      * @param {Number} colIndex The column index
5706      * @return {Boolean}
5707      */
5708     isHidden : function(colIndex){
5709         return this.config[colIndex].hidden;
5710     },
5711
5712
5713     /**
5714      * Returns true if the column width cannot be changed
5715      */
5716     isFixed : function(colIndex){
5717         return this.config[colIndex].fixed;
5718     },
5719
5720     /**
5721      * Returns true if the column can be resized
5722      * @return {Boolean}
5723      */
5724     isResizable : function(colIndex){
5725         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5726     },
5727     /**
5728      * Sets if a column is hidden.
5729      * @param {Number} colIndex The column index
5730      * @param {Boolean} hidden True if the column is hidden
5731      */
5732     setHidden : function(colIndex, hidden){
5733         this.config[colIndex].hidden = hidden;
5734         this.totalWidth = null;
5735         this.fireEvent("hiddenchange", this, colIndex, hidden);
5736     },
5737
5738     /**
5739      * Sets the editor for a column.
5740      * @param {Number} col The column index
5741      * @param {Object} editor The editor object
5742      */
5743     setEditor : function(col, editor){
5744         this.config[col].editor = editor;
5745     }
5746 });
5747
5748 Roo.grid.ColumnModel.defaultRenderer = function(value)
5749 {
5750     if(typeof value == "object") {
5751         return value;
5752     }
5753         if(typeof value == "string" && value.length < 1){
5754             return "&#160;";
5755         }
5756     
5757         return String.format("{0}", value);
5758 };
5759
5760 // Alias for backwards compatibility
5761 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5762 /*
5763  * Based on:
5764  * Ext JS Library 1.1.1
5765  * Copyright(c) 2006-2007, Ext JS, LLC.
5766  *
5767  * Originally Released Under LGPL - original licence link has changed is not relivant.
5768  *
5769  * Fork - LGPL
5770  * <script type="text/javascript">
5771  */
5772  
5773 /**
5774  * @class Roo.LoadMask
5775  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5776  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5777  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5778  * element's UpdateManager load indicator and will be destroyed after the initial load.
5779  * @constructor
5780  * Create a new LoadMask
5781  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5782  * @param {Object} config The config object
5783  */
5784 Roo.LoadMask = function(el, config){
5785     this.el = Roo.get(el);
5786     Roo.apply(this, config);
5787     if(this.store){
5788         this.store.on('beforeload', this.onBeforeLoad, this);
5789         this.store.on('load', this.onLoad, this);
5790         this.store.on('loadexception', this.onLoadException, this);
5791         this.removeMask = false;
5792     }else{
5793         var um = this.el.getUpdateManager();
5794         um.showLoadIndicator = false; // disable the default indicator
5795         um.on('beforeupdate', this.onBeforeLoad, this);
5796         um.on('update', this.onLoad, this);
5797         um.on('failure', this.onLoad, this);
5798         this.removeMask = true;
5799     }
5800 };
5801
5802 Roo.LoadMask.prototype = {
5803     /**
5804      * @cfg {Boolean} removeMask
5805      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5806      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5807      */
5808     /**
5809      * @cfg {String} msg
5810      * The text to display in a centered loading message box (defaults to 'Loading...')
5811      */
5812     msg : 'Loading...',
5813     /**
5814      * @cfg {String} msgCls
5815      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5816      */
5817     msgCls : 'x-mask-loading',
5818
5819     /**
5820      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5821      * @type Boolean
5822      */
5823     disabled: false,
5824
5825     /**
5826      * Disables the mask to prevent it from being displayed
5827      */
5828     disable : function(){
5829        this.disabled = true;
5830     },
5831
5832     /**
5833      * Enables the mask so that it can be displayed
5834      */
5835     enable : function(){
5836         this.disabled = false;
5837     },
5838     
5839     onLoadException : function()
5840     {
5841         Roo.log(arguments);
5842         
5843         if (typeof(arguments[3]) != 'undefined') {
5844             Roo.MessageBox.alert("Error loading",arguments[3]);
5845         } 
5846         /*
5847         try {
5848             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5849                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5850             }   
5851         } catch(e) {
5852             
5853         }
5854         */
5855     
5856         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5857     },
5858     // private
5859     onLoad : function()
5860     {
5861         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5862     },
5863
5864     // private
5865     onBeforeLoad : function(){
5866         if(!this.disabled){
5867             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5868         }
5869     },
5870
5871     // private
5872     destroy : function(){
5873         if(this.store){
5874             this.store.un('beforeload', this.onBeforeLoad, this);
5875             this.store.un('load', this.onLoad, this);
5876             this.store.un('loadexception', this.onLoadException, this);
5877         }else{
5878             var um = this.el.getUpdateManager();
5879             um.un('beforeupdate', this.onBeforeLoad, this);
5880             um.un('update', this.onLoad, this);
5881             um.un('failure', this.onLoad, this);
5882         }
5883     }
5884 };/*
5885  * - LGPL
5886  *
5887  * table
5888  * 
5889  */
5890
5891 /**
5892  * @class Roo.bootstrap.Table
5893  * @extends Roo.bootstrap.Component
5894  * Bootstrap Table class
5895  * @cfg {String} cls table class
5896  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5897  * @cfg {String} bgcolor Specifies the background color for a table
5898  * @cfg {Number} border Specifies whether the table cells should have borders or not
5899  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5900  * @cfg {Number} cellspacing Specifies the space between cells
5901  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5902  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5903  * @cfg {String} sortable Specifies that the table should be sortable
5904  * @cfg {String} summary Specifies a summary of the content of a table
5905  * @cfg {Number} width Specifies the width of a table
5906  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5907  * 
5908  * @cfg {boolean} striped Should the rows be alternative striped
5909  * @cfg {boolean} bordered Add borders to the table
5910  * @cfg {boolean} hover Add hover highlighting
5911  * @cfg {boolean} condensed Format condensed
5912  * @cfg {boolean} responsive Format condensed
5913  * @cfg {Boolean} loadMask (true|false) default false
5914  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5915  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5916  * @cfg {Boolean} rowSelection (true|false) default false
5917  * @cfg {Boolean} cellSelection (true|false) default false
5918  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5919  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5920  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5921  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5922  
5923  * 
5924  * @constructor
5925  * Create a new Table
5926  * @param {Object} config The config object
5927  */
5928
5929 Roo.bootstrap.Table = function(config){
5930     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5931     
5932   
5933     
5934     // BC...
5935     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5936     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5937     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5938     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5939     
5940     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5941     if (this.sm) {
5942         this.sm.grid = this;
5943         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5944         this.sm = this.selModel;
5945         this.sm.xmodule = this.xmodule || false;
5946     }
5947     
5948     if (this.cm && typeof(this.cm.config) == 'undefined') {
5949         this.colModel = new Roo.grid.ColumnModel(this.cm);
5950         this.cm = this.colModel;
5951         this.cm.xmodule = this.xmodule || false;
5952     }
5953     if (this.store) {
5954         this.store= Roo.factory(this.store, Roo.data);
5955         this.ds = this.store;
5956         this.ds.xmodule = this.xmodule || false;
5957          
5958     }
5959     if (this.footer && this.store) {
5960         this.footer.dataSource = this.ds;
5961         this.footer = Roo.factory(this.footer);
5962     }
5963     
5964     /** @private */
5965     this.addEvents({
5966         /**
5967          * @event cellclick
5968          * Fires when a cell is clicked
5969          * @param {Roo.bootstrap.Table} this
5970          * @param {Roo.Element} el
5971          * @param {Number} rowIndex
5972          * @param {Number} columnIndex
5973          * @param {Roo.EventObject} e
5974          */
5975         "cellclick" : true,
5976         /**
5977          * @event celldblclick
5978          * Fires when a cell is double clicked
5979          * @param {Roo.bootstrap.Table} this
5980          * @param {Roo.Element} el
5981          * @param {Number} rowIndex
5982          * @param {Number} columnIndex
5983          * @param {Roo.EventObject} e
5984          */
5985         "celldblclick" : true,
5986         /**
5987          * @event rowclick
5988          * Fires when a row is clicked
5989          * @param {Roo.bootstrap.Table} this
5990          * @param {Roo.Element} el
5991          * @param {Number} rowIndex
5992          * @param {Roo.EventObject} e
5993          */
5994         "rowclick" : true,
5995         /**
5996          * @event rowdblclick
5997          * Fires when a row is double clicked
5998          * @param {Roo.bootstrap.Table} this
5999          * @param {Roo.Element} el
6000          * @param {Number} rowIndex
6001          * @param {Roo.EventObject} e
6002          */
6003         "rowdblclick" : true,
6004         /**
6005          * @event mouseover
6006          * Fires when a mouseover occur
6007          * @param {Roo.bootstrap.Table} this
6008          * @param {Roo.Element} el
6009          * @param {Number} rowIndex
6010          * @param {Number} columnIndex
6011          * @param {Roo.EventObject} e
6012          */
6013         "mouseover" : true,
6014         /**
6015          * @event mouseout
6016          * Fires when a mouseout occur
6017          * @param {Roo.bootstrap.Table} this
6018          * @param {Roo.Element} el
6019          * @param {Number} rowIndex
6020          * @param {Number} columnIndex
6021          * @param {Roo.EventObject} e
6022          */
6023         "mouseout" : true,
6024         /**
6025          * @event rowclass
6026          * Fires when a row is rendered, so you can change add a style to it.
6027          * @param {Roo.bootstrap.Table} this
6028          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6029          */
6030         'rowclass' : true,
6031           /**
6032          * @event rowsrendered
6033          * Fires when all the  rows have been rendered
6034          * @param {Roo.bootstrap.Table} this
6035          */
6036         'rowsrendered' : true,
6037         /**
6038          * @event contextmenu
6039          * The raw contextmenu event for the entire grid.
6040          * @param {Roo.EventObject} e
6041          */
6042         "contextmenu" : true,
6043         /**
6044          * @event rowcontextmenu
6045          * Fires when a row is right clicked
6046          * @param {Roo.bootstrap.Table} this
6047          * @param {Number} rowIndex
6048          * @param {Roo.EventObject} e
6049          */
6050         "rowcontextmenu" : true,
6051         /**
6052          * @event cellcontextmenu
6053          * Fires when a cell is right clicked
6054          * @param {Roo.bootstrap.Table} this
6055          * @param {Number} rowIndex
6056          * @param {Number} cellIndex
6057          * @param {Roo.EventObject} e
6058          */
6059          "cellcontextmenu" : true,
6060          /**
6061          * @event headercontextmenu
6062          * Fires when a header is right clicked
6063          * @param {Roo.bootstrap.Table} this
6064          * @param {Number} columnIndex
6065          * @param {Roo.EventObject} e
6066          */
6067         "headercontextmenu" : true
6068     });
6069 };
6070
6071 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6072     
6073     cls: false,
6074     align: false,
6075     bgcolor: false,
6076     border: false,
6077     cellpadding: false,
6078     cellspacing: false,
6079     frame: false,
6080     rules: false,
6081     sortable: false,
6082     summary: false,
6083     width: false,
6084     striped : false,
6085     scrollBody : false,
6086     bordered: false,
6087     hover:  false,
6088     condensed : false,
6089     responsive : false,
6090     sm : false,
6091     cm : false,
6092     store : false,
6093     loadMask : false,
6094     footerShow : true,
6095     headerShow : true,
6096   
6097     rowSelection : false,
6098     cellSelection : false,
6099     layout : false,
6100     
6101     // Roo.Element - the tbody
6102     mainBody: false,
6103     // Roo.Element - thead element
6104     mainHead: false,
6105     
6106     container: false, // used by gridpanel...
6107     
6108     lazyLoad : false,
6109     
6110     CSS : Roo.util.CSS,
6111     
6112     auto_hide_footer : false,
6113     
6114     getAutoCreate : function()
6115     {
6116         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6117         
6118         cfg = {
6119             tag: 'table',
6120             cls : 'table',
6121             cn : []
6122         };
6123         if (this.scrollBody) {
6124             cfg.cls += ' table-body-fixed';
6125         }    
6126         if (this.striped) {
6127             cfg.cls += ' table-striped';
6128         }
6129         
6130         if (this.hover) {
6131             cfg.cls += ' table-hover';
6132         }
6133         if (this.bordered) {
6134             cfg.cls += ' table-bordered';
6135         }
6136         if (this.condensed) {
6137             cfg.cls += ' table-condensed';
6138         }
6139         if (this.responsive) {
6140             cfg.cls += ' table-responsive';
6141         }
6142         
6143         if (this.cls) {
6144             cfg.cls+=  ' ' +this.cls;
6145         }
6146         
6147         // this lot should be simplifed...
6148         var _t = this;
6149         var cp = [
6150             'align',
6151             'bgcolor',
6152             'border',
6153             'cellpadding',
6154             'cellspacing',
6155             'frame',
6156             'rules',
6157             'sortable',
6158             'summary',
6159             'width'
6160         ].forEach(function(k) {
6161             if (_t[k]) {
6162                 cfg[k] = _t[k];
6163             }
6164         });
6165         
6166         
6167         if (this.layout) {
6168             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6169         }
6170         
6171         if(this.store || this.cm){
6172             if(this.headerShow){
6173                 cfg.cn.push(this.renderHeader());
6174             }
6175             
6176             cfg.cn.push(this.renderBody());
6177             
6178             if(this.footerShow){
6179                 cfg.cn.push(this.renderFooter());
6180             }
6181             // where does this come from?
6182             //cfg.cls+=  ' TableGrid';
6183         }
6184         
6185         return { cn : [ cfg ] };
6186     },
6187     
6188     initEvents : function()
6189     {   
6190         if(!this.store || !this.cm){
6191             return;
6192         }
6193         if (this.selModel) {
6194             this.selModel.initEvents();
6195         }
6196         
6197         
6198         //Roo.log('initEvents with ds!!!!');
6199         
6200         this.mainBody = this.el.select('tbody', true).first();
6201         this.mainHead = this.el.select('thead', true).first();
6202         this.mainFoot = this.el.select('tfoot', true).first();
6203         
6204         
6205         
6206         var _this = this;
6207         
6208         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6209             e.on('click', _this.sort, _this);
6210         });
6211         
6212         this.mainBody.on("click", this.onClick, this);
6213         this.mainBody.on("dblclick", this.onDblClick, this);
6214         
6215         // why is this done????? = it breaks dialogs??
6216         //this.parent().el.setStyle('position', 'relative');
6217         
6218         
6219         if (this.footer) {
6220             this.footer.parentId = this.id;
6221             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6222             
6223             if(this.lazyLoad){
6224                 this.el.select('tfoot tr td').first().addClass('hide');
6225             }
6226         } 
6227         
6228         if(this.loadMask) {
6229             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6230         }
6231         
6232         this.store.on('load', this.onLoad, this);
6233         this.store.on('beforeload', this.onBeforeLoad, this);
6234         this.store.on('update', this.onUpdate, this);
6235         this.store.on('add', this.onAdd, this);
6236         this.store.on("clear", this.clear, this);
6237         
6238         this.el.on("contextmenu", this.onContextMenu, this);
6239         
6240         this.mainBody.on('scroll', this.onBodyScroll, this);
6241         
6242         this.cm.on("headerchange", this.onHeaderChange, this);
6243         
6244         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6245         
6246     },
6247     
6248     onContextMenu : function(e, t)
6249     {
6250         this.processEvent("contextmenu", e);
6251     },
6252     
6253     processEvent : function(name, e)
6254     {
6255         if (name != 'touchstart' ) {
6256             this.fireEvent(name, e);    
6257         }
6258         
6259         var t = e.getTarget();
6260         
6261         var cell = Roo.get(t);
6262         
6263         if(!cell){
6264             return;
6265         }
6266         
6267         if(cell.findParent('tfoot', false, true)){
6268             return;
6269         }
6270         
6271         if(cell.findParent('thead', false, true)){
6272             
6273             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6274                 cell = Roo.get(t).findParent('th', false, true);
6275                 if (!cell) {
6276                     Roo.log("failed to find th in thead?");
6277                     Roo.log(e.getTarget());
6278                     return;
6279                 }
6280             }
6281             
6282             var cellIndex = cell.dom.cellIndex;
6283             
6284             var ename = name == 'touchstart' ? 'click' : name;
6285             this.fireEvent("header" + ename, this, cellIndex, e);
6286             
6287             return;
6288         }
6289         
6290         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291             cell = Roo.get(t).findParent('td', false, true);
6292             if (!cell) {
6293                 Roo.log("failed to find th in tbody?");
6294                 Roo.log(e.getTarget());
6295                 return;
6296             }
6297         }
6298         
6299         var row = cell.findParent('tr', false, true);
6300         var cellIndex = cell.dom.cellIndex;
6301         var rowIndex = row.dom.rowIndex - 1;
6302         
6303         if(row !== false){
6304             
6305             this.fireEvent("row" + name, this, rowIndex, e);
6306             
6307             if(cell !== false){
6308             
6309                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6310             }
6311         }
6312         
6313     },
6314     
6315     onMouseover : function(e, el)
6316     {
6317         var cell = Roo.get(el);
6318         
6319         if(!cell){
6320             return;
6321         }
6322         
6323         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6324             cell = cell.findParent('td', false, true);
6325         }
6326         
6327         var row = cell.findParent('tr', false, true);
6328         var cellIndex = cell.dom.cellIndex;
6329         var rowIndex = row.dom.rowIndex - 1; // start from 0
6330         
6331         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6332         
6333     },
6334     
6335     onMouseout : function(e, el)
6336     {
6337         var cell = Roo.get(el);
6338         
6339         if(!cell){
6340             return;
6341         }
6342         
6343         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6344             cell = cell.findParent('td', false, true);
6345         }
6346         
6347         var row = cell.findParent('tr', false, true);
6348         var cellIndex = cell.dom.cellIndex;
6349         var rowIndex = row.dom.rowIndex - 1; // start from 0
6350         
6351         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6352         
6353     },
6354     
6355     onClick : function(e, el)
6356     {
6357         var cell = Roo.get(el);
6358         
6359         if(!cell || (!this.cellSelection && !this.rowSelection)){
6360             return;
6361         }
6362         
6363         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6364             cell = cell.findParent('td', false, true);
6365         }
6366         
6367         if(!cell || typeof(cell) == 'undefined'){
6368             return;
6369         }
6370         
6371         var row = cell.findParent('tr', false, true);
6372         
6373         if(!row || typeof(row) == 'undefined'){
6374             return;
6375         }
6376         
6377         var cellIndex = cell.dom.cellIndex;
6378         var rowIndex = this.getRowIndex(row);
6379         
6380         // why??? - should these not be based on SelectionModel?
6381         if(this.cellSelection){
6382             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6383         }
6384         
6385         if(this.rowSelection){
6386             this.fireEvent('rowclick', this, row, rowIndex, e);
6387         }
6388         
6389         
6390     },
6391         
6392     onDblClick : function(e,el)
6393     {
6394         var cell = Roo.get(el);
6395         
6396         if(!cell || (!this.cellSelection && !this.rowSelection)){
6397             return;
6398         }
6399         
6400         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6401             cell = cell.findParent('td', false, true);
6402         }
6403         
6404         if(!cell || typeof(cell) == 'undefined'){
6405             return;
6406         }
6407         
6408         var row = cell.findParent('tr', false, true);
6409         
6410         if(!row || typeof(row) == 'undefined'){
6411             return;
6412         }
6413         
6414         var cellIndex = cell.dom.cellIndex;
6415         var rowIndex = this.getRowIndex(row);
6416         
6417         if(this.cellSelection){
6418             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6419         }
6420         
6421         if(this.rowSelection){
6422             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6423         }
6424     },
6425     
6426     sort : function(e,el)
6427     {
6428         var col = Roo.get(el);
6429         
6430         if(!col.hasClass('sortable')){
6431             return;
6432         }
6433         
6434         var sort = col.attr('sort');
6435         var dir = 'ASC';
6436         
6437         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6438             dir = 'DESC';
6439         }
6440         
6441         this.store.sortInfo = {field : sort, direction : dir};
6442         
6443         if (this.footer) {
6444             Roo.log("calling footer first");
6445             this.footer.onClick('first');
6446         } else {
6447         
6448             this.store.load({ params : { start : 0 } });
6449         }
6450     },
6451     
6452     renderHeader : function()
6453     {
6454         var header = {
6455             tag: 'thead',
6456             cn : []
6457         };
6458         
6459         var cm = this.cm;
6460         this.totalWidth = 0;
6461         
6462         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6463             
6464             var config = cm.config[i];
6465             
6466             var c = {
6467                 tag: 'th',
6468                 cls : 'x-hcol-' + i,
6469                 style : '',
6470                 html: cm.getColumnHeader(i)
6471             };
6472             
6473             var hh = '';
6474             
6475             if(typeof(config.sortable) != 'undefined' && config.sortable){
6476                 c.cls = 'sortable';
6477                 c.html = '<i class="glyphicon"></i>' + c.html;
6478             }
6479             
6480             if(typeof(config.lgHeader) != 'undefined'){
6481                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6482             }
6483             
6484             if(typeof(config.mdHeader) != 'undefined'){
6485                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6486             }
6487             
6488             if(typeof(config.smHeader) != 'undefined'){
6489                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6490             }
6491             
6492             if(typeof(config.xsHeader) != 'undefined'){
6493                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6494             }
6495             
6496             if(hh.length){
6497                 c.html = hh;
6498             }
6499             
6500             if(typeof(config.tooltip) != 'undefined'){
6501                 c.tooltip = config.tooltip;
6502             }
6503             
6504             if(typeof(config.colspan) != 'undefined'){
6505                 c.colspan = config.colspan;
6506             }
6507             
6508             if(typeof(config.hidden) != 'undefined' && config.hidden){
6509                 c.style += ' display:none;';
6510             }
6511             
6512             if(typeof(config.dataIndex) != 'undefined'){
6513                 c.sort = config.dataIndex;
6514             }
6515             
6516            
6517             
6518             if(typeof(config.align) != 'undefined' && config.align.length){
6519                 c.style += ' text-align:' + config.align + ';';
6520             }
6521             
6522             if(typeof(config.width) != 'undefined'){
6523                 c.style += ' width:' + config.width + 'px;';
6524                 this.totalWidth += config.width;
6525             } else {
6526                 this.totalWidth += 100; // assume minimum of 100 per column?
6527             }
6528             
6529             if(typeof(config.cls) != 'undefined'){
6530                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6531             }
6532             
6533             ['xs','sm','md','lg'].map(function(size){
6534                 
6535                 if(typeof(config[size]) == 'undefined'){
6536                     return;
6537                 }
6538                 
6539                 if (!config[size]) { // 0 = hidden
6540                     c.cls += ' hidden-' + size;
6541                     return;
6542                 }
6543                 
6544                 c.cls += ' col-' + size + '-' + config[size];
6545
6546             });
6547             
6548             header.cn.push(c)
6549         }
6550         
6551         return header;
6552     },
6553     
6554     renderBody : function()
6555     {
6556         var body = {
6557             tag: 'tbody',
6558             cn : [
6559                 {
6560                     tag: 'tr',
6561                     cn : [
6562                         {
6563                             tag : 'td',
6564                             colspan :  this.cm.getColumnCount()
6565                         }
6566                     ]
6567                 }
6568             ]
6569         };
6570         
6571         return body;
6572     },
6573     
6574     renderFooter : function()
6575     {
6576         var footer = {
6577             tag: 'tfoot',
6578             cn : [
6579                 {
6580                     tag: 'tr',
6581                     cn : [
6582                         {
6583                             tag : 'td',
6584                             colspan :  this.cm.getColumnCount()
6585                         }
6586                     ]
6587                 }
6588             ]
6589         };
6590         
6591         return footer;
6592     },
6593     
6594     
6595     
6596     onLoad : function()
6597     {
6598 //        Roo.log('ds onload');
6599         this.clear();
6600         
6601         var _this = this;
6602         var cm = this.cm;
6603         var ds = this.store;
6604         
6605         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6606             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6607             if (_this.store.sortInfo) {
6608                     
6609                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6610                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6611                 }
6612                 
6613                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6614                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6615                 }
6616             }
6617         });
6618         
6619         var tbody =  this.mainBody;
6620               
6621         if(ds.getCount() > 0){
6622             ds.data.each(function(d,rowIndex){
6623                 var row =  this.renderRow(cm, ds, rowIndex);
6624                 
6625                 tbody.createChild(row);
6626                 
6627                 var _this = this;
6628                 
6629                 if(row.cellObjects.length){
6630                     Roo.each(row.cellObjects, function(r){
6631                         _this.renderCellObject(r);
6632                     })
6633                 }
6634                 
6635             }, this);
6636         }
6637         
6638         var tfoot = this.el.select('tfoot', true).first();
6639         
6640         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6641             
6642             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6643             
6644             var total = this.ds.getTotalCount();
6645             
6646             if(this.footer.pageSize < total){
6647                 this.mainFoot.show();
6648             }
6649         }
6650         
6651         Roo.each(this.el.select('tbody td', true).elements, function(e){
6652             e.on('mouseover', _this.onMouseover, _this);
6653         });
6654         
6655         Roo.each(this.el.select('tbody td', true).elements, function(e){
6656             e.on('mouseout', _this.onMouseout, _this);
6657         });
6658         this.fireEvent('rowsrendered', this);
6659         
6660         this.autoSize();
6661     },
6662     
6663     
6664     onUpdate : function(ds,record)
6665     {
6666         this.refreshRow(record);
6667         this.autoSize();
6668     },
6669     
6670     onRemove : function(ds, record, index, isUpdate){
6671         if(isUpdate !== true){
6672             this.fireEvent("beforerowremoved", this, index, record);
6673         }
6674         var bt = this.mainBody.dom;
6675         
6676         var rows = this.el.select('tbody > tr', true).elements;
6677         
6678         if(typeof(rows[index]) != 'undefined'){
6679             bt.removeChild(rows[index].dom);
6680         }
6681         
6682 //        if(bt.rows[index]){
6683 //            bt.removeChild(bt.rows[index]);
6684 //        }
6685         
6686         if(isUpdate !== true){
6687             //this.stripeRows(index);
6688             //this.syncRowHeights(index, index);
6689             //this.layout();
6690             this.fireEvent("rowremoved", this, index, record);
6691         }
6692     },
6693     
6694     onAdd : function(ds, records, rowIndex)
6695     {
6696         //Roo.log('on Add called');
6697         // - note this does not handle multiple adding very well..
6698         var bt = this.mainBody.dom;
6699         for (var i =0 ; i < records.length;i++) {
6700             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6701             //Roo.log(records[i]);
6702             //Roo.log(this.store.getAt(rowIndex+i));
6703             this.insertRow(this.store, rowIndex + i, false);
6704             return;
6705         }
6706         
6707     },
6708     
6709     
6710     refreshRow : function(record){
6711         var ds = this.store, index;
6712         if(typeof record == 'number'){
6713             index = record;
6714             record = ds.getAt(index);
6715         }else{
6716             index = ds.indexOf(record);
6717         }
6718         this.insertRow(ds, index, true);
6719         this.autoSize();
6720         this.onRemove(ds, record, index+1, true);
6721         this.autoSize();
6722         //this.syncRowHeights(index, index);
6723         //this.layout();
6724         this.fireEvent("rowupdated", this, index, record);
6725     },
6726     
6727     insertRow : function(dm, rowIndex, isUpdate){
6728         
6729         if(!isUpdate){
6730             this.fireEvent("beforerowsinserted", this, rowIndex);
6731         }
6732             //var s = this.getScrollState();
6733         var row = this.renderRow(this.cm, this.store, rowIndex);
6734         // insert before rowIndex..
6735         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6736         
6737         var _this = this;
6738                 
6739         if(row.cellObjects.length){
6740             Roo.each(row.cellObjects, function(r){
6741                 _this.renderCellObject(r);
6742             })
6743         }
6744             
6745         if(!isUpdate){
6746             this.fireEvent("rowsinserted", this, rowIndex);
6747             //this.syncRowHeights(firstRow, lastRow);
6748             //this.stripeRows(firstRow);
6749             //this.layout();
6750         }
6751         
6752     },
6753     
6754     
6755     getRowDom : function(rowIndex)
6756     {
6757         var rows = this.el.select('tbody > tr', true).elements;
6758         
6759         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6760         
6761     },
6762     // returns the object tree for a tr..
6763   
6764     
6765     renderRow : function(cm, ds, rowIndex) 
6766     {
6767         var d = ds.getAt(rowIndex);
6768         
6769         var row = {
6770             tag : 'tr',
6771             cls : 'x-row-' + rowIndex,
6772             cn : []
6773         };
6774             
6775         var cellObjects = [];
6776         
6777         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6778             var config = cm.config[i];
6779             
6780             var renderer = cm.getRenderer(i);
6781             var value = '';
6782             var id = false;
6783             
6784             if(typeof(renderer) !== 'undefined'){
6785                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6786             }
6787             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6788             // and are rendered into the cells after the row is rendered - using the id for the element.
6789             
6790             if(typeof(value) === 'object'){
6791                 id = Roo.id();
6792                 cellObjects.push({
6793                     container : id,
6794                     cfg : value 
6795                 })
6796             }
6797             
6798             var rowcfg = {
6799                 record: d,
6800                 rowIndex : rowIndex,
6801                 colIndex : i,
6802                 rowClass : ''
6803             };
6804
6805             this.fireEvent('rowclass', this, rowcfg);
6806             
6807             var td = {
6808                 tag: 'td',
6809                 cls : rowcfg.rowClass + ' x-col-' + i,
6810                 style: '',
6811                 html: (typeof(value) === 'object') ? '' : value
6812             };
6813             
6814             if (id) {
6815                 td.id = id;
6816             }
6817             
6818             if(typeof(config.colspan) != 'undefined'){
6819                 td.colspan = config.colspan;
6820             }
6821             
6822             if(typeof(config.hidden) != 'undefined' && config.hidden){
6823                 td.style += ' display:none;';
6824             }
6825             
6826             if(typeof(config.align) != 'undefined' && config.align.length){
6827                 td.style += ' text-align:' + config.align + ';';
6828             }
6829             if(typeof(config.valign) != 'undefined' && config.valign.length){
6830                 td.style += ' vertical-align:' + config.valign + ';';
6831             }
6832             
6833             if(typeof(config.width) != 'undefined'){
6834                 td.style += ' width:' +  config.width + 'px;';
6835             }
6836             
6837             if(typeof(config.cursor) != 'undefined'){
6838                 td.style += ' cursor:' +  config.cursor + ';';
6839             }
6840             
6841             if(typeof(config.cls) != 'undefined'){
6842                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6843             }
6844             
6845             ['xs','sm','md','lg'].map(function(size){
6846                 
6847                 if(typeof(config[size]) == 'undefined'){
6848                     return;
6849                 }
6850                 
6851                 if (!config[size]) { // 0 = hidden
6852                     td.cls += ' hidden-' + size;
6853                     return;
6854                 }
6855                 
6856                 td.cls += ' col-' + size + '-' + config[size];
6857
6858             });
6859             
6860             row.cn.push(td);
6861            
6862         }
6863         
6864         row.cellObjects = cellObjects;
6865         
6866         return row;
6867           
6868     },
6869     
6870     
6871     
6872     onBeforeLoad : function()
6873     {
6874         
6875     },
6876      /**
6877      * Remove all rows
6878      */
6879     clear : function()
6880     {
6881         this.el.select('tbody', true).first().dom.innerHTML = '';
6882     },
6883     /**
6884      * Show or hide a row.
6885      * @param {Number} rowIndex to show or hide
6886      * @param {Boolean} state hide
6887      */
6888     setRowVisibility : function(rowIndex, state)
6889     {
6890         var bt = this.mainBody.dom;
6891         
6892         var rows = this.el.select('tbody > tr', true).elements;
6893         
6894         if(typeof(rows[rowIndex]) == 'undefined'){
6895             return;
6896         }
6897         rows[rowIndex].dom.style.display = state ? '' : 'none';
6898     },
6899     
6900     
6901     getSelectionModel : function(){
6902         if(!this.selModel){
6903             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6904         }
6905         return this.selModel;
6906     },
6907     /*
6908      * Render the Roo.bootstrap object from renderder
6909      */
6910     renderCellObject : function(r)
6911     {
6912         var _this = this;
6913         
6914         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6915         
6916         var t = r.cfg.render(r.container);
6917         
6918         if(r.cfg.cn){
6919             Roo.each(r.cfg.cn, function(c){
6920                 var child = {
6921                     container: t.getChildContainer(),
6922                     cfg: c
6923                 };
6924                 _this.renderCellObject(child);
6925             })
6926         }
6927     },
6928     
6929     getRowIndex : function(row)
6930     {
6931         var rowIndex = -1;
6932         
6933         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6934             if(el != row){
6935                 return;
6936             }
6937             
6938             rowIndex = index;
6939         });
6940         
6941         return rowIndex;
6942     },
6943      /**
6944      * Returns the grid's underlying element = used by panel.Grid
6945      * @return {Element} The element
6946      */
6947     getGridEl : function(){
6948         return this.el;
6949     },
6950      /**
6951      * Forces a resize - used by panel.Grid
6952      * @return {Element} The element
6953      */
6954     autoSize : function()
6955     {
6956         //var ctr = Roo.get(this.container.dom.parentElement);
6957         var ctr = Roo.get(this.el.dom);
6958         
6959         var thd = this.getGridEl().select('thead',true).first();
6960         var tbd = this.getGridEl().select('tbody', true).first();
6961         var tfd = this.getGridEl().select('tfoot', true).first();
6962         
6963         var cw = ctr.getWidth();
6964         
6965         if (tbd) {
6966             
6967             tbd.setSize(ctr.getWidth(),
6968                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6969             );
6970             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6971             cw -= barsize;
6972         }
6973         cw = Math.max(cw, this.totalWidth);
6974         this.getGridEl().select('tr',true).setWidth(cw);
6975         // resize 'expandable coloumn?
6976         
6977         return; // we doe not have a view in this design..
6978         
6979     },
6980     onBodyScroll: function()
6981     {
6982         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6983         if(this.mainHead){
6984             this.mainHead.setStyle({
6985                 'position' : 'relative',
6986                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6987             });
6988         }
6989         
6990         if(this.lazyLoad){
6991             
6992             var scrollHeight = this.mainBody.dom.scrollHeight;
6993             
6994             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6995             
6996             var height = this.mainBody.getHeight();
6997             
6998             if(scrollHeight - height == scrollTop) {
6999                 
7000                 var total = this.ds.getTotalCount();
7001                 
7002                 if(this.footer.cursor + this.footer.pageSize < total){
7003                     
7004                     this.footer.ds.load({
7005                         params : {
7006                             start : this.footer.cursor + this.footer.pageSize,
7007                             limit : this.footer.pageSize
7008                         },
7009                         add : true
7010                     });
7011                 }
7012             }
7013             
7014         }
7015     },
7016     
7017     onHeaderChange : function()
7018     {
7019         var header = this.renderHeader();
7020         var table = this.el.select('table', true).first();
7021         
7022         this.mainHead.remove();
7023         this.mainHead = table.createChild(header, this.mainBody, false);
7024     },
7025     
7026     onHiddenChange : function(colModel, colIndex, hidden)
7027     {
7028         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7029         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7030         
7031         this.CSS.updateRule(thSelector, "display", "");
7032         this.CSS.updateRule(tdSelector, "display", "");
7033         
7034         if(hidden){
7035             this.CSS.updateRule(thSelector, "display", "none");
7036             this.CSS.updateRule(tdSelector, "display", "none");
7037         }
7038         
7039         this.onHeaderChange();
7040         this.onLoad();
7041         
7042     }
7043     
7044 });
7045
7046  
7047
7048  /*
7049  * - LGPL
7050  *
7051  * table cell
7052  * 
7053  */
7054
7055 /**
7056  * @class Roo.bootstrap.TableCell
7057  * @extends Roo.bootstrap.Component
7058  * Bootstrap TableCell class
7059  * @cfg {String} html cell contain text
7060  * @cfg {String} cls cell class
7061  * @cfg {String} tag cell tag (td|th) default td
7062  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7063  * @cfg {String} align Aligns the content in a cell
7064  * @cfg {String} axis Categorizes cells
7065  * @cfg {String} bgcolor Specifies the background color of a cell
7066  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7067  * @cfg {Number} colspan Specifies the number of columns a cell should span
7068  * @cfg {String} headers Specifies one or more header cells a cell is related to
7069  * @cfg {Number} height Sets the height of a cell
7070  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7071  * @cfg {Number} rowspan Sets the number of rows a cell should span
7072  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7073  * @cfg {String} valign Vertical aligns the content in a cell
7074  * @cfg {Number} width Specifies the width of a cell
7075  * 
7076  * @constructor
7077  * Create a new TableCell
7078  * @param {Object} config The config object
7079  */
7080
7081 Roo.bootstrap.TableCell = function(config){
7082     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7083 };
7084
7085 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7086     
7087     html: false,
7088     cls: false,
7089     tag: false,
7090     abbr: false,
7091     align: false,
7092     axis: false,
7093     bgcolor: false,
7094     charoff: false,
7095     colspan: false,
7096     headers: false,
7097     height: false,
7098     nowrap: false,
7099     rowspan: false,
7100     scope: false,
7101     valign: false,
7102     width: false,
7103     
7104     
7105     getAutoCreate : function(){
7106         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7107         
7108         cfg = {
7109             tag: 'td'
7110         };
7111         
7112         if(this.tag){
7113             cfg.tag = this.tag;
7114         }
7115         
7116         if (this.html) {
7117             cfg.html=this.html
7118         }
7119         if (this.cls) {
7120             cfg.cls=this.cls
7121         }
7122         if (this.abbr) {
7123             cfg.abbr=this.abbr
7124         }
7125         if (this.align) {
7126             cfg.align=this.align
7127         }
7128         if (this.axis) {
7129             cfg.axis=this.axis
7130         }
7131         if (this.bgcolor) {
7132             cfg.bgcolor=this.bgcolor
7133         }
7134         if (this.charoff) {
7135             cfg.charoff=this.charoff
7136         }
7137         if (this.colspan) {
7138             cfg.colspan=this.colspan
7139         }
7140         if (this.headers) {
7141             cfg.headers=this.headers
7142         }
7143         if (this.height) {
7144             cfg.height=this.height
7145         }
7146         if (this.nowrap) {
7147             cfg.nowrap=this.nowrap
7148         }
7149         if (this.rowspan) {
7150             cfg.rowspan=this.rowspan
7151         }
7152         if (this.scope) {
7153             cfg.scope=this.scope
7154         }
7155         if (this.valign) {
7156             cfg.valign=this.valign
7157         }
7158         if (this.width) {
7159             cfg.width=this.width
7160         }
7161         
7162         
7163         return cfg;
7164     }
7165    
7166 });
7167
7168  
7169
7170  /*
7171  * - LGPL
7172  *
7173  * table row
7174  * 
7175  */
7176
7177 /**
7178  * @class Roo.bootstrap.TableRow
7179  * @extends Roo.bootstrap.Component
7180  * Bootstrap TableRow class
7181  * @cfg {String} cls row class
7182  * @cfg {String} align Aligns the content in a table row
7183  * @cfg {String} bgcolor Specifies a background color for a table row
7184  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7185  * @cfg {String} valign Vertical aligns the content in a table row
7186  * 
7187  * @constructor
7188  * Create a new TableRow
7189  * @param {Object} config The config object
7190  */
7191
7192 Roo.bootstrap.TableRow = function(config){
7193     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7194 };
7195
7196 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7197     
7198     cls: false,
7199     align: false,
7200     bgcolor: false,
7201     charoff: false,
7202     valign: false,
7203     
7204     getAutoCreate : function(){
7205         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7206         
7207         cfg = {
7208             tag: 'tr'
7209         };
7210             
7211         if(this.cls){
7212             cfg.cls = this.cls;
7213         }
7214         if(this.align){
7215             cfg.align = this.align;
7216         }
7217         if(this.bgcolor){
7218             cfg.bgcolor = this.bgcolor;
7219         }
7220         if(this.charoff){
7221             cfg.charoff = this.charoff;
7222         }
7223         if(this.valign){
7224             cfg.valign = this.valign;
7225         }
7226         
7227         return cfg;
7228     }
7229    
7230 });
7231
7232  
7233
7234  /*
7235  * - LGPL
7236  *
7237  * table body
7238  * 
7239  */
7240
7241 /**
7242  * @class Roo.bootstrap.TableBody
7243  * @extends Roo.bootstrap.Component
7244  * Bootstrap TableBody class
7245  * @cfg {String} cls element class
7246  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7247  * @cfg {String} align Aligns the content inside the element
7248  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7249  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7250  * 
7251  * @constructor
7252  * Create a new TableBody
7253  * @param {Object} config The config object
7254  */
7255
7256 Roo.bootstrap.TableBody = function(config){
7257     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7258 };
7259
7260 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7261     
7262     cls: false,
7263     tag: false,
7264     align: false,
7265     charoff: false,
7266     valign: false,
7267     
7268     getAutoCreate : function(){
7269         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7270         
7271         cfg = {
7272             tag: 'tbody'
7273         };
7274             
7275         if (this.cls) {
7276             cfg.cls=this.cls
7277         }
7278         if(this.tag){
7279             cfg.tag = this.tag;
7280         }
7281         
7282         if(this.align){
7283             cfg.align = this.align;
7284         }
7285         if(this.charoff){
7286             cfg.charoff = this.charoff;
7287         }
7288         if(this.valign){
7289             cfg.valign = this.valign;
7290         }
7291         
7292         return cfg;
7293     }
7294     
7295     
7296 //    initEvents : function()
7297 //    {
7298 //        
7299 //        if(!this.store){
7300 //            return;
7301 //        }
7302 //        
7303 //        this.store = Roo.factory(this.store, Roo.data);
7304 //        this.store.on('load', this.onLoad, this);
7305 //        
7306 //        this.store.load();
7307 //        
7308 //    },
7309 //    
7310 //    onLoad: function () 
7311 //    {   
7312 //        this.fireEvent('load', this);
7313 //    }
7314 //    
7315 //   
7316 });
7317
7318  
7319
7320  /*
7321  * Based on:
7322  * Ext JS Library 1.1.1
7323  * Copyright(c) 2006-2007, Ext JS, LLC.
7324  *
7325  * Originally Released Under LGPL - original licence link has changed is not relivant.
7326  *
7327  * Fork - LGPL
7328  * <script type="text/javascript">
7329  */
7330
7331 // as we use this in bootstrap.
7332 Roo.namespace('Roo.form');
7333  /**
7334  * @class Roo.form.Action
7335  * Internal Class used to handle form actions
7336  * @constructor
7337  * @param {Roo.form.BasicForm} el The form element or its id
7338  * @param {Object} config Configuration options
7339  */
7340
7341  
7342  
7343 // define the action interface
7344 Roo.form.Action = function(form, options){
7345     this.form = form;
7346     this.options = options || {};
7347 };
7348 /**
7349  * Client Validation Failed
7350  * @const 
7351  */
7352 Roo.form.Action.CLIENT_INVALID = 'client';
7353 /**
7354  * Server Validation Failed
7355  * @const 
7356  */
7357 Roo.form.Action.SERVER_INVALID = 'server';
7358  /**
7359  * Connect to Server Failed
7360  * @const 
7361  */
7362 Roo.form.Action.CONNECT_FAILURE = 'connect';
7363 /**
7364  * Reading Data from Server Failed
7365  * @const 
7366  */
7367 Roo.form.Action.LOAD_FAILURE = 'load';
7368
7369 Roo.form.Action.prototype = {
7370     type : 'default',
7371     failureType : undefined,
7372     response : undefined,
7373     result : undefined,
7374
7375     // interface method
7376     run : function(options){
7377
7378     },
7379
7380     // interface method
7381     success : function(response){
7382
7383     },
7384
7385     // interface method
7386     handleResponse : function(response){
7387
7388     },
7389
7390     // default connection failure
7391     failure : function(response){
7392         
7393         this.response = response;
7394         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7395         this.form.afterAction(this, false);
7396     },
7397
7398     processResponse : function(response){
7399         this.response = response;
7400         if(!response.responseText){
7401             return true;
7402         }
7403         this.result = this.handleResponse(response);
7404         return this.result;
7405     },
7406
7407     // utility functions used internally
7408     getUrl : function(appendParams){
7409         var url = this.options.url || this.form.url || this.form.el.dom.action;
7410         if(appendParams){
7411             var p = this.getParams();
7412             if(p){
7413                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7414             }
7415         }
7416         return url;
7417     },
7418
7419     getMethod : function(){
7420         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7421     },
7422
7423     getParams : function(){
7424         var bp = this.form.baseParams;
7425         var p = this.options.params;
7426         if(p){
7427             if(typeof p == "object"){
7428                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7429             }else if(typeof p == 'string' && bp){
7430                 p += '&' + Roo.urlEncode(bp);
7431             }
7432         }else if(bp){
7433             p = Roo.urlEncode(bp);
7434         }
7435         return p;
7436     },
7437
7438     createCallback : function(){
7439         return {
7440             success: this.success,
7441             failure: this.failure,
7442             scope: this,
7443             timeout: (this.form.timeout*1000),
7444             upload: this.form.fileUpload ? this.success : undefined
7445         };
7446     }
7447 };
7448
7449 Roo.form.Action.Submit = function(form, options){
7450     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7451 };
7452
7453 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7454     type : 'submit',
7455
7456     haveProgress : false,
7457     uploadComplete : false,
7458     
7459     // uploadProgress indicator.
7460     uploadProgress : function()
7461     {
7462         if (!this.form.progressUrl) {
7463             return;
7464         }
7465         
7466         if (!this.haveProgress) {
7467             Roo.MessageBox.progress("Uploading", "Uploading");
7468         }
7469         if (this.uploadComplete) {
7470            Roo.MessageBox.hide();
7471            return;
7472         }
7473         
7474         this.haveProgress = true;
7475    
7476         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7477         
7478         var c = new Roo.data.Connection();
7479         c.request({
7480             url : this.form.progressUrl,
7481             params: {
7482                 id : uid
7483             },
7484             method: 'GET',
7485             success : function(req){
7486                //console.log(data);
7487                 var rdata = false;
7488                 var edata;
7489                 try  {
7490                    rdata = Roo.decode(req.responseText)
7491                 } catch (e) {
7492                     Roo.log("Invalid data from server..");
7493                     Roo.log(edata);
7494                     return;
7495                 }
7496                 if (!rdata || !rdata.success) {
7497                     Roo.log(rdata);
7498                     Roo.MessageBox.alert(Roo.encode(rdata));
7499                     return;
7500                 }
7501                 var data = rdata.data;
7502                 
7503                 if (this.uploadComplete) {
7504                    Roo.MessageBox.hide();
7505                    return;
7506                 }
7507                    
7508                 if (data){
7509                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7510                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7511                     );
7512                 }
7513                 this.uploadProgress.defer(2000,this);
7514             },
7515        
7516             failure: function(data) {
7517                 Roo.log('progress url failed ');
7518                 Roo.log(data);
7519             },
7520             scope : this
7521         });
7522            
7523     },
7524     
7525     
7526     run : function()
7527     {
7528         // run get Values on the form, so it syncs any secondary forms.
7529         this.form.getValues();
7530         
7531         var o = this.options;
7532         var method = this.getMethod();
7533         var isPost = method == 'POST';
7534         if(o.clientValidation === false || this.form.isValid()){
7535             
7536             if (this.form.progressUrl) {
7537                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7538                     (new Date() * 1) + '' + Math.random());
7539                     
7540             } 
7541             
7542             
7543             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7544                 form:this.form.el.dom,
7545                 url:this.getUrl(!isPost),
7546                 method: method,
7547                 params:isPost ? this.getParams() : null,
7548                 isUpload: this.form.fileUpload
7549             }));
7550             
7551             this.uploadProgress();
7552
7553         }else if (o.clientValidation !== false){ // client validation failed
7554             this.failureType = Roo.form.Action.CLIENT_INVALID;
7555             this.form.afterAction(this, false);
7556         }
7557     },
7558
7559     success : function(response)
7560     {
7561         this.uploadComplete= true;
7562         if (this.haveProgress) {
7563             Roo.MessageBox.hide();
7564         }
7565         
7566         
7567         var result = this.processResponse(response);
7568         if(result === true || result.success){
7569             this.form.afterAction(this, true);
7570             return;
7571         }
7572         if(result.errors){
7573             this.form.markInvalid(result.errors);
7574             this.failureType = Roo.form.Action.SERVER_INVALID;
7575         }
7576         this.form.afterAction(this, false);
7577     },
7578     failure : function(response)
7579     {
7580         this.uploadComplete= true;
7581         if (this.haveProgress) {
7582             Roo.MessageBox.hide();
7583         }
7584         
7585         this.response = response;
7586         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7587         this.form.afterAction(this, false);
7588     },
7589     
7590     handleResponse : function(response){
7591         if(this.form.errorReader){
7592             var rs = this.form.errorReader.read(response);
7593             var errors = [];
7594             if(rs.records){
7595                 for(var i = 0, len = rs.records.length; i < len; i++) {
7596                     var r = rs.records[i];
7597                     errors[i] = r.data;
7598                 }
7599             }
7600             if(errors.length < 1){
7601                 errors = null;
7602             }
7603             return {
7604                 success : rs.success,
7605                 errors : errors
7606             };
7607         }
7608         var ret = false;
7609         try {
7610             ret = Roo.decode(response.responseText);
7611         } catch (e) {
7612             ret = {
7613                 success: false,
7614                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7615                 errors : []
7616             };
7617         }
7618         return ret;
7619         
7620     }
7621 });
7622
7623
7624 Roo.form.Action.Load = function(form, options){
7625     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7626     this.reader = this.form.reader;
7627 };
7628
7629 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7630     type : 'load',
7631
7632     run : function(){
7633         
7634         Roo.Ajax.request(Roo.apply(
7635                 this.createCallback(), {
7636                     method:this.getMethod(),
7637                     url:this.getUrl(false),
7638                     params:this.getParams()
7639         }));
7640     },
7641
7642     success : function(response){
7643         
7644         var result = this.processResponse(response);
7645         if(result === true || !result.success || !result.data){
7646             this.failureType = Roo.form.Action.LOAD_FAILURE;
7647             this.form.afterAction(this, false);
7648             return;
7649         }
7650         this.form.clearInvalid();
7651         this.form.setValues(result.data);
7652         this.form.afterAction(this, true);
7653     },
7654
7655     handleResponse : function(response){
7656         if(this.form.reader){
7657             var rs = this.form.reader.read(response);
7658             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7659             return {
7660                 success : rs.success,
7661                 data : data
7662             };
7663         }
7664         return Roo.decode(response.responseText);
7665     }
7666 });
7667
7668 Roo.form.Action.ACTION_TYPES = {
7669     'load' : Roo.form.Action.Load,
7670     'submit' : Roo.form.Action.Submit
7671 };/*
7672  * - LGPL
7673  *
7674  * form
7675  *
7676  */
7677
7678 /**
7679  * @class Roo.bootstrap.Form
7680  * @extends Roo.bootstrap.Component
7681  * Bootstrap Form class
7682  * @cfg {String} method  GET | POST (default POST)
7683  * @cfg {String} labelAlign top | left (default top)
7684  * @cfg {String} align left  | right - for navbars
7685  * @cfg {Boolean} loadMask load mask when submit (default true)
7686
7687  *
7688  * @constructor
7689  * Create a new Form
7690  * @param {Object} config The config object
7691  */
7692
7693
7694 Roo.bootstrap.Form = function(config){
7695     
7696     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7697     
7698     Roo.bootstrap.Form.popover.apply();
7699     
7700     this.addEvents({
7701         /**
7702          * @event clientvalidation
7703          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7704          * @param {Form} this
7705          * @param {Boolean} valid true if the form has passed client-side validation
7706          */
7707         clientvalidation: true,
7708         /**
7709          * @event beforeaction
7710          * Fires before any action is performed. Return false to cancel the action.
7711          * @param {Form} this
7712          * @param {Action} action The action to be performed
7713          */
7714         beforeaction: true,
7715         /**
7716          * @event actionfailed
7717          * Fires when an action fails.
7718          * @param {Form} this
7719          * @param {Action} action The action that failed
7720          */
7721         actionfailed : true,
7722         /**
7723          * @event actioncomplete
7724          * Fires when an action is completed.
7725          * @param {Form} this
7726          * @param {Action} action The action that completed
7727          */
7728         actioncomplete : true
7729     });
7730 };
7731
7732 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7733
7734      /**
7735      * @cfg {String} method
7736      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7737      */
7738     method : 'POST',
7739     /**
7740      * @cfg {String} url
7741      * The URL to use for form actions if one isn't supplied in the action options.
7742      */
7743     /**
7744      * @cfg {Boolean} fileUpload
7745      * Set to true if this form is a file upload.
7746      */
7747
7748     /**
7749      * @cfg {Object} baseParams
7750      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7751      */
7752
7753     /**
7754      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7755      */
7756     timeout: 30,
7757     /**
7758      * @cfg {Sting} align (left|right) for navbar forms
7759      */
7760     align : 'left',
7761
7762     // private
7763     activeAction : null,
7764
7765     /**
7766      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7767      * element by passing it or its id or mask the form itself by passing in true.
7768      * @type Mixed
7769      */
7770     waitMsgTarget : false,
7771
7772     loadMask : true,
7773     
7774     /**
7775      * @cfg {Boolean} errorMask (true|false) default false
7776      */
7777     errorMask : false,
7778     
7779     /**
7780      * @cfg {Number} maskOffset Default 100
7781      */
7782     maskOffset : 100,
7783     
7784     /**
7785      * @cfg {Boolean} maskBody
7786      */
7787     maskBody : false,
7788
7789     getAutoCreate : function(){
7790
7791         var cfg = {
7792             tag: 'form',
7793             method : this.method || 'POST',
7794             id : this.id || Roo.id(),
7795             cls : ''
7796         };
7797         if (this.parent().xtype.match(/^Nav/)) {
7798             cfg.cls = 'navbar-form navbar-' + this.align;
7799
7800         }
7801
7802         if (this.labelAlign == 'left' ) {
7803             cfg.cls += ' form-horizontal';
7804         }
7805
7806
7807         return cfg;
7808     },
7809     initEvents : function()
7810     {
7811         this.el.on('submit', this.onSubmit, this);
7812         // this was added as random key presses on the form where triggering form submit.
7813         this.el.on('keypress', function(e) {
7814             if (e.getCharCode() != 13) {
7815                 return true;
7816             }
7817             // we might need to allow it for textareas.. and some other items.
7818             // check e.getTarget().
7819
7820             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7821                 return true;
7822             }
7823
7824             Roo.log("keypress blocked");
7825
7826             e.preventDefault();
7827             return false;
7828         });
7829         
7830     },
7831     // private
7832     onSubmit : function(e){
7833         e.stopEvent();
7834     },
7835
7836      /**
7837      * Returns true if client-side validation on the form is successful.
7838      * @return Boolean
7839      */
7840     isValid : function(){
7841         var items = this.getItems();
7842         var valid = true;
7843         var target = false;
7844         
7845         items.each(function(f){
7846             
7847             if(f.validate()){
7848                 return;
7849             }
7850             
7851             Roo.log('invalid field: ' + f.name);
7852             
7853             valid = false;
7854
7855             if(!target && f.el.isVisible(true)){
7856                 target = f;
7857             }
7858            
7859         });
7860         
7861         if(this.errorMask && !valid){
7862             Roo.bootstrap.Form.popover.mask(this, target);
7863         }
7864         
7865         return valid;
7866     },
7867     
7868     /**
7869      * Returns true if any fields in this form have changed since their original load.
7870      * @return Boolean
7871      */
7872     isDirty : function(){
7873         var dirty = false;
7874         var items = this.getItems();
7875         items.each(function(f){
7876            if(f.isDirty()){
7877                dirty = true;
7878                return false;
7879            }
7880            return true;
7881         });
7882         return dirty;
7883     },
7884      /**
7885      * Performs a predefined action (submit or load) or custom actions you define on this form.
7886      * @param {String} actionName The name of the action type
7887      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7888      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7889      * accept other config options):
7890      * <pre>
7891 Property          Type             Description
7892 ----------------  ---------------  ----------------------------------------------------------------------------------
7893 url               String           The url for the action (defaults to the form's url)
7894 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7895 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7896 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7897                                    validate the form on the client (defaults to false)
7898      * </pre>
7899      * @return {BasicForm} this
7900      */
7901     doAction : function(action, options){
7902         if(typeof action == 'string'){
7903             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7904         }
7905         if(this.fireEvent('beforeaction', this, action) !== false){
7906             this.beforeAction(action);
7907             action.run.defer(100, action);
7908         }
7909         return this;
7910     },
7911
7912     // private
7913     beforeAction : function(action){
7914         var o = action.options;
7915         
7916         if(this.loadMask){
7917             
7918             if(this.maskBody){
7919                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7920             } else {
7921                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7922             }
7923         }
7924         // not really supported yet.. ??
7925
7926         //if(this.waitMsgTarget === true){
7927         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7928         //}else if(this.waitMsgTarget){
7929         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7930         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7931         //}else {
7932         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7933        // }
7934
7935     },
7936
7937     // private
7938     afterAction : function(action, success){
7939         this.activeAction = null;
7940         var o = action.options;
7941
7942         if(this.loadMask){
7943             
7944             if(this.maskBody){
7945                 Roo.get(document.body).unmask();
7946             } else {
7947                 this.el.unmask();
7948             }
7949         }
7950         
7951         //if(this.waitMsgTarget === true){
7952 //            this.el.unmask();
7953         //}else if(this.waitMsgTarget){
7954         //    this.waitMsgTarget.unmask();
7955         //}else{
7956         //    Roo.MessageBox.updateProgress(1);
7957         //    Roo.MessageBox.hide();
7958        // }
7959         //
7960         if(success){
7961             if(o.reset){
7962                 this.reset();
7963             }
7964             Roo.callback(o.success, o.scope, [this, action]);
7965             this.fireEvent('actioncomplete', this, action);
7966
7967         }else{
7968
7969             // failure condition..
7970             // we have a scenario where updates need confirming.
7971             // eg. if a locking scenario exists..
7972             // we look for { errors : { needs_confirm : true }} in the response.
7973             if (
7974                 (typeof(action.result) != 'undefined')  &&
7975                 (typeof(action.result.errors) != 'undefined')  &&
7976                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7977            ){
7978                 var _t = this;
7979                 Roo.log("not supported yet");
7980                  /*
7981
7982                 Roo.MessageBox.confirm(
7983                     "Change requires confirmation",
7984                     action.result.errorMsg,
7985                     function(r) {
7986                         if (r != 'yes') {
7987                             return;
7988                         }
7989                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7990                     }
7991
7992                 );
7993                 */
7994
7995
7996                 return;
7997             }
7998
7999             Roo.callback(o.failure, o.scope, [this, action]);
8000             // show an error message if no failed handler is set..
8001             if (!this.hasListener('actionfailed')) {
8002                 Roo.log("need to add dialog support");
8003                 /*
8004                 Roo.MessageBox.alert("Error",
8005                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8006                         action.result.errorMsg :
8007                         "Saving Failed, please check your entries or try again"
8008                 );
8009                 */
8010             }
8011
8012             this.fireEvent('actionfailed', this, action);
8013         }
8014
8015     },
8016     /**
8017      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8018      * @param {String} id The value to search for
8019      * @return Field
8020      */
8021     findField : function(id){
8022         var items = this.getItems();
8023         var field = items.get(id);
8024         if(!field){
8025              items.each(function(f){
8026                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8027                     field = f;
8028                     return false;
8029                 }
8030                 return true;
8031             });
8032         }
8033         return field || null;
8034     },
8035      /**
8036      * Mark fields in this form invalid in bulk.
8037      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8038      * @return {BasicForm} this
8039      */
8040     markInvalid : function(errors){
8041         if(errors instanceof Array){
8042             for(var i = 0, len = errors.length; i < len; i++){
8043                 var fieldError = errors[i];
8044                 var f = this.findField(fieldError.id);
8045                 if(f){
8046                     f.markInvalid(fieldError.msg);
8047                 }
8048             }
8049         }else{
8050             var field, id;
8051             for(id in errors){
8052                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8053                     field.markInvalid(errors[id]);
8054                 }
8055             }
8056         }
8057         //Roo.each(this.childForms || [], function (f) {
8058         //    f.markInvalid(errors);
8059         //});
8060
8061         return this;
8062     },
8063
8064     /**
8065      * Set values for fields in this form in bulk.
8066      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8067      * @return {BasicForm} this
8068      */
8069     setValues : function(values){
8070         if(values instanceof Array){ // array of objects
8071             for(var i = 0, len = values.length; i < len; i++){
8072                 var v = values[i];
8073                 var f = this.findField(v.id);
8074                 if(f){
8075                     f.setValue(v.value);
8076                     if(this.trackResetOnLoad){
8077                         f.originalValue = f.getValue();
8078                     }
8079                 }
8080             }
8081         }else{ // object hash
8082             var field, id;
8083             for(id in values){
8084                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8085
8086                     if (field.setFromData &&
8087                         field.valueField &&
8088                         field.displayField &&
8089                         // combos' with local stores can
8090                         // be queried via setValue()
8091                         // to set their value..
8092                         (field.store && !field.store.isLocal)
8093                         ) {
8094                         // it's a combo
8095                         var sd = { };
8096                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8097                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8098                         field.setFromData(sd);
8099
8100                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8101                         
8102                         field.setFromData(values);
8103                         
8104                     } else {
8105                         field.setValue(values[id]);
8106                     }
8107
8108
8109                     if(this.trackResetOnLoad){
8110                         field.originalValue = field.getValue();
8111                     }
8112                 }
8113             }
8114         }
8115
8116         //Roo.each(this.childForms || [], function (f) {
8117         //    f.setValues(values);
8118         //});
8119
8120         return this;
8121     },
8122
8123     /**
8124      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8125      * they are returned as an array.
8126      * @param {Boolean} asString
8127      * @return {Object}
8128      */
8129     getValues : function(asString){
8130         //if (this.childForms) {
8131             // copy values from the child forms
8132         //    Roo.each(this.childForms, function (f) {
8133         //        this.setValues(f.getValues());
8134         //    }, this);
8135         //}
8136
8137
8138
8139         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8140         if(asString === true){
8141             return fs;
8142         }
8143         return Roo.urlDecode(fs);
8144     },
8145
8146     /**
8147      * Returns the fields in this form as an object with key/value pairs.
8148      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8149      * @return {Object}
8150      */
8151     getFieldValues : function(with_hidden)
8152     {
8153         var items = this.getItems();
8154         var ret = {};
8155         items.each(function(f){
8156             
8157             if (!f.getName()) {
8158                 return;
8159             }
8160             
8161             var v = f.getValue();
8162             
8163             if (f.inputType =='radio') {
8164                 if (typeof(ret[f.getName()]) == 'undefined') {
8165                     ret[f.getName()] = ''; // empty..
8166                 }
8167
8168                 if (!f.el.dom.checked) {
8169                     return;
8170
8171                 }
8172                 v = f.el.dom.value;
8173
8174             }
8175             
8176             if(f.xtype == 'MoneyField'){
8177                 ret[f.currencyName] = f.getCurrency();
8178             }
8179
8180             // not sure if this supported any more..
8181             if ((typeof(v) == 'object') && f.getRawValue) {
8182                 v = f.getRawValue() ; // dates..
8183             }
8184             // combo boxes where name != hiddenName...
8185             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8186                 ret[f.name] = f.getRawValue();
8187             }
8188             ret[f.getName()] = v;
8189         });
8190
8191         return ret;
8192     },
8193
8194     /**
8195      * Clears all invalid messages in this form.
8196      * @return {BasicForm} this
8197      */
8198     clearInvalid : function(){
8199         var items = this.getItems();
8200
8201         items.each(function(f){
8202            f.clearInvalid();
8203         });
8204
8205         return this;
8206     },
8207
8208     /**
8209      * Resets this form.
8210      * @return {BasicForm} this
8211      */
8212     reset : function(){
8213         var items = this.getItems();
8214         items.each(function(f){
8215             f.reset();
8216         });
8217
8218         Roo.each(this.childForms || [], function (f) {
8219             f.reset();
8220         });
8221
8222
8223         return this;
8224     },
8225     
8226     getItems : function()
8227     {
8228         var r=new Roo.util.MixedCollection(false, function(o){
8229             return o.id || (o.id = Roo.id());
8230         });
8231         var iter = function(el) {
8232             if (el.inputEl) {
8233                 r.add(el);
8234             }
8235             if (!el.items) {
8236                 return;
8237             }
8238             Roo.each(el.items,function(e) {
8239                 iter(e);
8240             });
8241         };
8242
8243         iter(this);
8244         return r;
8245     },
8246     
8247     hideFields : function(items)
8248     {
8249         Roo.each(items, function(i){
8250             
8251             var f = this.findField(i);
8252             
8253             if(!f){
8254                 return;
8255             }
8256             
8257             if(f.xtype == 'DateField'){
8258                 f.setVisible(false);
8259                 return;
8260             }
8261             
8262             f.hide();
8263             
8264         }, this);
8265     },
8266     
8267     showFields : function(items)
8268     {
8269         Roo.each(items, function(i){
8270             
8271             var f = this.findField(i);
8272             
8273             if(!f){
8274                 return;
8275             }
8276             
8277             if(f.xtype == 'DateField'){
8278                 f.setVisible(true);
8279                 return;
8280             }
8281             
8282             f.show();
8283             
8284         }, this);
8285     }
8286
8287 });
8288
8289 Roo.apply(Roo.bootstrap.Form, {
8290     
8291     popover : {
8292         
8293         padding : 5,
8294         
8295         isApplied : false,
8296         
8297         isMasked : false,
8298         
8299         form : false,
8300         
8301         target : false,
8302         
8303         toolTip : false,
8304         
8305         intervalID : false,
8306         
8307         maskEl : false,
8308         
8309         apply : function()
8310         {
8311             if(this.isApplied){
8312                 return;
8313             }
8314             
8315             this.maskEl = {
8316                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8317                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8318                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8319                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8320             };
8321             
8322             this.maskEl.top.enableDisplayMode("block");
8323             this.maskEl.left.enableDisplayMode("block");
8324             this.maskEl.bottom.enableDisplayMode("block");
8325             this.maskEl.right.enableDisplayMode("block");
8326             
8327             this.toolTip = new Roo.bootstrap.Tooltip({
8328                 cls : 'roo-form-error-popover',
8329                 alignment : {
8330                     'left' : ['r-l', [-2,0], 'right'],
8331                     'right' : ['l-r', [2,0], 'left'],
8332                     'bottom' : ['tl-bl', [0,2], 'top'],
8333                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8334                 }
8335             });
8336             
8337             this.toolTip.render(Roo.get(document.body));
8338
8339             this.toolTip.el.enableDisplayMode("block");
8340             
8341             Roo.get(document.body).on('click', function(){
8342                 this.unmask();
8343             }, this);
8344             
8345             Roo.get(document.body).on('touchstart', function(){
8346                 this.unmask();
8347             }, this);
8348             
8349             this.isApplied = true
8350         },
8351         
8352         mask : function(form, target)
8353         {
8354             this.form = form;
8355             
8356             this.target = target;
8357             
8358             if(!this.form.errorMask || !target.el){
8359                 return;
8360             }
8361             
8362             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8363             
8364             Roo.log(scrollable);
8365             
8366             var ot = this.target.el.calcOffsetsTo(scrollable);
8367             
8368             var scrollTo = ot[1] - this.form.maskOffset;
8369             
8370             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8371             
8372             scrollable.scrollTo('top', scrollTo);
8373             
8374             var box = this.target.el.getBox();
8375             Roo.log(box);
8376             var zIndex = Roo.bootstrap.Modal.zIndex++;
8377
8378             
8379             this.maskEl.top.setStyle('position', 'absolute');
8380             this.maskEl.top.setStyle('z-index', zIndex);
8381             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8382             this.maskEl.top.setLeft(0);
8383             this.maskEl.top.setTop(0);
8384             this.maskEl.top.show();
8385             
8386             this.maskEl.left.setStyle('position', 'absolute');
8387             this.maskEl.left.setStyle('z-index', zIndex);
8388             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8389             this.maskEl.left.setLeft(0);
8390             this.maskEl.left.setTop(box.y - this.padding);
8391             this.maskEl.left.show();
8392
8393             this.maskEl.bottom.setStyle('position', 'absolute');
8394             this.maskEl.bottom.setStyle('z-index', zIndex);
8395             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8396             this.maskEl.bottom.setLeft(0);
8397             this.maskEl.bottom.setTop(box.bottom + this.padding);
8398             this.maskEl.bottom.show();
8399
8400             this.maskEl.right.setStyle('position', 'absolute');
8401             this.maskEl.right.setStyle('z-index', zIndex);
8402             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8403             this.maskEl.right.setLeft(box.right + this.padding);
8404             this.maskEl.right.setTop(box.y - this.padding);
8405             this.maskEl.right.show();
8406
8407             this.toolTip.bindEl = this.target.el;
8408
8409             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8410
8411             var tip = this.target.blankText;
8412
8413             if(this.target.getValue() !== '' ) {
8414                 
8415                 if (this.target.invalidText.length) {
8416                     tip = this.target.invalidText;
8417                 } else if (this.target.regexText.length){
8418                     tip = this.target.regexText;
8419                 }
8420             }
8421
8422             this.toolTip.show(tip);
8423
8424             this.intervalID = window.setInterval(function() {
8425                 Roo.bootstrap.Form.popover.unmask();
8426             }, 10000);
8427
8428             window.onwheel = function(){ return false;};
8429             
8430             (function(){ this.isMasked = true; }).defer(500, this);
8431             
8432         },
8433         
8434         unmask : function()
8435         {
8436             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8437                 return;
8438             }
8439             
8440             this.maskEl.top.setStyle('position', 'absolute');
8441             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8442             this.maskEl.top.hide();
8443
8444             this.maskEl.left.setStyle('position', 'absolute');
8445             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8446             this.maskEl.left.hide();
8447
8448             this.maskEl.bottom.setStyle('position', 'absolute');
8449             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8450             this.maskEl.bottom.hide();
8451
8452             this.maskEl.right.setStyle('position', 'absolute');
8453             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8454             this.maskEl.right.hide();
8455             
8456             this.toolTip.hide();
8457             
8458             this.toolTip.el.hide();
8459             
8460             window.onwheel = function(){ return true;};
8461             
8462             if(this.intervalID){
8463                 window.clearInterval(this.intervalID);
8464                 this.intervalID = false;
8465             }
8466             
8467             this.isMasked = false;
8468             
8469         }
8470         
8471     }
8472     
8473 });
8474
8475 /*
8476  * Based on:
8477  * Ext JS Library 1.1.1
8478  * Copyright(c) 2006-2007, Ext JS, LLC.
8479  *
8480  * Originally Released Under LGPL - original licence link has changed is not relivant.
8481  *
8482  * Fork - LGPL
8483  * <script type="text/javascript">
8484  */
8485 /**
8486  * @class Roo.form.VTypes
8487  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8488  * @singleton
8489  */
8490 Roo.form.VTypes = function(){
8491     // closure these in so they are only created once.
8492     var alpha = /^[a-zA-Z_]+$/;
8493     var alphanum = /^[a-zA-Z0-9_]+$/;
8494     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8495     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8496
8497     // All these messages and functions are configurable
8498     return {
8499         /**
8500          * The function used to validate email addresses
8501          * @param {String} value The email address
8502          */
8503         'email' : function(v){
8504             return email.test(v);
8505         },
8506         /**
8507          * The error text to display when the email validation function returns false
8508          * @type String
8509          */
8510         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8511         /**
8512          * The keystroke filter mask to be applied on email input
8513          * @type RegExp
8514          */
8515         'emailMask' : /[a-z0-9_\.\-@]/i,
8516
8517         /**
8518          * The function used to validate URLs
8519          * @param {String} value The URL
8520          */
8521         'url' : function(v){
8522             return url.test(v);
8523         },
8524         /**
8525          * The error text to display when the url validation function returns false
8526          * @type String
8527          */
8528         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8529         
8530         /**
8531          * The function used to validate alpha values
8532          * @param {String} value The value
8533          */
8534         'alpha' : function(v){
8535             return alpha.test(v);
8536         },
8537         /**
8538          * The error text to display when the alpha validation function returns false
8539          * @type String
8540          */
8541         'alphaText' : 'This field should only contain letters and _',
8542         /**
8543          * The keystroke filter mask to be applied on alpha input
8544          * @type RegExp
8545          */
8546         'alphaMask' : /[a-z_]/i,
8547
8548         /**
8549          * The function used to validate alphanumeric values
8550          * @param {String} value The value
8551          */
8552         'alphanum' : function(v){
8553             return alphanum.test(v);
8554         },
8555         /**
8556          * The error text to display when the alphanumeric validation function returns false
8557          * @type String
8558          */
8559         'alphanumText' : 'This field should only contain letters, numbers and _',
8560         /**
8561          * The keystroke filter mask to be applied on alphanumeric input
8562          * @type RegExp
8563          */
8564         'alphanumMask' : /[a-z0-9_]/i
8565     };
8566 }();/*
8567  * - LGPL
8568  *
8569  * Input
8570  * 
8571  */
8572
8573 /**
8574  * @class Roo.bootstrap.Input
8575  * @extends Roo.bootstrap.Component
8576  * Bootstrap Input class
8577  * @cfg {Boolean} disabled is it disabled
8578  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8579  * @cfg {String} name name of the input
8580  * @cfg {string} fieldLabel - the label associated
8581  * @cfg {string} placeholder - placeholder to put in text.
8582  * @cfg {string}  before - input group add on before
8583  * @cfg {string} after - input group add on after
8584  * @cfg {string} size - (lg|sm) or leave empty..
8585  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8586  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8587  * @cfg {Number} md colspan out of 12 for computer-sized screens
8588  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8589  * @cfg {string} value default value of the input
8590  * @cfg {Number} labelWidth set the width of label 
8591  * @cfg {Number} labellg set the width of label (1-12)
8592  * @cfg {Number} labelmd set the width of label (1-12)
8593  * @cfg {Number} labelsm set the width of label (1-12)
8594  * @cfg {Number} labelxs set the width of label (1-12)
8595  * @cfg {String} labelAlign (top|left)
8596  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8597  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8598  * @cfg {String} indicatorpos (left|right) default left
8599  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8600  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8601
8602  * @cfg {String} align (left|center|right) Default left
8603  * @cfg {Boolean} forceFeedback (true|false) Default false
8604  * 
8605  * @constructor
8606  * Create a new Input
8607  * @param {Object} config The config object
8608  */
8609
8610 Roo.bootstrap.Input = function(config){
8611     
8612     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8613     
8614     this.addEvents({
8615         /**
8616          * @event focus
8617          * Fires when this field receives input focus.
8618          * @param {Roo.form.Field} this
8619          */
8620         focus : true,
8621         /**
8622          * @event blur
8623          * Fires when this field loses input focus.
8624          * @param {Roo.form.Field} this
8625          */
8626         blur : true,
8627         /**
8628          * @event specialkey
8629          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8630          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8631          * @param {Roo.form.Field} this
8632          * @param {Roo.EventObject} e The event object
8633          */
8634         specialkey : true,
8635         /**
8636          * @event change
8637          * Fires just before the field blurs if the field value has changed.
8638          * @param {Roo.form.Field} this
8639          * @param {Mixed} newValue The new value
8640          * @param {Mixed} oldValue The original value
8641          */
8642         change : true,
8643         /**
8644          * @event invalid
8645          * Fires after the field has been marked as invalid.
8646          * @param {Roo.form.Field} this
8647          * @param {String} msg The validation message
8648          */
8649         invalid : true,
8650         /**
8651          * @event valid
8652          * Fires after the field has been validated with no errors.
8653          * @param {Roo.form.Field} this
8654          */
8655         valid : true,
8656          /**
8657          * @event keyup
8658          * Fires after the key up
8659          * @param {Roo.form.Field} this
8660          * @param {Roo.EventObject}  e The event Object
8661          */
8662         keyup : true
8663     });
8664 };
8665
8666 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8667      /**
8668      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8669       automatic validation (defaults to "keyup").
8670      */
8671     validationEvent : "keyup",
8672      /**
8673      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8674      */
8675     validateOnBlur : true,
8676     /**
8677      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8678      */
8679     validationDelay : 250,
8680      /**
8681      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8682      */
8683     focusClass : "x-form-focus",  // not needed???
8684     
8685        
8686     /**
8687      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8688      */
8689     invalidClass : "has-warning",
8690     
8691     /**
8692      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8693      */
8694     validClass : "has-success",
8695     
8696     /**
8697      * @cfg {Boolean} hasFeedback (true|false) default true
8698      */
8699     hasFeedback : true,
8700     
8701     /**
8702      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8703      */
8704     invalidFeedbackClass : "glyphicon-warning-sign",
8705     
8706     /**
8707      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8708      */
8709     validFeedbackClass : "glyphicon-ok",
8710     
8711     /**
8712      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8713      */
8714     selectOnFocus : false,
8715     
8716      /**
8717      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8718      */
8719     maskRe : null,
8720        /**
8721      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8722      */
8723     vtype : null,
8724     
8725       /**
8726      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8727      */
8728     disableKeyFilter : false,
8729     
8730        /**
8731      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8732      */
8733     disabled : false,
8734      /**
8735      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8736      */
8737     allowBlank : true,
8738     /**
8739      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8740      */
8741     blankText : "Please complete this mandatory field",
8742     
8743      /**
8744      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8745      */
8746     minLength : 0,
8747     /**
8748      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8749      */
8750     maxLength : Number.MAX_VALUE,
8751     /**
8752      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8753      */
8754     minLengthText : "The minimum length for this field is {0}",
8755     /**
8756      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8757      */
8758     maxLengthText : "The maximum length for this field is {0}",
8759   
8760     
8761     /**
8762      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8763      * If available, this function will be called only after the basic validators all return true, and will be passed the
8764      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8765      */
8766     validator : null,
8767     /**
8768      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8769      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8770      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8771      */
8772     regex : null,
8773     /**
8774      * @cfg {String} regexText -- Depricated - use Invalid Text
8775      */
8776     regexText : "",
8777     
8778     /**
8779      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8780      */
8781     invalidText : "",
8782     
8783     
8784     
8785     autocomplete: false,
8786     
8787     
8788     fieldLabel : '',
8789     inputType : 'text',
8790     
8791     name : false,
8792     placeholder: false,
8793     before : false,
8794     after : false,
8795     size : false,
8796     hasFocus : false,
8797     preventMark: false,
8798     isFormField : true,
8799     value : '',
8800     labelWidth : 2,
8801     labelAlign : false,
8802     readOnly : false,
8803     align : false,
8804     formatedValue : false,
8805     forceFeedback : false,
8806     
8807     indicatorpos : 'left',
8808     
8809     labellg : 0,
8810     labelmd : 0,
8811     labelsm : 0,
8812     labelxs : 0,
8813     
8814     capture : '',
8815     accept : '',
8816     
8817     parentLabelAlign : function()
8818     {
8819         var parent = this;
8820         while (parent.parent()) {
8821             parent = parent.parent();
8822             if (typeof(parent.labelAlign) !='undefined') {
8823                 return parent.labelAlign;
8824             }
8825         }
8826         return 'left';
8827         
8828     },
8829     
8830     getAutoCreate : function()
8831     {
8832         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8833         
8834         var id = Roo.id();
8835         
8836         var cfg = {};
8837         
8838         if(this.inputType != 'hidden'){
8839             cfg.cls = 'form-group' //input-group
8840         }
8841         
8842         var input =  {
8843             tag: 'input',
8844             id : id,
8845             type : this.inputType,
8846             value : this.value,
8847             cls : 'form-control',
8848             placeholder : this.placeholder || '',
8849             autocomplete : this.autocomplete || 'new-password'
8850         };
8851         
8852         if(this.capture.length){
8853             input.capture = this.capture;
8854         }
8855         
8856         if(this.accept.length){
8857             input.accept = this.accept + "/*";
8858         }
8859         
8860         if(this.align){
8861             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8862         }
8863         
8864         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8865             input.maxLength = this.maxLength;
8866         }
8867         
8868         if (this.disabled) {
8869             input.disabled=true;
8870         }
8871         
8872         if (this.readOnly) {
8873             input.readonly=true;
8874         }
8875         
8876         if (this.name) {
8877             input.name = this.name;
8878         }
8879         
8880         if (this.size) {
8881             input.cls += ' input-' + this.size;
8882         }
8883         
8884         var settings=this;
8885         ['xs','sm','md','lg'].map(function(size){
8886             if (settings[size]) {
8887                 cfg.cls += ' col-' + size + '-' + settings[size];
8888             }
8889         });
8890         
8891         var inputblock = input;
8892         
8893         var feedback = {
8894             tag: 'span',
8895             cls: 'glyphicon form-control-feedback'
8896         };
8897             
8898         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8899             
8900             inputblock = {
8901                 cls : 'has-feedback',
8902                 cn :  [
8903                     input,
8904                     feedback
8905                 ] 
8906             };  
8907         }
8908         
8909         if (this.before || this.after) {
8910             
8911             inputblock = {
8912                 cls : 'input-group',
8913                 cn :  [] 
8914             };
8915             
8916             if (this.before && typeof(this.before) == 'string') {
8917                 
8918                 inputblock.cn.push({
8919                     tag :'span',
8920                     cls : 'roo-input-before input-group-addon',
8921                     html : this.before
8922                 });
8923             }
8924             if (this.before && typeof(this.before) == 'object') {
8925                 this.before = Roo.factory(this.before);
8926                 
8927                 inputblock.cn.push({
8928                     tag :'span',
8929                     cls : 'roo-input-before input-group-' +
8930                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8931                 });
8932             }
8933             
8934             inputblock.cn.push(input);
8935             
8936             if (this.after && typeof(this.after) == 'string') {
8937                 inputblock.cn.push({
8938                     tag :'span',
8939                     cls : 'roo-input-after input-group-addon',
8940                     html : this.after
8941                 });
8942             }
8943             if (this.after && typeof(this.after) == 'object') {
8944                 this.after = Roo.factory(this.after);
8945                 
8946                 inputblock.cn.push({
8947                     tag :'span',
8948                     cls : 'roo-input-after input-group-' +
8949                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8950                 });
8951             }
8952             
8953             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8954                 inputblock.cls += ' has-feedback';
8955                 inputblock.cn.push(feedback);
8956             }
8957         };
8958         
8959         if (align ==='left' && this.fieldLabel.length) {
8960             
8961             cfg.cls += ' roo-form-group-label-left';
8962             
8963             cfg.cn = [
8964                 {
8965                     tag : 'i',
8966                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8967                     tooltip : 'This field is required'
8968                 },
8969                 {
8970                     tag: 'label',
8971                     'for' :  id,
8972                     cls : 'control-label',
8973                     html : this.fieldLabel
8974
8975                 },
8976                 {
8977                     cls : "", 
8978                     cn: [
8979                         inputblock
8980                     ]
8981                 }
8982             ];
8983             
8984             var labelCfg = cfg.cn[1];
8985             var contentCfg = cfg.cn[2];
8986             
8987             if(this.indicatorpos == 'right'){
8988                 cfg.cn = [
8989                     {
8990                         tag: 'label',
8991                         'for' :  id,
8992                         cls : 'control-label',
8993                         cn : [
8994                             {
8995                                 tag : 'span',
8996                                 html : this.fieldLabel
8997                             },
8998                             {
8999                                 tag : 'i',
9000                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9001                                 tooltip : 'This field is required'
9002                             }
9003                         ]
9004                     },
9005                     {
9006                         cls : "",
9007                         cn: [
9008                             inputblock
9009                         ]
9010                     }
9011
9012                 ];
9013                 
9014                 labelCfg = cfg.cn[0];
9015                 contentCfg = cfg.cn[1];
9016             
9017             }
9018             
9019             if(this.labelWidth > 12){
9020                 labelCfg.style = "width: " + this.labelWidth + 'px';
9021             }
9022             
9023             if(this.labelWidth < 13 && this.labelmd == 0){
9024                 this.labelmd = this.labelWidth;
9025             }
9026             
9027             if(this.labellg > 0){
9028                 labelCfg.cls += ' col-lg-' + this.labellg;
9029                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9030             }
9031             
9032             if(this.labelmd > 0){
9033                 labelCfg.cls += ' col-md-' + this.labelmd;
9034                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9035             }
9036             
9037             if(this.labelsm > 0){
9038                 labelCfg.cls += ' col-sm-' + this.labelsm;
9039                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9040             }
9041             
9042             if(this.labelxs > 0){
9043                 labelCfg.cls += ' col-xs-' + this.labelxs;
9044                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9045             }
9046             
9047             
9048         } else if ( this.fieldLabel.length) {
9049                 
9050             cfg.cn = [
9051                 {
9052                     tag : 'i',
9053                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9054                     tooltip : 'This field is required'
9055                 },
9056                 {
9057                     tag: 'label',
9058                    //cls : 'input-group-addon',
9059                     html : this.fieldLabel
9060
9061                 },
9062
9063                inputblock
9064
9065            ];
9066            
9067            if(this.indicatorpos == 'right'){
9068                 
9069                 cfg.cn = [
9070                     {
9071                         tag: 'label',
9072                        //cls : 'input-group-addon',
9073                         html : this.fieldLabel
9074
9075                     },
9076                     {
9077                         tag : 'i',
9078                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9079                         tooltip : 'This field is required'
9080                     },
9081
9082                    inputblock
9083
9084                ];
9085
9086             }
9087
9088         } else {
9089             
9090             cfg.cn = [
9091
9092                     inputblock
9093
9094             ];
9095                 
9096                 
9097         };
9098         
9099         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9100            cfg.cls += ' navbar-form';
9101         }
9102         
9103         if (this.parentType === 'NavGroup') {
9104            cfg.cls += ' navbar-form';
9105            cfg.tag = 'li';
9106         }
9107         
9108         return cfg;
9109         
9110     },
9111     /**
9112      * return the real input element.
9113      */
9114     inputEl: function ()
9115     {
9116         return this.el.select('input.form-control',true).first();
9117     },
9118     
9119     tooltipEl : function()
9120     {
9121         return this.inputEl();
9122     },
9123     
9124     indicatorEl : function()
9125     {
9126         var indicator = this.el.select('i.roo-required-indicator',true).first();
9127         
9128         if(!indicator){
9129             return false;
9130         }
9131         
9132         return indicator;
9133         
9134     },
9135     
9136     setDisabled : function(v)
9137     {
9138         var i  = this.inputEl().dom;
9139         if (!v) {
9140             i.removeAttribute('disabled');
9141             return;
9142             
9143         }
9144         i.setAttribute('disabled','true');
9145     },
9146     initEvents : function()
9147     {
9148           
9149         this.inputEl().on("keydown" , this.fireKey,  this);
9150         this.inputEl().on("focus", this.onFocus,  this);
9151         this.inputEl().on("blur", this.onBlur,  this);
9152         
9153         this.inputEl().relayEvent('keyup', this);
9154         
9155         this.indicator = this.indicatorEl();
9156         
9157         if(this.indicator){
9158             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9159         }
9160  
9161         // reference to original value for reset
9162         this.originalValue = this.getValue();
9163         //Roo.form.TextField.superclass.initEvents.call(this);
9164         if(this.validationEvent == 'keyup'){
9165             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9166             this.inputEl().on('keyup', this.filterValidation, this);
9167         }
9168         else if(this.validationEvent !== false){
9169             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9170         }
9171         
9172         if(this.selectOnFocus){
9173             this.on("focus", this.preFocus, this);
9174             
9175         }
9176         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9177             this.inputEl().on("keypress", this.filterKeys, this);
9178         } else {
9179             this.inputEl().relayEvent('keypress', this);
9180         }
9181        /* if(this.grow){
9182             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9183             this.el.on("click", this.autoSize,  this);
9184         }
9185         */
9186         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9187             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9188         }
9189         
9190         if (typeof(this.before) == 'object') {
9191             this.before.render(this.el.select('.roo-input-before',true).first());
9192         }
9193         if (typeof(this.after) == 'object') {
9194             this.after.render(this.el.select('.roo-input-after',true).first());
9195         }
9196         
9197         this.inputEl().on('change', this.onChange, this);
9198         
9199     },
9200     filterValidation : function(e){
9201         if(!e.isNavKeyPress()){
9202             this.validationTask.delay(this.validationDelay);
9203         }
9204     },
9205      /**
9206      * Validates the field value
9207      * @return {Boolean} True if the value is valid, else false
9208      */
9209     validate : function(){
9210         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9211         if(this.disabled || this.validateValue(this.getRawValue())){
9212             this.markValid();
9213             return true;
9214         }
9215         
9216         this.markInvalid();
9217         return false;
9218     },
9219     
9220     
9221     /**
9222      * Validates a value according to the field's validation rules and marks the field as invalid
9223      * if the validation fails
9224      * @param {Mixed} value The value to validate
9225      * @return {Boolean} True if the value is valid, else false
9226      */
9227     validateValue : function(value)
9228     {
9229         if(this.getVisibilityEl().hasClass('hidden')){
9230             return true;
9231         }
9232         
9233         if(value.length < 1)  { // if it's blank
9234             if(this.allowBlank){
9235                 return true;
9236             }
9237             return false;
9238         }
9239         
9240         if(value.length < this.minLength){
9241             return false;
9242         }
9243         if(value.length > this.maxLength){
9244             return false;
9245         }
9246         if(this.vtype){
9247             var vt = Roo.form.VTypes;
9248             if(!vt[this.vtype](value, this)){
9249                 return false;
9250             }
9251         }
9252         if(typeof this.validator == "function"){
9253             var msg = this.validator(value);
9254             if(msg !== true){
9255                 return false;
9256             }
9257             if (typeof(msg) == 'string') {
9258                 this.invalidText = msg;
9259             }
9260         }
9261         
9262         if(this.regex && !this.regex.test(value)){
9263             return false;
9264         }
9265         
9266         return true;
9267     },
9268     
9269      // private
9270     fireKey : function(e){
9271         //Roo.log('field ' + e.getKey());
9272         if(e.isNavKeyPress()){
9273             this.fireEvent("specialkey", this, e);
9274         }
9275     },
9276     focus : function (selectText){
9277         if(this.rendered){
9278             this.inputEl().focus();
9279             if(selectText === true){
9280                 this.inputEl().dom.select();
9281             }
9282         }
9283         return this;
9284     } ,
9285     
9286     onFocus : function(){
9287         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9288            // this.el.addClass(this.focusClass);
9289         }
9290         if(!this.hasFocus){
9291             this.hasFocus = true;
9292             this.startValue = this.getValue();
9293             this.fireEvent("focus", this);
9294         }
9295     },
9296     
9297     beforeBlur : Roo.emptyFn,
9298
9299     
9300     // private
9301     onBlur : function(){
9302         this.beforeBlur();
9303         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9304             //this.el.removeClass(this.focusClass);
9305         }
9306         this.hasFocus = false;
9307         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9308             this.validate();
9309         }
9310         var v = this.getValue();
9311         if(String(v) !== String(this.startValue)){
9312             this.fireEvent('change', this, v, this.startValue);
9313         }
9314         this.fireEvent("blur", this);
9315     },
9316     
9317     onChange : function(e)
9318     {
9319         var v = this.getValue();
9320         if(String(v) !== String(this.startValue)){
9321             this.fireEvent('change', this, v, this.startValue);
9322         }
9323         
9324     },
9325     
9326     /**
9327      * Resets the current field value to the originally loaded value and clears any validation messages
9328      */
9329     reset : function(){
9330         this.setValue(this.originalValue);
9331         this.validate();
9332     },
9333      /**
9334      * Returns the name of the field
9335      * @return {Mixed} name The name field
9336      */
9337     getName: function(){
9338         return this.name;
9339     },
9340      /**
9341      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9342      * @return {Mixed} value The field value
9343      */
9344     getValue : function(){
9345         
9346         var v = this.inputEl().getValue();
9347         
9348         return v;
9349     },
9350     /**
9351      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9352      * @return {Mixed} value The field value
9353      */
9354     getRawValue : function(){
9355         var v = this.inputEl().getValue();
9356         
9357         return v;
9358     },
9359     
9360     /**
9361      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9362      * @param {Mixed} value The value to set
9363      */
9364     setRawValue : function(v){
9365         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9366     },
9367     
9368     selectText : function(start, end){
9369         var v = this.getRawValue();
9370         if(v.length > 0){
9371             start = start === undefined ? 0 : start;
9372             end = end === undefined ? v.length : end;
9373             var d = this.inputEl().dom;
9374             if(d.setSelectionRange){
9375                 d.setSelectionRange(start, end);
9376             }else if(d.createTextRange){
9377                 var range = d.createTextRange();
9378                 range.moveStart("character", start);
9379                 range.moveEnd("character", v.length-end);
9380                 range.select();
9381             }
9382         }
9383     },
9384     
9385     /**
9386      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9387      * @param {Mixed} value The value to set
9388      */
9389     setValue : function(v){
9390         this.value = v;
9391         if(this.rendered){
9392             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9393             this.validate();
9394         }
9395     },
9396     
9397     /*
9398     processValue : function(value){
9399         if(this.stripCharsRe){
9400             var newValue = value.replace(this.stripCharsRe, '');
9401             if(newValue !== value){
9402                 this.setRawValue(newValue);
9403                 return newValue;
9404             }
9405         }
9406         return value;
9407     },
9408   */
9409     preFocus : function(){
9410         
9411         if(this.selectOnFocus){
9412             this.inputEl().dom.select();
9413         }
9414     },
9415     filterKeys : function(e){
9416         var k = e.getKey();
9417         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9418             return;
9419         }
9420         var c = e.getCharCode(), cc = String.fromCharCode(c);
9421         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9422             return;
9423         }
9424         if(!this.maskRe.test(cc)){
9425             e.stopEvent();
9426         }
9427     },
9428      /**
9429      * Clear any invalid styles/messages for this field
9430      */
9431     clearInvalid : function(){
9432         
9433         if(!this.el || this.preventMark){ // not rendered
9434             return;
9435         }
9436         
9437      
9438         this.el.removeClass(this.invalidClass);
9439         
9440         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9441             
9442             var feedback = this.el.select('.form-control-feedback', true).first();
9443             
9444             if(feedback){
9445                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9446             }
9447             
9448         }
9449         
9450         if(this.indicator){
9451             this.indicator.removeClass('visible');
9452             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9453         }
9454         
9455         this.fireEvent('valid', this);
9456     },
9457     
9458      /**
9459      * Mark this field as valid
9460      */
9461     markValid : function()
9462     {
9463         if(!this.el  || this.preventMark){ // not rendered...
9464             return;
9465         }
9466         
9467         this.el.removeClass([this.invalidClass, this.validClass]);
9468         
9469         var feedback = this.el.select('.form-control-feedback', true).first();
9470             
9471         if(feedback){
9472             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9473         }
9474         
9475         if(this.indicator){
9476             this.indicator.removeClass('visible');
9477             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9478         }
9479         
9480         if(this.disabled){
9481             return;
9482         }
9483         
9484         if(this.allowBlank && !this.getRawValue().length){
9485             return;
9486         }
9487         
9488         this.el.addClass(this.validClass);
9489         
9490         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9491             
9492             var feedback = this.el.select('.form-control-feedback', true).first();
9493             
9494             if(feedback){
9495                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9496                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9497             }
9498             
9499         }
9500         
9501         this.fireEvent('valid', this);
9502     },
9503     
9504      /**
9505      * Mark this field as invalid
9506      * @param {String} msg The validation message
9507      */
9508     markInvalid : function(msg)
9509     {
9510         if(!this.el  || this.preventMark){ // not rendered
9511             return;
9512         }
9513         
9514         this.el.removeClass([this.invalidClass, this.validClass]);
9515         
9516         var feedback = this.el.select('.form-control-feedback', true).first();
9517             
9518         if(feedback){
9519             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9520         }
9521
9522         if(this.disabled){
9523             return;
9524         }
9525         
9526         if(this.allowBlank && !this.getRawValue().length){
9527             return;
9528         }
9529         
9530         if(this.indicator){
9531             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9532             this.indicator.addClass('visible');
9533         }
9534         
9535         this.el.addClass(this.invalidClass);
9536         
9537         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9538             
9539             var feedback = this.el.select('.form-control-feedback', true).first();
9540             
9541             if(feedback){
9542                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9543                 
9544                 if(this.getValue().length || this.forceFeedback){
9545                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9546                 }
9547                 
9548             }
9549             
9550         }
9551         
9552         this.fireEvent('invalid', this, msg);
9553     },
9554     // private
9555     SafariOnKeyDown : function(event)
9556     {
9557         // this is a workaround for a password hang bug on chrome/ webkit.
9558         if (this.inputEl().dom.type != 'password') {
9559             return;
9560         }
9561         
9562         var isSelectAll = false;
9563         
9564         if(this.inputEl().dom.selectionEnd > 0){
9565             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9566         }
9567         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9568             event.preventDefault();
9569             this.setValue('');
9570             return;
9571         }
9572         
9573         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9574             
9575             event.preventDefault();
9576             // this is very hacky as keydown always get's upper case.
9577             //
9578             var cc = String.fromCharCode(event.getCharCode());
9579             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9580             
9581         }
9582     },
9583     adjustWidth : function(tag, w){
9584         tag = tag.toLowerCase();
9585         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9586             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9587                 if(tag == 'input'){
9588                     return w + 2;
9589                 }
9590                 if(tag == 'textarea'){
9591                     return w-2;
9592                 }
9593             }else if(Roo.isOpera){
9594                 if(tag == 'input'){
9595                     return w + 2;
9596                 }
9597                 if(tag == 'textarea'){
9598                     return w-2;
9599                 }
9600             }
9601         }
9602         return w;
9603     },
9604     
9605     setFieldLabel : function(v)
9606     {
9607         if(!this.rendered){
9608             return;
9609         }
9610         
9611         if(this.indicator){
9612             var ar = this.el.select('label > span',true);
9613             
9614             if (ar.elements.length) {
9615                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9616                 this.fieldLabel = v;
9617                 return;
9618             }
9619             
9620             var br = this.el.select('label',true);
9621             
9622             if(br.elements.length) {
9623                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9624                 this.fieldLabel = v;
9625                 return;
9626             }
9627             
9628             Roo.log('Cannot Found any of label > span || label in input');
9629             return;
9630         }
9631         
9632         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9633         this.fieldLabel = v;
9634         
9635         
9636     }
9637 });
9638
9639  
9640 /*
9641  * - LGPL
9642  *
9643  * Input
9644  * 
9645  */
9646
9647 /**
9648  * @class Roo.bootstrap.TextArea
9649  * @extends Roo.bootstrap.Input
9650  * Bootstrap TextArea class
9651  * @cfg {Number} cols Specifies the visible width of a text area
9652  * @cfg {Number} rows Specifies the visible number of lines in a text area
9653  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9654  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9655  * @cfg {string} html text
9656  * 
9657  * @constructor
9658  * Create a new TextArea
9659  * @param {Object} config The config object
9660  */
9661
9662 Roo.bootstrap.TextArea = function(config){
9663     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9664    
9665 };
9666
9667 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9668      
9669     cols : false,
9670     rows : 5,
9671     readOnly : false,
9672     warp : 'soft',
9673     resize : false,
9674     value: false,
9675     html: false,
9676     
9677     getAutoCreate : function(){
9678         
9679         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9680         
9681         var id = Roo.id();
9682         
9683         var cfg = {};
9684         
9685         if(this.inputType != 'hidden'){
9686             cfg.cls = 'form-group' //input-group
9687         }
9688         
9689         var input =  {
9690             tag: 'textarea',
9691             id : id,
9692             warp : this.warp,
9693             rows : this.rows,
9694             value : this.value || '',
9695             html: this.html || '',
9696             cls : 'form-control',
9697             placeholder : this.placeholder || '' 
9698             
9699         };
9700         
9701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9702             input.maxLength = this.maxLength;
9703         }
9704         
9705         if(this.resize){
9706             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9707         }
9708         
9709         if(this.cols){
9710             input.cols = this.cols;
9711         }
9712         
9713         if (this.readOnly) {
9714             input.readonly = true;
9715         }
9716         
9717         if (this.name) {
9718             input.name = this.name;
9719         }
9720         
9721         if (this.size) {
9722             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9723         }
9724         
9725         var settings=this;
9726         ['xs','sm','md','lg'].map(function(size){
9727             if (settings[size]) {
9728                 cfg.cls += ' col-' + size + '-' + settings[size];
9729             }
9730         });
9731         
9732         var inputblock = input;
9733         
9734         if(this.hasFeedback && !this.allowBlank){
9735             
9736             var feedback = {
9737                 tag: 'span',
9738                 cls: 'glyphicon form-control-feedback'
9739             };
9740
9741             inputblock = {
9742                 cls : 'has-feedback',
9743                 cn :  [
9744                     input,
9745                     feedback
9746                 ] 
9747             };  
9748         }
9749         
9750         
9751         if (this.before || this.after) {
9752             
9753             inputblock = {
9754                 cls : 'input-group',
9755                 cn :  [] 
9756             };
9757             if (this.before) {
9758                 inputblock.cn.push({
9759                     tag :'span',
9760                     cls : 'input-group-addon',
9761                     html : this.before
9762                 });
9763             }
9764             
9765             inputblock.cn.push(input);
9766             
9767             if(this.hasFeedback && !this.allowBlank){
9768                 inputblock.cls += ' has-feedback';
9769                 inputblock.cn.push(feedback);
9770             }
9771             
9772             if (this.after) {
9773                 inputblock.cn.push({
9774                     tag :'span',
9775                     cls : 'input-group-addon',
9776                     html : this.after
9777                 });
9778             }
9779             
9780         }
9781         
9782         if (align ==='left' && this.fieldLabel.length) {
9783             cfg.cn = [
9784                 {
9785                     tag: 'label',
9786                     'for' :  id,
9787                     cls : 'control-label',
9788                     html : this.fieldLabel
9789                 },
9790                 {
9791                     cls : "",
9792                     cn: [
9793                         inputblock
9794                     ]
9795                 }
9796
9797             ];
9798             
9799             if(this.labelWidth > 12){
9800                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9801             }
9802
9803             if(this.labelWidth < 13 && this.labelmd == 0){
9804                 this.labelmd = this.labelWidth;
9805             }
9806
9807             if(this.labellg > 0){
9808                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9809                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9810             }
9811
9812             if(this.labelmd > 0){
9813                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9814                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9815             }
9816
9817             if(this.labelsm > 0){
9818                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9819                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9820             }
9821
9822             if(this.labelxs > 0){
9823                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9824                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9825             }
9826             
9827         } else if ( this.fieldLabel.length) {
9828             cfg.cn = [
9829
9830                {
9831                    tag: 'label',
9832                    //cls : 'input-group-addon',
9833                    html : this.fieldLabel
9834
9835                },
9836
9837                inputblock
9838
9839            ];
9840
9841         } else {
9842
9843             cfg.cn = [
9844
9845                 inputblock
9846
9847             ];
9848                 
9849         }
9850         
9851         if (this.disabled) {
9852             input.disabled=true;
9853         }
9854         
9855         return cfg;
9856         
9857     },
9858     /**
9859      * return the real textarea element.
9860      */
9861     inputEl: function ()
9862     {
9863         return this.el.select('textarea.form-control',true).first();
9864     },
9865     
9866     /**
9867      * Clear any invalid styles/messages for this field
9868      */
9869     clearInvalid : function()
9870     {
9871         
9872         if(!this.el || this.preventMark){ // not rendered
9873             return;
9874         }
9875         
9876         var label = this.el.select('label', true).first();
9877         var icon = this.el.select('i.fa-star', true).first();
9878         
9879         if(label && icon){
9880             icon.remove();
9881         }
9882         
9883         this.el.removeClass(this.invalidClass);
9884         
9885         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9886             
9887             var feedback = this.el.select('.form-control-feedback', true).first();
9888             
9889             if(feedback){
9890                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9891             }
9892             
9893         }
9894         
9895         this.fireEvent('valid', this);
9896     },
9897     
9898      /**
9899      * Mark this field as valid
9900      */
9901     markValid : function()
9902     {
9903         if(!this.el  || this.preventMark){ // not rendered
9904             return;
9905         }
9906         
9907         this.el.removeClass([this.invalidClass, this.validClass]);
9908         
9909         var feedback = this.el.select('.form-control-feedback', true).first();
9910             
9911         if(feedback){
9912             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9913         }
9914
9915         if(this.disabled || this.allowBlank){
9916             return;
9917         }
9918         
9919         var label = this.el.select('label', true).first();
9920         var icon = this.el.select('i.fa-star', true).first();
9921         
9922         if(label && icon){
9923             icon.remove();
9924         }
9925         
9926         this.el.addClass(this.validClass);
9927         
9928         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9929             
9930             var feedback = this.el.select('.form-control-feedback', true).first();
9931             
9932             if(feedback){
9933                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9934                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9935             }
9936             
9937         }
9938         
9939         this.fireEvent('valid', this);
9940     },
9941     
9942      /**
9943      * Mark this field as invalid
9944      * @param {String} msg The validation message
9945      */
9946     markInvalid : function(msg)
9947     {
9948         if(!this.el  || this.preventMark){ // not rendered
9949             return;
9950         }
9951         
9952         this.el.removeClass([this.invalidClass, this.validClass]);
9953         
9954         var feedback = this.el.select('.form-control-feedback', true).first();
9955             
9956         if(feedback){
9957             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9958         }
9959
9960         if(this.disabled || this.allowBlank){
9961             return;
9962         }
9963         
9964         var label = this.el.select('label', true).first();
9965         var icon = this.el.select('i.fa-star', true).first();
9966         
9967         if(!this.getValue().length && label && !icon){
9968             this.el.createChild({
9969                 tag : 'i',
9970                 cls : 'text-danger fa fa-lg fa-star',
9971                 tooltip : 'This field is required',
9972                 style : 'margin-right:5px;'
9973             }, label, true);
9974         }
9975
9976         this.el.addClass(this.invalidClass);
9977         
9978         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9979             
9980             var feedback = this.el.select('.form-control-feedback', true).first();
9981             
9982             if(feedback){
9983                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9984                 
9985                 if(this.getValue().length || this.forceFeedback){
9986                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9987                 }
9988                 
9989             }
9990             
9991         }
9992         
9993         this.fireEvent('invalid', this, msg);
9994     }
9995 });
9996
9997  
9998 /*
9999  * - LGPL
10000  *
10001  * trigger field - base class for combo..
10002  * 
10003  */
10004  
10005 /**
10006  * @class Roo.bootstrap.TriggerField
10007  * @extends Roo.bootstrap.Input
10008  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10009  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10010  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10011  * for which you can provide a custom implementation.  For example:
10012  * <pre><code>
10013 var trigger = new Roo.bootstrap.TriggerField();
10014 trigger.onTriggerClick = myTriggerFn;
10015 trigger.applyTo('my-field');
10016 </code></pre>
10017  *
10018  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10019  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10020  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10021  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10022  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10023
10024  * @constructor
10025  * Create a new TriggerField.
10026  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10027  * to the base TextField)
10028  */
10029 Roo.bootstrap.TriggerField = function(config){
10030     this.mimicing = false;
10031     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10032 };
10033
10034 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10035     /**
10036      * @cfg {String} triggerClass A CSS class to apply to the trigger
10037      */
10038      /**
10039      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10040      */
10041     hideTrigger:false,
10042
10043     /**
10044      * @cfg {Boolean} removable (true|false) special filter default false
10045      */
10046     removable : false,
10047     
10048     /** @cfg {Boolean} grow @hide */
10049     /** @cfg {Number} growMin @hide */
10050     /** @cfg {Number} growMax @hide */
10051
10052     /**
10053      * @hide 
10054      * @method
10055      */
10056     autoSize: Roo.emptyFn,
10057     // private
10058     monitorTab : true,
10059     // private
10060     deferHeight : true,
10061
10062     
10063     actionMode : 'wrap',
10064     
10065     caret : false,
10066     
10067     
10068     getAutoCreate : function(){
10069        
10070         var align = this.labelAlign || this.parentLabelAlign();
10071         
10072         var id = Roo.id();
10073         
10074         var cfg = {
10075             cls: 'form-group' //input-group
10076         };
10077         
10078         
10079         var input =  {
10080             tag: 'input',
10081             id : id,
10082             type : this.inputType,
10083             cls : 'form-control',
10084             autocomplete: 'new-password',
10085             placeholder : this.placeholder || '' 
10086             
10087         };
10088         if (this.name) {
10089             input.name = this.name;
10090         }
10091         if (this.size) {
10092             input.cls += ' input-' + this.size;
10093         }
10094         
10095         if (this.disabled) {
10096             input.disabled=true;
10097         }
10098         
10099         var inputblock = input;
10100         
10101         if(this.hasFeedback && !this.allowBlank){
10102             
10103             var feedback = {
10104                 tag: 'span',
10105                 cls: 'glyphicon form-control-feedback'
10106             };
10107             
10108             if(this.removable && !this.editable && !this.tickable){
10109                 inputblock = {
10110                     cls : 'has-feedback',
10111                     cn :  [
10112                         inputblock,
10113                         {
10114                             tag: 'button',
10115                             html : 'x',
10116                             cls : 'roo-combo-removable-btn close'
10117                         },
10118                         feedback
10119                     ] 
10120                 };
10121             } else {
10122                 inputblock = {
10123                     cls : 'has-feedback',
10124                     cn :  [
10125                         inputblock,
10126                         feedback
10127                     ] 
10128                 };
10129             }
10130
10131         } else {
10132             if(this.removable && !this.editable && !this.tickable){
10133                 inputblock = {
10134                     cls : 'roo-removable',
10135                     cn :  [
10136                         inputblock,
10137                         {
10138                             tag: 'button',
10139                             html : 'x',
10140                             cls : 'roo-combo-removable-btn close'
10141                         }
10142                     ] 
10143                 };
10144             }
10145         }
10146         
10147         if (this.before || this.after) {
10148             
10149             inputblock = {
10150                 cls : 'input-group',
10151                 cn :  [] 
10152             };
10153             if (this.before) {
10154                 inputblock.cn.push({
10155                     tag :'span',
10156                     cls : 'input-group-addon',
10157                     html : this.before
10158                 });
10159             }
10160             
10161             inputblock.cn.push(input);
10162             
10163             if(this.hasFeedback && !this.allowBlank){
10164                 inputblock.cls += ' has-feedback';
10165                 inputblock.cn.push(feedback);
10166             }
10167             
10168             if (this.after) {
10169                 inputblock.cn.push({
10170                     tag :'span',
10171                     cls : 'input-group-addon',
10172                     html : this.after
10173                 });
10174             }
10175             
10176         };
10177         
10178         var box = {
10179             tag: 'div',
10180             cn: [
10181                 {
10182                     tag: 'input',
10183                     type : 'hidden',
10184                     cls: 'form-hidden-field'
10185                 },
10186                 inputblock
10187             ]
10188             
10189         };
10190         
10191         if(this.multiple){
10192             box = {
10193                 tag: 'div',
10194                 cn: [
10195                     {
10196                         tag: 'input',
10197                         type : 'hidden',
10198                         cls: 'form-hidden-field'
10199                     },
10200                     {
10201                         tag: 'ul',
10202                         cls: 'roo-select2-choices',
10203                         cn:[
10204                             {
10205                                 tag: 'li',
10206                                 cls: 'roo-select2-search-field',
10207                                 cn: [
10208
10209                                     inputblock
10210                                 ]
10211                             }
10212                         ]
10213                     }
10214                 ]
10215             }
10216         };
10217         
10218         var combobox = {
10219             cls: 'roo-select2-container input-group',
10220             cn: [
10221                 box
10222 //                {
10223 //                    tag: 'ul',
10224 //                    cls: 'typeahead typeahead-long dropdown-menu',
10225 //                    style: 'display:none'
10226 //                }
10227             ]
10228         };
10229         
10230         if(!this.multiple && this.showToggleBtn){
10231             
10232             var caret = {
10233                         tag: 'span',
10234                         cls: 'caret'
10235              };
10236             if (this.caret != false) {
10237                 caret = {
10238                      tag: 'i',
10239                      cls: 'fa fa-' + this.caret
10240                 };
10241                 
10242             }
10243             
10244             combobox.cn.push({
10245                 tag :'span',
10246                 cls : 'input-group-addon btn dropdown-toggle',
10247                 cn : [
10248                     caret,
10249                     {
10250                         tag: 'span',
10251                         cls: 'combobox-clear',
10252                         cn  : [
10253                             {
10254                                 tag : 'i',
10255                                 cls: 'icon-remove'
10256                             }
10257                         ]
10258                     }
10259                 ]
10260
10261             })
10262         }
10263         
10264         if(this.multiple){
10265             combobox.cls += ' roo-select2-container-multi';
10266         }
10267         
10268         if (align ==='left' && this.fieldLabel.length) {
10269             
10270             cfg.cls += ' roo-form-group-label-left';
10271
10272             cfg.cn = [
10273                 {
10274                     tag : 'i',
10275                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10276                     tooltip : 'This field is required'
10277                 },
10278                 {
10279                     tag: 'label',
10280                     'for' :  id,
10281                     cls : 'control-label',
10282                     html : this.fieldLabel
10283
10284                 },
10285                 {
10286                     cls : "", 
10287                     cn: [
10288                         combobox
10289                     ]
10290                 }
10291
10292             ];
10293             
10294             var labelCfg = cfg.cn[1];
10295             var contentCfg = cfg.cn[2];
10296             
10297             if(this.indicatorpos == 'right'){
10298                 cfg.cn = [
10299                     {
10300                         tag: 'label',
10301                         'for' :  id,
10302                         cls : 'control-label',
10303                         cn : [
10304                             {
10305                                 tag : 'span',
10306                                 html : this.fieldLabel
10307                             },
10308                             {
10309                                 tag : 'i',
10310                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10311                                 tooltip : 'This field is required'
10312                             }
10313                         ]
10314                     },
10315                     {
10316                         cls : "", 
10317                         cn: [
10318                             combobox
10319                         ]
10320                     }
10321
10322                 ];
10323                 
10324                 labelCfg = cfg.cn[0];
10325                 contentCfg = cfg.cn[1];
10326             }
10327             
10328             if(this.labelWidth > 12){
10329                 labelCfg.style = "width: " + this.labelWidth + 'px';
10330             }
10331             
10332             if(this.labelWidth < 13 && this.labelmd == 0){
10333                 this.labelmd = this.labelWidth;
10334             }
10335             
10336             if(this.labellg > 0){
10337                 labelCfg.cls += ' col-lg-' + this.labellg;
10338                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10339             }
10340             
10341             if(this.labelmd > 0){
10342                 labelCfg.cls += ' col-md-' + this.labelmd;
10343                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10344             }
10345             
10346             if(this.labelsm > 0){
10347                 labelCfg.cls += ' col-sm-' + this.labelsm;
10348                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10349             }
10350             
10351             if(this.labelxs > 0){
10352                 labelCfg.cls += ' col-xs-' + this.labelxs;
10353                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10354             }
10355             
10356         } else if ( this.fieldLabel.length) {
10357 //                Roo.log(" label");
10358             cfg.cn = [
10359                 {
10360                    tag : 'i',
10361                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10362                    tooltip : 'This field is required'
10363                },
10364                {
10365                    tag: 'label',
10366                    //cls : 'input-group-addon',
10367                    html : this.fieldLabel
10368
10369                },
10370
10371                combobox
10372
10373             ];
10374             
10375             if(this.indicatorpos == 'right'){
10376                 
10377                 cfg.cn = [
10378                     {
10379                        tag: 'label',
10380                        cn : [
10381                            {
10382                                tag : 'span',
10383                                html : this.fieldLabel
10384                            },
10385                            {
10386                               tag : 'i',
10387                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10388                               tooltip : 'This field is required'
10389                            }
10390                        ]
10391
10392                     },
10393                     combobox
10394
10395                 ];
10396
10397             }
10398
10399         } else {
10400             
10401 //                Roo.log(" no label && no align");
10402                 cfg = combobox
10403                      
10404                 
10405         }
10406         
10407         var settings=this;
10408         ['xs','sm','md','lg'].map(function(size){
10409             if (settings[size]) {
10410                 cfg.cls += ' col-' + size + '-' + settings[size];
10411             }
10412         });
10413         
10414         return cfg;
10415         
10416     },
10417     
10418     
10419     
10420     // private
10421     onResize : function(w, h){
10422 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10423 //        if(typeof w == 'number'){
10424 //            var x = w - this.trigger.getWidth();
10425 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10426 //            this.trigger.setStyle('left', x+'px');
10427 //        }
10428     },
10429
10430     // private
10431     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10432
10433     // private
10434     getResizeEl : function(){
10435         return this.inputEl();
10436     },
10437
10438     // private
10439     getPositionEl : function(){
10440         return this.inputEl();
10441     },
10442
10443     // private
10444     alignErrorIcon : function(){
10445         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10446     },
10447
10448     // private
10449     initEvents : function(){
10450         
10451         this.createList();
10452         
10453         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10454         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10455         if(!this.multiple && this.showToggleBtn){
10456             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10457             if(this.hideTrigger){
10458                 this.trigger.setDisplayed(false);
10459             }
10460             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10461         }
10462         
10463         if(this.multiple){
10464             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10465         }
10466         
10467         if(this.removable && !this.editable && !this.tickable){
10468             var close = this.closeTriggerEl();
10469             
10470             if(close){
10471                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10472                 close.on('click', this.removeBtnClick, this, close);
10473             }
10474         }
10475         
10476         //this.trigger.addClassOnOver('x-form-trigger-over');
10477         //this.trigger.addClassOnClick('x-form-trigger-click');
10478         
10479         //if(!this.width){
10480         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10481         //}
10482     },
10483     
10484     closeTriggerEl : function()
10485     {
10486         var close = this.el.select('.roo-combo-removable-btn', true).first();
10487         return close ? close : false;
10488     },
10489     
10490     removeBtnClick : function(e, h, el)
10491     {
10492         e.preventDefault();
10493         
10494         if(this.fireEvent("remove", this) !== false){
10495             this.reset();
10496             this.fireEvent("afterremove", this)
10497         }
10498     },
10499     
10500     createList : function()
10501     {
10502         this.list = Roo.get(document.body).createChild({
10503             tag: 'ul',
10504             cls: 'typeahead typeahead-long dropdown-menu',
10505             style: 'display:none'
10506         });
10507         
10508         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10509         
10510     },
10511
10512     // private
10513     initTrigger : function(){
10514        
10515     },
10516
10517     // private
10518     onDestroy : function(){
10519         if(this.trigger){
10520             this.trigger.removeAllListeners();
10521           //  this.trigger.remove();
10522         }
10523         //if(this.wrap){
10524         //    this.wrap.remove();
10525         //}
10526         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10527     },
10528
10529     // private
10530     onFocus : function(){
10531         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10532         /*
10533         if(!this.mimicing){
10534             this.wrap.addClass('x-trigger-wrap-focus');
10535             this.mimicing = true;
10536             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10537             if(this.monitorTab){
10538                 this.el.on("keydown", this.checkTab, this);
10539             }
10540         }
10541         */
10542     },
10543
10544     // private
10545     checkTab : function(e){
10546         if(e.getKey() == e.TAB){
10547             this.triggerBlur();
10548         }
10549     },
10550
10551     // private
10552     onBlur : function(){
10553         // do nothing
10554     },
10555
10556     // private
10557     mimicBlur : function(e, t){
10558         /*
10559         if(!this.wrap.contains(t) && this.validateBlur()){
10560             this.triggerBlur();
10561         }
10562         */
10563     },
10564
10565     // private
10566     triggerBlur : function(){
10567         this.mimicing = false;
10568         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10569         if(this.monitorTab){
10570             this.el.un("keydown", this.checkTab, this);
10571         }
10572         //this.wrap.removeClass('x-trigger-wrap-focus');
10573         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10574     },
10575
10576     // private
10577     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10578     validateBlur : function(e, t){
10579         return true;
10580     },
10581
10582     // private
10583     onDisable : function(){
10584         this.inputEl().dom.disabled = true;
10585         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10586         //if(this.wrap){
10587         //    this.wrap.addClass('x-item-disabled');
10588         //}
10589     },
10590
10591     // private
10592     onEnable : function(){
10593         this.inputEl().dom.disabled = false;
10594         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10595         //if(this.wrap){
10596         //    this.el.removeClass('x-item-disabled');
10597         //}
10598     },
10599
10600     // private
10601     onShow : function(){
10602         var ae = this.getActionEl();
10603         
10604         if(ae){
10605             ae.dom.style.display = '';
10606             ae.dom.style.visibility = 'visible';
10607         }
10608     },
10609
10610     // private
10611     
10612     onHide : function(){
10613         var ae = this.getActionEl();
10614         ae.dom.style.display = 'none';
10615     },
10616
10617     /**
10618      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10619      * by an implementing function.
10620      * @method
10621      * @param {EventObject} e
10622      */
10623     onTriggerClick : Roo.emptyFn
10624 });
10625  /*
10626  * Based on:
10627  * Ext JS Library 1.1.1
10628  * Copyright(c) 2006-2007, Ext JS, LLC.
10629  *
10630  * Originally Released Under LGPL - original licence link has changed is not relivant.
10631  *
10632  * Fork - LGPL
10633  * <script type="text/javascript">
10634  */
10635
10636
10637 /**
10638  * @class Roo.data.SortTypes
10639  * @singleton
10640  * Defines the default sorting (casting?) comparison functions used when sorting data.
10641  */
10642 Roo.data.SortTypes = {
10643     /**
10644      * Default sort that does nothing
10645      * @param {Mixed} s The value being converted
10646      * @return {Mixed} The comparison value
10647      */
10648     none : function(s){
10649         return s;
10650     },
10651     
10652     /**
10653      * The regular expression used to strip tags
10654      * @type {RegExp}
10655      * @property
10656      */
10657     stripTagsRE : /<\/?[^>]+>/gi,
10658     
10659     /**
10660      * Strips all HTML tags to sort on text only
10661      * @param {Mixed} s The value being converted
10662      * @return {String} The comparison value
10663      */
10664     asText : function(s){
10665         return String(s).replace(this.stripTagsRE, "");
10666     },
10667     
10668     /**
10669      * Strips all HTML tags to sort on text only - Case insensitive
10670      * @param {Mixed} s The value being converted
10671      * @return {String} The comparison value
10672      */
10673     asUCText : function(s){
10674         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10675     },
10676     
10677     /**
10678      * Case insensitive string
10679      * @param {Mixed} s The value being converted
10680      * @return {String} The comparison value
10681      */
10682     asUCString : function(s) {
10683         return String(s).toUpperCase();
10684     },
10685     
10686     /**
10687      * Date sorting
10688      * @param {Mixed} s The value being converted
10689      * @return {Number} The comparison value
10690      */
10691     asDate : function(s) {
10692         if(!s){
10693             return 0;
10694         }
10695         if(s instanceof Date){
10696             return s.getTime();
10697         }
10698         return Date.parse(String(s));
10699     },
10700     
10701     /**
10702      * Float sorting
10703      * @param {Mixed} s The value being converted
10704      * @return {Float} The comparison value
10705      */
10706     asFloat : function(s) {
10707         var val = parseFloat(String(s).replace(/,/g, ""));
10708         if(isNaN(val)) {
10709             val = 0;
10710         }
10711         return val;
10712     },
10713     
10714     /**
10715      * Integer sorting
10716      * @param {Mixed} s The value being converted
10717      * @return {Number} The comparison value
10718      */
10719     asInt : function(s) {
10720         var val = parseInt(String(s).replace(/,/g, ""));
10721         if(isNaN(val)) {
10722             val = 0;
10723         }
10724         return val;
10725     }
10726 };/*
10727  * Based on:
10728  * Ext JS Library 1.1.1
10729  * Copyright(c) 2006-2007, Ext JS, LLC.
10730  *
10731  * Originally Released Under LGPL - original licence link has changed is not relivant.
10732  *
10733  * Fork - LGPL
10734  * <script type="text/javascript">
10735  */
10736
10737 /**
10738 * @class Roo.data.Record
10739  * Instances of this class encapsulate both record <em>definition</em> information, and record
10740  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10741  * to access Records cached in an {@link Roo.data.Store} object.<br>
10742  * <p>
10743  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10744  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10745  * objects.<br>
10746  * <p>
10747  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10748  * @constructor
10749  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10750  * {@link #create}. The parameters are the same.
10751  * @param {Array} data An associative Array of data values keyed by the field name.
10752  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10753  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10754  * not specified an integer id is generated.
10755  */
10756 Roo.data.Record = function(data, id){
10757     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10758     this.data = data;
10759 };
10760
10761 /**
10762  * Generate a constructor for a specific record layout.
10763  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10764  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10765  * Each field definition object may contain the following properties: <ul>
10766  * <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,
10767  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10768  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10769  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10770  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10771  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10772  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10773  * this may be omitted.</p></li>
10774  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10775  * <ul><li>auto (Default, implies no conversion)</li>
10776  * <li>string</li>
10777  * <li>int</li>
10778  * <li>float</li>
10779  * <li>boolean</li>
10780  * <li>date</li></ul></p></li>
10781  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10782  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10783  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10784  * by the Reader into an object that will be stored in the Record. It is passed the
10785  * following parameters:<ul>
10786  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10787  * </ul></p></li>
10788  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10789  * </ul>
10790  * <br>usage:<br><pre><code>
10791 var TopicRecord = Roo.data.Record.create(
10792     {name: 'title', mapping: 'topic_title'},
10793     {name: 'author', mapping: 'username'},
10794     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10795     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10796     {name: 'lastPoster', mapping: 'user2'},
10797     {name: 'excerpt', mapping: 'post_text'}
10798 );
10799
10800 var myNewRecord = new TopicRecord({
10801     title: 'Do my job please',
10802     author: 'noobie',
10803     totalPosts: 1,
10804     lastPost: new Date(),
10805     lastPoster: 'Animal',
10806     excerpt: 'No way dude!'
10807 });
10808 myStore.add(myNewRecord);
10809 </code></pre>
10810  * @method create
10811  * @static
10812  */
10813 Roo.data.Record.create = function(o){
10814     var f = function(){
10815         f.superclass.constructor.apply(this, arguments);
10816     };
10817     Roo.extend(f, Roo.data.Record);
10818     var p = f.prototype;
10819     p.fields = new Roo.util.MixedCollection(false, function(field){
10820         return field.name;
10821     });
10822     for(var i = 0, len = o.length; i < len; i++){
10823         p.fields.add(new Roo.data.Field(o[i]));
10824     }
10825     f.getField = function(name){
10826         return p.fields.get(name);  
10827     };
10828     return f;
10829 };
10830
10831 Roo.data.Record.AUTO_ID = 1000;
10832 Roo.data.Record.EDIT = 'edit';
10833 Roo.data.Record.REJECT = 'reject';
10834 Roo.data.Record.COMMIT = 'commit';
10835
10836 Roo.data.Record.prototype = {
10837     /**
10838      * Readonly flag - true if this record has been modified.
10839      * @type Boolean
10840      */
10841     dirty : false,
10842     editing : false,
10843     error: null,
10844     modified: null,
10845
10846     // private
10847     join : function(store){
10848         this.store = store;
10849     },
10850
10851     /**
10852      * Set the named field to the specified value.
10853      * @param {String} name The name of the field to set.
10854      * @param {Object} value The value to set the field to.
10855      */
10856     set : function(name, value){
10857         if(this.data[name] == value){
10858             return;
10859         }
10860         this.dirty = true;
10861         if(!this.modified){
10862             this.modified = {};
10863         }
10864         if(typeof this.modified[name] == 'undefined'){
10865             this.modified[name] = this.data[name];
10866         }
10867         this.data[name] = value;
10868         if(!this.editing && this.store){
10869             this.store.afterEdit(this);
10870         }       
10871     },
10872
10873     /**
10874      * Get the value of the named field.
10875      * @param {String} name The name of the field to get the value of.
10876      * @return {Object} The value of the field.
10877      */
10878     get : function(name){
10879         return this.data[name]; 
10880     },
10881
10882     // private
10883     beginEdit : function(){
10884         this.editing = true;
10885         this.modified = {}; 
10886     },
10887
10888     // private
10889     cancelEdit : function(){
10890         this.editing = false;
10891         delete this.modified;
10892     },
10893
10894     // private
10895     endEdit : function(){
10896         this.editing = false;
10897         if(this.dirty && this.store){
10898             this.store.afterEdit(this);
10899         }
10900     },
10901
10902     /**
10903      * Usually called by the {@link Roo.data.Store} which owns the Record.
10904      * Rejects all changes made to the Record since either creation, or the last commit operation.
10905      * Modified fields are reverted to their original values.
10906      * <p>
10907      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10908      * of reject operations.
10909      */
10910     reject : function(){
10911         var m = this.modified;
10912         for(var n in m){
10913             if(typeof m[n] != "function"){
10914                 this.data[n] = m[n];
10915             }
10916         }
10917         this.dirty = false;
10918         delete this.modified;
10919         this.editing = false;
10920         if(this.store){
10921             this.store.afterReject(this);
10922         }
10923     },
10924
10925     /**
10926      * Usually called by the {@link Roo.data.Store} which owns the Record.
10927      * Commits all changes made to the Record since either creation, or the last commit operation.
10928      * <p>
10929      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10930      * of commit operations.
10931      */
10932     commit : function(){
10933         this.dirty = false;
10934         delete this.modified;
10935         this.editing = false;
10936         if(this.store){
10937             this.store.afterCommit(this);
10938         }
10939     },
10940
10941     // private
10942     hasError : function(){
10943         return this.error != null;
10944     },
10945
10946     // private
10947     clearError : function(){
10948         this.error = null;
10949     },
10950
10951     /**
10952      * Creates a copy of this record.
10953      * @param {String} id (optional) A new record id if you don't want to use this record's id
10954      * @return {Record}
10955      */
10956     copy : function(newId) {
10957         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10958     }
10959 };/*
10960  * Based on:
10961  * Ext JS Library 1.1.1
10962  * Copyright(c) 2006-2007, Ext JS, LLC.
10963  *
10964  * Originally Released Under LGPL - original licence link has changed is not relivant.
10965  *
10966  * Fork - LGPL
10967  * <script type="text/javascript">
10968  */
10969
10970
10971
10972 /**
10973  * @class Roo.data.Store
10974  * @extends Roo.util.Observable
10975  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10976  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10977  * <p>
10978  * 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
10979  * has no knowledge of the format of the data returned by the Proxy.<br>
10980  * <p>
10981  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10982  * instances from the data object. These records are cached and made available through accessor functions.
10983  * @constructor
10984  * Creates a new Store.
10985  * @param {Object} config A config object containing the objects needed for the Store to access data,
10986  * and read the data into Records.
10987  */
10988 Roo.data.Store = function(config){
10989     this.data = new Roo.util.MixedCollection(false);
10990     this.data.getKey = function(o){
10991         return o.id;
10992     };
10993     this.baseParams = {};
10994     // private
10995     this.paramNames = {
10996         "start" : "start",
10997         "limit" : "limit",
10998         "sort" : "sort",
10999         "dir" : "dir",
11000         "multisort" : "_multisort"
11001     };
11002
11003     if(config && config.data){
11004         this.inlineData = config.data;
11005         delete config.data;
11006     }
11007
11008     Roo.apply(this, config);
11009     
11010     if(this.reader){ // reader passed
11011         this.reader = Roo.factory(this.reader, Roo.data);
11012         this.reader.xmodule = this.xmodule || false;
11013         if(!this.recordType){
11014             this.recordType = this.reader.recordType;
11015         }
11016         if(this.reader.onMetaChange){
11017             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11018         }
11019     }
11020
11021     if(this.recordType){
11022         this.fields = this.recordType.prototype.fields;
11023     }
11024     this.modified = [];
11025
11026     this.addEvents({
11027         /**
11028          * @event datachanged
11029          * Fires when the data cache has changed, and a widget which is using this Store
11030          * as a Record cache should refresh its view.
11031          * @param {Store} this
11032          */
11033         datachanged : true,
11034         /**
11035          * @event metachange
11036          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11037          * @param {Store} this
11038          * @param {Object} meta The JSON metadata
11039          */
11040         metachange : true,
11041         /**
11042          * @event add
11043          * Fires when Records have been added to the Store
11044          * @param {Store} this
11045          * @param {Roo.data.Record[]} records The array of Records added
11046          * @param {Number} index The index at which the record(s) were added
11047          */
11048         add : true,
11049         /**
11050          * @event remove
11051          * Fires when a Record has been removed from the Store
11052          * @param {Store} this
11053          * @param {Roo.data.Record} record The Record that was removed
11054          * @param {Number} index The index at which the record was removed
11055          */
11056         remove : true,
11057         /**
11058          * @event update
11059          * Fires when a Record has been updated
11060          * @param {Store} this
11061          * @param {Roo.data.Record} record The Record that was updated
11062          * @param {String} operation The update operation being performed.  Value may be one of:
11063          * <pre><code>
11064  Roo.data.Record.EDIT
11065  Roo.data.Record.REJECT
11066  Roo.data.Record.COMMIT
11067          * </code></pre>
11068          */
11069         update : true,
11070         /**
11071          * @event clear
11072          * Fires when the data cache has been cleared.
11073          * @param {Store} this
11074          */
11075         clear : true,
11076         /**
11077          * @event beforeload
11078          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11079          * the load action will be canceled.
11080          * @param {Store} this
11081          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11082          */
11083         beforeload : true,
11084         /**
11085          * @event beforeloadadd
11086          * Fires after a new set of Records has been loaded.
11087          * @param {Store} this
11088          * @param {Roo.data.Record[]} records The Records that were loaded
11089          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11090          */
11091         beforeloadadd : true,
11092         /**
11093          * @event load
11094          * Fires after a new set of Records has been loaded, before they are added to the store.
11095          * @param {Store} this
11096          * @param {Roo.data.Record[]} records The Records that were loaded
11097          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11098          * @params {Object} return from reader
11099          */
11100         load : true,
11101         /**
11102          * @event loadexception
11103          * Fires if an exception occurs in the Proxy during loading.
11104          * Called with the signature of the Proxy's "loadexception" event.
11105          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11106          * 
11107          * @param {Proxy} 
11108          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11109          * @param {Object} load options 
11110          * @param {Object} jsonData from your request (normally this contains the Exception)
11111          */
11112         loadexception : true
11113     });
11114     
11115     if(this.proxy){
11116         this.proxy = Roo.factory(this.proxy, Roo.data);
11117         this.proxy.xmodule = this.xmodule || false;
11118         this.relayEvents(this.proxy,  ["loadexception"]);
11119     }
11120     this.sortToggle = {};
11121     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11122
11123     Roo.data.Store.superclass.constructor.call(this);
11124
11125     if(this.inlineData){
11126         this.loadData(this.inlineData);
11127         delete this.inlineData;
11128     }
11129 };
11130
11131 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11132      /**
11133     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11134     * without a remote query - used by combo/forms at present.
11135     */
11136     
11137     /**
11138     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11139     */
11140     /**
11141     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11142     */
11143     /**
11144     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11145     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11146     */
11147     /**
11148     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11149     * on any HTTP request
11150     */
11151     /**
11152     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11153     */
11154     /**
11155     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11156     */
11157     multiSort: false,
11158     /**
11159     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11160     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11161     */
11162     remoteSort : false,
11163
11164     /**
11165     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11166      * loaded or when a record is removed. (defaults to false).
11167     */
11168     pruneModifiedRecords : false,
11169
11170     // private
11171     lastOptions : null,
11172
11173     /**
11174      * Add Records to the Store and fires the add event.
11175      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11176      */
11177     add : function(records){
11178         records = [].concat(records);
11179         for(var i = 0, len = records.length; i < len; i++){
11180             records[i].join(this);
11181         }
11182         var index = this.data.length;
11183         this.data.addAll(records);
11184         this.fireEvent("add", this, records, index);
11185     },
11186
11187     /**
11188      * Remove a Record from the Store and fires the remove event.
11189      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11190      */
11191     remove : function(record){
11192         var index = this.data.indexOf(record);
11193         this.data.removeAt(index);
11194  
11195         if(this.pruneModifiedRecords){
11196             this.modified.remove(record);
11197         }
11198         this.fireEvent("remove", this, record, index);
11199     },
11200
11201     /**
11202      * Remove all Records from the Store and fires the clear event.
11203      */
11204     removeAll : function(){
11205         this.data.clear();
11206         if(this.pruneModifiedRecords){
11207             this.modified = [];
11208         }
11209         this.fireEvent("clear", this);
11210     },
11211
11212     /**
11213      * Inserts Records to the Store at the given index and fires the add event.
11214      * @param {Number} index The start index at which to insert the passed Records.
11215      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11216      */
11217     insert : function(index, records){
11218         records = [].concat(records);
11219         for(var i = 0, len = records.length; i < len; i++){
11220             this.data.insert(index, records[i]);
11221             records[i].join(this);
11222         }
11223         this.fireEvent("add", this, records, index);
11224     },
11225
11226     /**
11227      * Get the index within the cache of the passed Record.
11228      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11229      * @return {Number} The index of the passed Record. Returns -1 if not found.
11230      */
11231     indexOf : function(record){
11232         return this.data.indexOf(record);
11233     },
11234
11235     /**
11236      * Get the index within the cache of the Record with the passed id.
11237      * @param {String} id The id of the Record to find.
11238      * @return {Number} The index of the Record. Returns -1 if not found.
11239      */
11240     indexOfId : function(id){
11241         return this.data.indexOfKey(id);
11242     },
11243
11244     /**
11245      * Get the Record with the specified id.
11246      * @param {String} id The id of the Record to find.
11247      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11248      */
11249     getById : function(id){
11250         return this.data.key(id);
11251     },
11252
11253     /**
11254      * Get the Record at the specified index.
11255      * @param {Number} index The index of the Record to find.
11256      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11257      */
11258     getAt : function(index){
11259         return this.data.itemAt(index);
11260     },
11261
11262     /**
11263      * Returns a range of Records between specified indices.
11264      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11265      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11266      * @return {Roo.data.Record[]} An array of Records
11267      */
11268     getRange : function(start, end){
11269         return this.data.getRange(start, end);
11270     },
11271
11272     // private
11273     storeOptions : function(o){
11274         o = Roo.apply({}, o);
11275         delete o.callback;
11276         delete o.scope;
11277         this.lastOptions = o;
11278     },
11279
11280     /**
11281      * Loads the Record cache from the configured Proxy using the configured Reader.
11282      * <p>
11283      * If using remote paging, then the first load call must specify the <em>start</em>
11284      * and <em>limit</em> properties in the options.params property to establish the initial
11285      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11286      * <p>
11287      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11288      * and this call will return before the new data has been loaded. Perform any post-processing
11289      * in a callback function, or in a "load" event handler.</strong>
11290      * <p>
11291      * @param {Object} options An object containing properties which control loading options:<ul>
11292      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11293      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11294      * passed the following arguments:<ul>
11295      * <li>r : Roo.data.Record[]</li>
11296      * <li>options: Options object from the load call</li>
11297      * <li>success: Boolean success indicator</li></ul></li>
11298      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11299      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11300      * </ul>
11301      */
11302     load : function(options){
11303         options = options || {};
11304         if(this.fireEvent("beforeload", this, options) !== false){
11305             this.storeOptions(options);
11306             var p = Roo.apply(options.params || {}, this.baseParams);
11307             // if meta was not loaded from remote source.. try requesting it.
11308             if (!this.reader.metaFromRemote) {
11309                 p._requestMeta = 1;
11310             }
11311             if(this.sortInfo && this.remoteSort){
11312                 var pn = this.paramNames;
11313                 p[pn["sort"]] = this.sortInfo.field;
11314                 p[pn["dir"]] = this.sortInfo.direction;
11315             }
11316             if (this.multiSort) {
11317                 var pn = this.paramNames;
11318                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11319             }
11320             
11321             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11322         }
11323     },
11324
11325     /**
11326      * Reloads the Record cache from the configured Proxy using the configured Reader and
11327      * the options from the last load operation performed.
11328      * @param {Object} options (optional) An object containing properties which may override the options
11329      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11330      * the most recently used options are reused).
11331      */
11332     reload : function(options){
11333         this.load(Roo.applyIf(options||{}, this.lastOptions));
11334     },
11335
11336     // private
11337     // Called as a callback by the Reader during a load operation.
11338     loadRecords : function(o, options, success){
11339         if(!o || success === false){
11340             if(success !== false){
11341                 this.fireEvent("load", this, [], options, o);
11342             }
11343             if(options.callback){
11344                 options.callback.call(options.scope || this, [], options, false);
11345             }
11346             return;
11347         }
11348         // if data returned failure - throw an exception.
11349         if (o.success === false) {
11350             // show a message if no listener is registered.
11351             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11352                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11353             }
11354             // loadmask wil be hooked into this..
11355             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11356             return;
11357         }
11358         var r = o.records, t = o.totalRecords || r.length;
11359         
11360         this.fireEvent("beforeloadadd", this, r, options, o);
11361         
11362         if(!options || options.add !== true){
11363             if(this.pruneModifiedRecords){
11364                 this.modified = [];
11365             }
11366             for(var i = 0, len = r.length; i < len; i++){
11367                 r[i].join(this);
11368             }
11369             if(this.snapshot){
11370                 this.data = this.snapshot;
11371                 delete this.snapshot;
11372             }
11373             this.data.clear();
11374             this.data.addAll(r);
11375             this.totalLength = t;
11376             this.applySort();
11377             this.fireEvent("datachanged", this);
11378         }else{
11379             this.totalLength = Math.max(t, this.data.length+r.length);
11380             this.add(r);
11381         }
11382         
11383         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11384                 
11385             var e = new Roo.data.Record({});
11386
11387             e.set(this.parent.displayField, this.parent.emptyTitle);
11388             e.set(this.parent.valueField, '');
11389
11390             this.insert(0, e);
11391         }
11392             
11393         this.fireEvent("load", this, r, options, o);
11394         if(options.callback){
11395             options.callback.call(options.scope || this, r, options, true);
11396         }
11397     },
11398
11399
11400     /**
11401      * Loads data from a passed data block. A Reader which understands the format of the data
11402      * must have been configured in the constructor.
11403      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11404      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11405      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11406      */
11407     loadData : function(o, append){
11408         var r = this.reader.readRecords(o);
11409         this.loadRecords(r, {add: append}, true);
11410     },
11411
11412     /**
11413      * Gets the number of cached records.
11414      * <p>
11415      * <em>If using paging, this may not be the total size of the dataset. If the data object
11416      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11417      * the data set size</em>
11418      */
11419     getCount : function(){
11420         return this.data.length || 0;
11421     },
11422
11423     /**
11424      * Gets the total number of records in the dataset as returned by the server.
11425      * <p>
11426      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11427      * the dataset size</em>
11428      */
11429     getTotalCount : function(){
11430         return this.totalLength || 0;
11431     },
11432
11433     /**
11434      * Returns the sort state of the Store as an object with two properties:
11435      * <pre><code>
11436  field {String} The name of the field by which the Records are sorted
11437  direction {String} The sort order, "ASC" or "DESC"
11438      * </code></pre>
11439      */
11440     getSortState : function(){
11441         return this.sortInfo;
11442     },
11443
11444     // private
11445     applySort : function(){
11446         if(this.sortInfo && !this.remoteSort){
11447             var s = this.sortInfo, f = s.field;
11448             var st = this.fields.get(f).sortType;
11449             var fn = function(r1, r2){
11450                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11451                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11452             };
11453             this.data.sort(s.direction, fn);
11454             if(this.snapshot && this.snapshot != this.data){
11455                 this.snapshot.sort(s.direction, fn);
11456             }
11457         }
11458     },
11459
11460     /**
11461      * Sets the default sort column and order to be used by the next load operation.
11462      * @param {String} fieldName The name of the field to sort by.
11463      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11464      */
11465     setDefaultSort : function(field, dir){
11466         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11467     },
11468
11469     /**
11470      * Sort the Records.
11471      * If remote sorting is used, the sort is performed on the server, and the cache is
11472      * reloaded. If local sorting is used, the cache is sorted internally.
11473      * @param {String} fieldName The name of the field to sort by.
11474      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11475      */
11476     sort : function(fieldName, dir){
11477         var f = this.fields.get(fieldName);
11478         if(!dir){
11479             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11480             
11481             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11482                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11483             }else{
11484                 dir = f.sortDir;
11485             }
11486         }
11487         this.sortToggle[f.name] = dir;
11488         this.sortInfo = {field: f.name, direction: dir};
11489         if(!this.remoteSort){
11490             this.applySort();
11491             this.fireEvent("datachanged", this);
11492         }else{
11493             this.load(this.lastOptions);
11494         }
11495     },
11496
11497     /**
11498      * Calls the specified function for each of the Records in the cache.
11499      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11500      * Returning <em>false</em> aborts and exits the iteration.
11501      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11502      */
11503     each : function(fn, scope){
11504         this.data.each(fn, scope);
11505     },
11506
11507     /**
11508      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11509      * (e.g., during paging).
11510      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11511      */
11512     getModifiedRecords : function(){
11513         return this.modified;
11514     },
11515
11516     // private
11517     createFilterFn : function(property, value, anyMatch){
11518         if(!value.exec){ // not a regex
11519             value = String(value);
11520             if(value.length == 0){
11521                 return false;
11522             }
11523             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11524         }
11525         return function(r){
11526             return value.test(r.data[property]);
11527         };
11528     },
11529
11530     /**
11531      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11532      * @param {String} property A field on your records
11533      * @param {Number} start The record index to start at (defaults to 0)
11534      * @param {Number} end The last record index to include (defaults to length - 1)
11535      * @return {Number} The sum
11536      */
11537     sum : function(property, start, end){
11538         var rs = this.data.items, v = 0;
11539         start = start || 0;
11540         end = (end || end === 0) ? end : rs.length-1;
11541
11542         for(var i = start; i <= end; i++){
11543             v += (rs[i].data[property] || 0);
11544         }
11545         return v;
11546     },
11547
11548     /**
11549      * Filter the records by a specified property.
11550      * @param {String} field A field on your records
11551      * @param {String/RegExp} value Either a string that the field
11552      * should start with or a RegExp to test against the field
11553      * @param {Boolean} anyMatch True to match any part not just the beginning
11554      */
11555     filter : function(property, value, anyMatch){
11556         var fn = this.createFilterFn(property, value, anyMatch);
11557         return fn ? this.filterBy(fn) : this.clearFilter();
11558     },
11559
11560     /**
11561      * Filter by a function. The specified function will be called with each
11562      * record in this data source. If the function returns true the record is included,
11563      * otherwise it is filtered.
11564      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11565      * @param {Object} scope (optional) The scope of the function (defaults to this)
11566      */
11567     filterBy : function(fn, scope){
11568         this.snapshot = this.snapshot || this.data;
11569         this.data = this.queryBy(fn, scope||this);
11570         this.fireEvent("datachanged", this);
11571     },
11572
11573     /**
11574      * Query the records by a specified property.
11575      * @param {String} field A field on your records
11576      * @param {String/RegExp} value Either a string that the field
11577      * should start with or a RegExp to test against the field
11578      * @param {Boolean} anyMatch True to match any part not just the beginning
11579      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11580      */
11581     query : function(property, value, anyMatch){
11582         var fn = this.createFilterFn(property, value, anyMatch);
11583         return fn ? this.queryBy(fn) : this.data.clone();
11584     },
11585
11586     /**
11587      * Query by a function. The specified function will be called with each
11588      * record in this data source. If the function returns true the record is included
11589      * in the results.
11590      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11591      * @param {Object} scope (optional) The scope of the function (defaults to this)
11592       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11593      **/
11594     queryBy : function(fn, scope){
11595         var data = this.snapshot || this.data;
11596         return data.filterBy(fn, scope||this);
11597     },
11598
11599     /**
11600      * Collects unique values for a particular dataIndex from this store.
11601      * @param {String} dataIndex The property to collect
11602      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11603      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11604      * @return {Array} An array of the unique values
11605      **/
11606     collect : function(dataIndex, allowNull, bypassFilter){
11607         var d = (bypassFilter === true && this.snapshot) ?
11608                 this.snapshot.items : this.data.items;
11609         var v, sv, r = [], l = {};
11610         for(var i = 0, len = d.length; i < len; i++){
11611             v = d[i].data[dataIndex];
11612             sv = String(v);
11613             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11614                 l[sv] = true;
11615                 r[r.length] = v;
11616             }
11617         }
11618         return r;
11619     },
11620
11621     /**
11622      * Revert to a view of the Record cache with no filtering applied.
11623      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11624      */
11625     clearFilter : function(suppressEvent){
11626         if(this.snapshot && this.snapshot != this.data){
11627             this.data = this.snapshot;
11628             delete this.snapshot;
11629             if(suppressEvent !== true){
11630                 this.fireEvent("datachanged", this);
11631             }
11632         }
11633     },
11634
11635     // private
11636     afterEdit : function(record){
11637         if(this.modified.indexOf(record) == -1){
11638             this.modified.push(record);
11639         }
11640         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11641     },
11642     
11643     // private
11644     afterReject : function(record){
11645         this.modified.remove(record);
11646         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11647     },
11648
11649     // private
11650     afterCommit : function(record){
11651         this.modified.remove(record);
11652         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11653     },
11654
11655     /**
11656      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11657      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11658      */
11659     commitChanges : function(){
11660         var m = this.modified.slice(0);
11661         this.modified = [];
11662         for(var i = 0, len = m.length; i < len; i++){
11663             m[i].commit();
11664         }
11665     },
11666
11667     /**
11668      * Cancel outstanding changes on all changed records.
11669      */
11670     rejectChanges : function(){
11671         var m = this.modified.slice(0);
11672         this.modified = [];
11673         for(var i = 0, len = m.length; i < len; i++){
11674             m[i].reject();
11675         }
11676     },
11677
11678     onMetaChange : function(meta, rtype, o){
11679         this.recordType = rtype;
11680         this.fields = rtype.prototype.fields;
11681         delete this.snapshot;
11682         this.sortInfo = meta.sortInfo || this.sortInfo;
11683         this.modified = [];
11684         this.fireEvent('metachange', this, this.reader.meta);
11685     },
11686     
11687     moveIndex : function(data, type)
11688     {
11689         var index = this.indexOf(data);
11690         
11691         var newIndex = index + type;
11692         
11693         this.remove(data);
11694         
11695         this.insert(newIndex, data);
11696         
11697     }
11698 });/*
11699  * Based on:
11700  * Ext JS Library 1.1.1
11701  * Copyright(c) 2006-2007, Ext JS, LLC.
11702  *
11703  * Originally Released Under LGPL - original licence link has changed is not relivant.
11704  *
11705  * Fork - LGPL
11706  * <script type="text/javascript">
11707  */
11708
11709 /**
11710  * @class Roo.data.SimpleStore
11711  * @extends Roo.data.Store
11712  * Small helper class to make creating Stores from Array data easier.
11713  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11714  * @cfg {Array} fields An array of field definition objects, or field name strings.
11715  * @cfg {Array} data The multi-dimensional array of data
11716  * @constructor
11717  * @param {Object} config
11718  */
11719 Roo.data.SimpleStore = function(config){
11720     Roo.data.SimpleStore.superclass.constructor.call(this, {
11721         isLocal : true,
11722         reader: new Roo.data.ArrayReader({
11723                 id: config.id
11724             },
11725             Roo.data.Record.create(config.fields)
11726         ),
11727         proxy : new Roo.data.MemoryProxy(config.data)
11728     });
11729     this.load();
11730 };
11731 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11732  * Based on:
11733  * Ext JS Library 1.1.1
11734  * Copyright(c) 2006-2007, Ext JS, LLC.
11735  *
11736  * Originally Released Under LGPL - original licence link has changed is not relivant.
11737  *
11738  * Fork - LGPL
11739  * <script type="text/javascript">
11740  */
11741
11742 /**
11743 /**
11744  * @extends Roo.data.Store
11745  * @class Roo.data.JsonStore
11746  * Small helper class to make creating Stores for JSON data easier. <br/>
11747 <pre><code>
11748 var store = new Roo.data.JsonStore({
11749     url: 'get-images.php',
11750     root: 'images',
11751     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11752 });
11753 </code></pre>
11754  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11755  * JsonReader and HttpProxy (unless inline data is provided).</b>
11756  * @cfg {Array} fields An array of field definition objects, or field name strings.
11757  * @constructor
11758  * @param {Object} config
11759  */
11760 Roo.data.JsonStore = function(c){
11761     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11762         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11763         reader: new Roo.data.JsonReader(c, c.fields)
11764     }));
11765 };
11766 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11767  * Based on:
11768  * Ext JS Library 1.1.1
11769  * Copyright(c) 2006-2007, Ext JS, LLC.
11770  *
11771  * Originally Released Under LGPL - original licence link has changed is not relivant.
11772  *
11773  * Fork - LGPL
11774  * <script type="text/javascript">
11775  */
11776
11777  
11778 Roo.data.Field = function(config){
11779     if(typeof config == "string"){
11780         config = {name: config};
11781     }
11782     Roo.apply(this, config);
11783     
11784     if(!this.type){
11785         this.type = "auto";
11786     }
11787     
11788     var st = Roo.data.SortTypes;
11789     // named sortTypes are supported, here we look them up
11790     if(typeof this.sortType == "string"){
11791         this.sortType = st[this.sortType];
11792     }
11793     
11794     // set default sortType for strings and dates
11795     if(!this.sortType){
11796         switch(this.type){
11797             case "string":
11798                 this.sortType = st.asUCString;
11799                 break;
11800             case "date":
11801                 this.sortType = st.asDate;
11802                 break;
11803             default:
11804                 this.sortType = st.none;
11805         }
11806     }
11807
11808     // define once
11809     var stripRe = /[\$,%]/g;
11810
11811     // prebuilt conversion function for this field, instead of
11812     // switching every time we're reading a value
11813     if(!this.convert){
11814         var cv, dateFormat = this.dateFormat;
11815         switch(this.type){
11816             case "":
11817             case "auto":
11818             case undefined:
11819                 cv = function(v){ return v; };
11820                 break;
11821             case "string":
11822                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11823                 break;
11824             case "int":
11825                 cv = function(v){
11826                     return v !== undefined && v !== null && v !== '' ?
11827                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11828                     };
11829                 break;
11830             case "float":
11831                 cv = function(v){
11832                     return v !== undefined && v !== null && v !== '' ?
11833                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11834                     };
11835                 break;
11836             case "bool":
11837             case "boolean":
11838                 cv = function(v){ return v === true || v === "true" || v == 1; };
11839                 break;
11840             case "date":
11841                 cv = function(v){
11842                     if(!v){
11843                         return '';
11844                     }
11845                     if(v instanceof Date){
11846                         return v;
11847                     }
11848                     if(dateFormat){
11849                         if(dateFormat == "timestamp"){
11850                             return new Date(v*1000);
11851                         }
11852                         return Date.parseDate(v, dateFormat);
11853                     }
11854                     var parsed = Date.parse(v);
11855                     return parsed ? new Date(parsed) : null;
11856                 };
11857              break;
11858             
11859         }
11860         this.convert = cv;
11861     }
11862 };
11863
11864 Roo.data.Field.prototype = {
11865     dateFormat: null,
11866     defaultValue: "",
11867     mapping: null,
11868     sortType : null,
11869     sortDir : "ASC"
11870 };/*
11871  * Based on:
11872  * Ext JS Library 1.1.1
11873  * Copyright(c) 2006-2007, Ext JS, LLC.
11874  *
11875  * Originally Released Under LGPL - original licence link has changed is not relivant.
11876  *
11877  * Fork - LGPL
11878  * <script type="text/javascript">
11879  */
11880  
11881 // Base class for reading structured data from a data source.  This class is intended to be
11882 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11883
11884 /**
11885  * @class Roo.data.DataReader
11886  * Base class for reading structured data from a data source.  This class is intended to be
11887  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11888  */
11889
11890 Roo.data.DataReader = function(meta, recordType){
11891     
11892     this.meta = meta;
11893     
11894     this.recordType = recordType instanceof Array ? 
11895         Roo.data.Record.create(recordType) : recordType;
11896 };
11897
11898 Roo.data.DataReader.prototype = {
11899      /**
11900      * Create an empty record
11901      * @param {Object} data (optional) - overlay some values
11902      * @return {Roo.data.Record} record created.
11903      */
11904     newRow :  function(d) {
11905         var da =  {};
11906         this.recordType.prototype.fields.each(function(c) {
11907             switch( c.type) {
11908                 case 'int' : da[c.name] = 0; break;
11909                 case 'date' : da[c.name] = new Date(); break;
11910                 case 'float' : da[c.name] = 0.0; break;
11911                 case 'boolean' : da[c.name] = false; break;
11912                 default : da[c.name] = ""; break;
11913             }
11914             
11915         });
11916         return new this.recordType(Roo.apply(da, d));
11917     }
11918     
11919 };/*
11920  * Based on:
11921  * Ext JS Library 1.1.1
11922  * Copyright(c) 2006-2007, Ext JS, LLC.
11923  *
11924  * Originally Released Under LGPL - original licence link has changed is not relivant.
11925  *
11926  * Fork - LGPL
11927  * <script type="text/javascript">
11928  */
11929
11930 /**
11931  * @class Roo.data.DataProxy
11932  * @extends Roo.data.Observable
11933  * This class is an abstract base class for implementations which provide retrieval of
11934  * unformatted data objects.<br>
11935  * <p>
11936  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11937  * (of the appropriate type which knows how to parse the data object) to provide a block of
11938  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11939  * <p>
11940  * Custom implementations must implement the load method as described in
11941  * {@link Roo.data.HttpProxy#load}.
11942  */
11943 Roo.data.DataProxy = function(){
11944     this.addEvents({
11945         /**
11946          * @event beforeload
11947          * Fires before a network request is made to retrieve a data object.
11948          * @param {Object} This DataProxy object.
11949          * @param {Object} params The params parameter to the load function.
11950          */
11951         beforeload : true,
11952         /**
11953          * @event load
11954          * Fires before the load method's callback is called.
11955          * @param {Object} This DataProxy object.
11956          * @param {Object} o The data object.
11957          * @param {Object} arg The callback argument object passed to the load function.
11958          */
11959         load : true,
11960         /**
11961          * @event loadexception
11962          * Fires if an Exception occurs during data retrieval.
11963          * @param {Object} This DataProxy object.
11964          * @param {Object} o The data object.
11965          * @param {Object} arg The callback argument object passed to the load function.
11966          * @param {Object} e The Exception.
11967          */
11968         loadexception : true
11969     });
11970     Roo.data.DataProxy.superclass.constructor.call(this);
11971 };
11972
11973 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11974
11975     /**
11976      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11977      */
11978 /*
11979  * Based on:
11980  * Ext JS Library 1.1.1
11981  * Copyright(c) 2006-2007, Ext JS, LLC.
11982  *
11983  * Originally Released Under LGPL - original licence link has changed is not relivant.
11984  *
11985  * Fork - LGPL
11986  * <script type="text/javascript">
11987  */
11988 /**
11989  * @class Roo.data.MemoryProxy
11990  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11991  * to the Reader when its load method is called.
11992  * @constructor
11993  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11994  */
11995 Roo.data.MemoryProxy = function(data){
11996     if (data.data) {
11997         data = data.data;
11998     }
11999     Roo.data.MemoryProxy.superclass.constructor.call(this);
12000     this.data = data;
12001 };
12002
12003 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12004     
12005     /**
12006      * Load data from the requested source (in this case an in-memory
12007      * data object passed to the constructor), read the data object into
12008      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12009      * process that block using the passed callback.
12010      * @param {Object} params This parameter is not used by the MemoryProxy class.
12011      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12012      * object into a block of Roo.data.Records.
12013      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12014      * The function must be passed <ul>
12015      * <li>The Record block object</li>
12016      * <li>The "arg" argument from the load function</li>
12017      * <li>A boolean success indicator</li>
12018      * </ul>
12019      * @param {Object} scope The scope in which to call the callback
12020      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12021      */
12022     load : function(params, reader, callback, scope, arg){
12023         params = params || {};
12024         var result;
12025         try {
12026             result = reader.readRecords(this.data);
12027         }catch(e){
12028             this.fireEvent("loadexception", this, arg, null, e);
12029             callback.call(scope, null, arg, false);
12030             return;
12031         }
12032         callback.call(scope, result, arg, true);
12033     },
12034     
12035     // private
12036     update : function(params, records){
12037         
12038     }
12039 });/*
12040  * Based on:
12041  * Ext JS Library 1.1.1
12042  * Copyright(c) 2006-2007, Ext JS, LLC.
12043  *
12044  * Originally Released Under LGPL - original licence link has changed is not relivant.
12045  *
12046  * Fork - LGPL
12047  * <script type="text/javascript">
12048  */
12049 /**
12050  * @class Roo.data.HttpProxy
12051  * @extends Roo.data.DataProxy
12052  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12053  * configured to reference a certain URL.<br><br>
12054  * <p>
12055  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12056  * from which the running page was served.<br><br>
12057  * <p>
12058  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12059  * <p>
12060  * Be aware that to enable the browser to parse an XML document, the server must set
12061  * the Content-Type header in the HTTP response to "text/xml".
12062  * @constructor
12063  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12064  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12065  * will be used to make the request.
12066  */
12067 Roo.data.HttpProxy = function(conn){
12068     Roo.data.HttpProxy.superclass.constructor.call(this);
12069     // is conn a conn config or a real conn?
12070     this.conn = conn;
12071     this.useAjax = !conn || !conn.events;
12072   
12073 };
12074
12075 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12076     // thse are take from connection...
12077     
12078     /**
12079      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12080      */
12081     /**
12082      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12083      * extra parameters to each request made by this object. (defaults to undefined)
12084      */
12085     /**
12086      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12087      *  to each request made by this object. (defaults to undefined)
12088      */
12089     /**
12090      * @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)
12091      */
12092     /**
12093      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12094      */
12095      /**
12096      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12097      * @type Boolean
12098      */
12099   
12100
12101     /**
12102      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12103      * @type Boolean
12104      */
12105     /**
12106      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12107      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12108      * a finer-grained basis than the DataProxy events.
12109      */
12110     getConnection : function(){
12111         return this.useAjax ? Roo.Ajax : this.conn;
12112     },
12113
12114     /**
12115      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12116      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12117      * process that block using the passed callback.
12118      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12119      * for the request to the remote server.
12120      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12121      * object into a block of Roo.data.Records.
12122      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12123      * The function must be passed <ul>
12124      * <li>The Record block object</li>
12125      * <li>The "arg" argument from the load function</li>
12126      * <li>A boolean success indicator</li>
12127      * </ul>
12128      * @param {Object} scope The scope in which to call the callback
12129      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12130      */
12131     load : function(params, reader, callback, scope, arg){
12132         if(this.fireEvent("beforeload", this, params) !== false){
12133             var  o = {
12134                 params : params || {},
12135                 request: {
12136                     callback : callback,
12137                     scope : scope,
12138                     arg : arg
12139                 },
12140                 reader: reader,
12141                 callback : this.loadResponse,
12142                 scope: this
12143             };
12144             if(this.useAjax){
12145                 Roo.applyIf(o, this.conn);
12146                 if(this.activeRequest){
12147                     Roo.Ajax.abort(this.activeRequest);
12148                 }
12149                 this.activeRequest = Roo.Ajax.request(o);
12150             }else{
12151                 this.conn.request(o);
12152             }
12153         }else{
12154             callback.call(scope||this, null, arg, false);
12155         }
12156     },
12157
12158     // private
12159     loadResponse : function(o, success, response){
12160         delete this.activeRequest;
12161         if(!success){
12162             this.fireEvent("loadexception", this, o, response);
12163             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12164             return;
12165         }
12166         var result;
12167         try {
12168             result = o.reader.read(response);
12169         }catch(e){
12170             this.fireEvent("loadexception", this, o, response, e);
12171             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12172             return;
12173         }
12174         
12175         this.fireEvent("load", this, o, o.request.arg);
12176         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12177     },
12178
12179     // private
12180     update : function(dataSet){
12181
12182     },
12183
12184     // private
12185     updateResponse : function(dataSet){
12186
12187     }
12188 });/*
12189  * Based on:
12190  * Ext JS Library 1.1.1
12191  * Copyright(c) 2006-2007, Ext JS, LLC.
12192  *
12193  * Originally Released Under LGPL - original licence link has changed is not relivant.
12194  *
12195  * Fork - LGPL
12196  * <script type="text/javascript">
12197  */
12198
12199 /**
12200  * @class Roo.data.ScriptTagProxy
12201  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12202  * other than the originating domain of the running page.<br><br>
12203  * <p>
12204  * <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
12205  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12206  * <p>
12207  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12208  * source code that is used as the source inside a &lt;script> tag.<br><br>
12209  * <p>
12210  * In order for the browser to process the returned data, the server must wrap the data object
12211  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12212  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12213  * depending on whether the callback name was passed:
12214  * <p>
12215  * <pre><code>
12216 boolean scriptTag = false;
12217 String cb = request.getParameter("callback");
12218 if (cb != null) {
12219     scriptTag = true;
12220     response.setContentType("text/javascript");
12221 } else {
12222     response.setContentType("application/x-json");
12223 }
12224 Writer out = response.getWriter();
12225 if (scriptTag) {
12226     out.write(cb + "(");
12227 }
12228 out.print(dataBlock.toJsonString());
12229 if (scriptTag) {
12230     out.write(");");
12231 }
12232 </pre></code>
12233  *
12234  * @constructor
12235  * @param {Object} config A configuration object.
12236  */
12237 Roo.data.ScriptTagProxy = function(config){
12238     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12239     Roo.apply(this, config);
12240     this.head = document.getElementsByTagName("head")[0];
12241 };
12242
12243 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12244
12245 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12246     /**
12247      * @cfg {String} url The URL from which to request the data object.
12248      */
12249     /**
12250      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12251      */
12252     timeout : 30000,
12253     /**
12254      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12255      * the server the name of the callback function set up by the load call to process the returned data object.
12256      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12257      * javascript output which calls this named function passing the data object as its only parameter.
12258      */
12259     callbackParam : "callback",
12260     /**
12261      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12262      * name to the request.
12263      */
12264     nocache : true,
12265
12266     /**
12267      * Load data from the configured URL, read the data object into
12268      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12269      * process that block using the passed callback.
12270      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12271      * for the request to the remote server.
12272      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12273      * object into a block of Roo.data.Records.
12274      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12275      * The function must be passed <ul>
12276      * <li>The Record block object</li>
12277      * <li>The "arg" argument from the load function</li>
12278      * <li>A boolean success indicator</li>
12279      * </ul>
12280      * @param {Object} scope The scope in which to call the callback
12281      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12282      */
12283     load : function(params, reader, callback, scope, arg){
12284         if(this.fireEvent("beforeload", this, params) !== false){
12285
12286             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12287
12288             var url = this.url;
12289             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12290             if(this.nocache){
12291                 url += "&_dc=" + (new Date().getTime());
12292             }
12293             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12294             var trans = {
12295                 id : transId,
12296                 cb : "stcCallback"+transId,
12297                 scriptId : "stcScript"+transId,
12298                 params : params,
12299                 arg : arg,
12300                 url : url,
12301                 callback : callback,
12302                 scope : scope,
12303                 reader : reader
12304             };
12305             var conn = this;
12306
12307             window[trans.cb] = function(o){
12308                 conn.handleResponse(o, trans);
12309             };
12310
12311             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12312
12313             if(this.autoAbort !== false){
12314                 this.abort();
12315             }
12316
12317             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12318
12319             var script = document.createElement("script");
12320             script.setAttribute("src", url);
12321             script.setAttribute("type", "text/javascript");
12322             script.setAttribute("id", trans.scriptId);
12323             this.head.appendChild(script);
12324
12325             this.trans = trans;
12326         }else{
12327             callback.call(scope||this, null, arg, false);
12328         }
12329     },
12330
12331     // private
12332     isLoading : function(){
12333         return this.trans ? true : false;
12334     },
12335
12336     /**
12337      * Abort the current server request.
12338      */
12339     abort : function(){
12340         if(this.isLoading()){
12341             this.destroyTrans(this.trans);
12342         }
12343     },
12344
12345     // private
12346     destroyTrans : function(trans, isLoaded){
12347         this.head.removeChild(document.getElementById(trans.scriptId));
12348         clearTimeout(trans.timeoutId);
12349         if(isLoaded){
12350             window[trans.cb] = undefined;
12351             try{
12352                 delete window[trans.cb];
12353             }catch(e){}
12354         }else{
12355             // if hasn't been loaded, wait for load to remove it to prevent script error
12356             window[trans.cb] = function(){
12357                 window[trans.cb] = undefined;
12358                 try{
12359                     delete window[trans.cb];
12360                 }catch(e){}
12361             };
12362         }
12363     },
12364
12365     // private
12366     handleResponse : function(o, trans){
12367         this.trans = false;
12368         this.destroyTrans(trans, true);
12369         var result;
12370         try {
12371             result = trans.reader.readRecords(o);
12372         }catch(e){
12373             this.fireEvent("loadexception", this, o, trans.arg, e);
12374             trans.callback.call(trans.scope||window, null, trans.arg, false);
12375             return;
12376         }
12377         this.fireEvent("load", this, o, trans.arg);
12378         trans.callback.call(trans.scope||window, result, trans.arg, true);
12379     },
12380
12381     // private
12382     handleFailure : function(trans){
12383         this.trans = false;
12384         this.destroyTrans(trans, false);
12385         this.fireEvent("loadexception", this, null, trans.arg);
12386         trans.callback.call(trans.scope||window, null, trans.arg, false);
12387     }
12388 });/*
12389  * Based on:
12390  * Ext JS Library 1.1.1
12391  * Copyright(c) 2006-2007, Ext JS, LLC.
12392  *
12393  * Originally Released Under LGPL - original licence link has changed is not relivant.
12394  *
12395  * Fork - LGPL
12396  * <script type="text/javascript">
12397  */
12398
12399 /**
12400  * @class Roo.data.JsonReader
12401  * @extends Roo.data.DataReader
12402  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12403  * based on mappings in a provided Roo.data.Record constructor.
12404  * 
12405  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12406  * in the reply previously. 
12407  * 
12408  * <p>
12409  * Example code:
12410  * <pre><code>
12411 var RecordDef = Roo.data.Record.create([
12412     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12413     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12414 ]);
12415 var myReader = new Roo.data.JsonReader({
12416     totalProperty: "results",    // The property which contains the total dataset size (optional)
12417     root: "rows",                // The property which contains an Array of row objects
12418     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12419 }, RecordDef);
12420 </code></pre>
12421  * <p>
12422  * This would consume a JSON file like this:
12423  * <pre><code>
12424 { 'results': 2, 'rows': [
12425     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12426     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12427 }
12428 </code></pre>
12429  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12430  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12431  * paged from the remote server.
12432  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12433  * @cfg {String} root name of the property which contains the Array of row objects.
12434  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12435  * @cfg {Array} fields Array of field definition objects
12436  * @constructor
12437  * Create a new JsonReader
12438  * @param {Object} meta Metadata configuration options
12439  * @param {Object} recordType Either an Array of field definition objects,
12440  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12441  */
12442 Roo.data.JsonReader = function(meta, recordType){
12443     
12444     meta = meta || {};
12445     // set some defaults:
12446     Roo.applyIf(meta, {
12447         totalProperty: 'total',
12448         successProperty : 'success',
12449         root : 'data',
12450         id : 'id'
12451     });
12452     
12453     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12454 };
12455 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12456     
12457     /**
12458      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12459      * Used by Store query builder to append _requestMeta to params.
12460      * 
12461      */
12462     metaFromRemote : false,
12463     /**
12464      * This method is only used by a DataProxy which has retrieved data from a remote server.
12465      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12466      * @return {Object} data A data block which is used by an Roo.data.Store object as
12467      * a cache of Roo.data.Records.
12468      */
12469     read : function(response){
12470         var json = response.responseText;
12471        
12472         var o = /* eval:var:o */ eval("("+json+")");
12473         if(!o) {
12474             throw {message: "JsonReader.read: Json object not found"};
12475         }
12476         
12477         if(o.metaData){
12478             
12479             delete this.ef;
12480             this.metaFromRemote = true;
12481             this.meta = o.metaData;
12482             this.recordType = Roo.data.Record.create(o.metaData.fields);
12483             this.onMetaChange(this.meta, this.recordType, o);
12484         }
12485         return this.readRecords(o);
12486     },
12487
12488     // private function a store will implement
12489     onMetaChange : function(meta, recordType, o){
12490
12491     },
12492
12493     /**
12494          * @ignore
12495          */
12496     simpleAccess: function(obj, subsc) {
12497         return obj[subsc];
12498     },
12499
12500         /**
12501          * @ignore
12502          */
12503     getJsonAccessor: function(){
12504         var re = /[\[\.]/;
12505         return function(expr) {
12506             try {
12507                 return(re.test(expr))
12508                     ? new Function("obj", "return obj." + expr)
12509                     : function(obj){
12510                         return obj[expr];
12511                     };
12512             } catch(e){}
12513             return Roo.emptyFn;
12514         };
12515     }(),
12516
12517     /**
12518      * Create a data block containing Roo.data.Records from an XML document.
12519      * @param {Object} o An object which contains an Array of row objects in the property specified
12520      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12521      * which contains the total size of the dataset.
12522      * @return {Object} data A data block which is used by an Roo.data.Store object as
12523      * a cache of Roo.data.Records.
12524      */
12525     readRecords : function(o){
12526         /**
12527          * After any data loads, the raw JSON data is available for further custom processing.
12528          * @type Object
12529          */
12530         this.o = o;
12531         var s = this.meta, Record = this.recordType,
12532             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12533
12534 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12535         if (!this.ef) {
12536             if(s.totalProperty) {
12537                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12538                 }
12539                 if(s.successProperty) {
12540                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12541                 }
12542                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12543                 if (s.id) {
12544                         var g = this.getJsonAccessor(s.id);
12545                         this.getId = function(rec) {
12546                                 var r = g(rec);  
12547                                 return (r === undefined || r === "") ? null : r;
12548                         };
12549                 } else {
12550                         this.getId = function(){return null;};
12551                 }
12552             this.ef = [];
12553             for(var jj = 0; jj < fl; jj++){
12554                 f = fi[jj];
12555                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12556                 this.ef[jj] = this.getJsonAccessor(map);
12557             }
12558         }
12559
12560         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12561         if(s.totalProperty){
12562             var vt = parseInt(this.getTotal(o), 10);
12563             if(!isNaN(vt)){
12564                 totalRecords = vt;
12565             }
12566         }
12567         if(s.successProperty){
12568             var vs = this.getSuccess(o);
12569             if(vs === false || vs === 'false'){
12570                 success = false;
12571             }
12572         }
12573         var records = [];
12574         for(var i = 0; i < c; i++){
12575                 var n = root[i];
12576             var values = {};
12577             var id = this.getId(n);
12578             for(var j = 0; j < fl; j++){
12579                 f = fi[j];
12580             var v = this.ef[j](n);
12581             if (!f.convert) {
12582                 Roo.log('missing convert for ' + f.name);
12583                 Roo.log(f);
12584                 continue;
12585             }
12586             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12587             }
12588             var record = new Record(values, id);
12589             record.json = n;
12590             records[i] = record;
12591         }
12592         return {
12593             raw : o,
12594             success : success,
12595             records : records,
12596             totalRecords : totalRecords
12597         };
12598     }
12599 });/*
12600  * Based on:
12601  * Ext JS Library 1.1.1
12602  * Copyright(c) 2006-2007, Ext JS, LLC.
12603  *
12604  * Originally Released Under LGPL - original licence link has changed is not relivant.
12605  *
12606  * Fork - LGPL
12607  * <script type="text/javascript">
12608  */
12609
12610 /**
12611  * @class Roo.data.ArrayReader
12612  * @extends Roo.data.DataReader
12613  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12614  * Each element of that Array represents a row of data fields. The
12615  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12616  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12617  * <p>
12618  * Example code:.
12619  * <pre><code>
12620 var RecordDef = Roo.data.Record.create([
12621     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12622     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12623 ]);
12624 var myReader = new Roo.data.ArrayReader({
12625     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12626 }, RecordDef);
12627 </code></pre>
12628  * <p>
12629  * This would consume an Array like this:
12630  * <pre><code>
12631 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12632   </code></pre>
12633  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12634  * @constructor
12635  * Create a new JsonReader
12636  * @param {Object} meta Metadata configuration options.
12637  * @param {Object} recordType Either an Array of field definition objects
12638  * as specified to {@link Roo.data.Record#create},
12639  * or an {@link Roo.data.Record} object
12640  * created using {@link Roo.data.Record#create}.
12641  */
12642 Roo.data.ArrayReader = function(meta, recordType){
12643     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12644 };
12645
12646 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12647     /**
12648      * Create a data block containing Roo.data.Records from an XML document.
12649      * @param {Object} o An Array of row objects which represents the dataset.
12650      * @return {Object} data A data block which is used by an Roo.data.Store object as
12651      * a cache of Roo.data.Records.
12652      */
12653     readRecords : function(o){
12654         var sid = this.meta ? this.meta.id : null;
12655         var recordType = this.recordType, fields = recordType.prototype.fields;
12656         var records = [];
12657         var root = o;
12658             for(var i = 0; i < root.length; i++){
12659                     var n = root[i];
12660                 var values = {};
12661                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12662                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12663                 var f = fields.items[j];
12664                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12665                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12666                 v = f.convert(v);
12667                 values[f.name] = v;
12668             }
12669                 var record = new recordType(values, id);
12670                 record.json = n;
12671                 records[records.length] = record;
12672             }
12673             return {
12674                 records : records,
12675                 totalRecords : records.length
12676             };
12677     }
12678 });/*
12679  * - LGPL
12680  * * 
12681  */
12682
12683 /**
12684  * @class Roo.bootstrap.ComboBox
12685  * @extends Roo.bootstrap.TriggerField
12686  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12687  * @cfg {Boolean} append (true|false) default false
12688  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12689  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12690  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12691  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12692  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12693  * @cfg {Boolean} animate default true
12694  * @cfg {Boolean} emptyResultText only for touch device
12695  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12696  * @cfg {String} emptyTitle default ''
12697  * @constructor
12698  * Create a new ComboBox.
12699  * @param {Object} config Configuration options
12700  */
12701 Roo.bootstrap.ComboBox = function(config){
12702     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12703     this.addEvents({
12704         /**
12705          * @event expand
12706          * Fires when the dropdown list is expanded
12707         * @param {Roo.bootstrap.ComboBox} combo This combo box
12708         */
12709         'expand' : true,
12710         /**
12711          * @event collapse
12712          * Fires when the dropdown list is collapsed
12713         * @param {Roo.bootstrap.ComboBox} combo This combo box
12714         */
12715         'collapse' : true,
12716         /**
12717          * @event beforeselect
12718          * Fires before a list item is selected. Return false to cancel the selection.
12719         * @param {Roo.bootstrap.ComboBox} combo This combo box
12720         * @param {Roo.data.Record} record The data record returned from the underlying store
12721         * @param {Number} index The index of the selected item in the dropdown list
12722         */
12723         'beforeselect' : true,
12724         /**
12725          * @event select
12726          * Fires when a list item is selected
12727         * @param {Roo.bootstrap.ComboBox} combo This combo box
12728         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12729         * @param {Number} index The index of the selected item in the dropdown list
12730         */
12731         'select' : true,
12732         /**
12733          * @event beforequery
12734          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12735          * The event object passed has these properties:
12736         * @param {Roo.bootstrap.ComboBox} combo This combo box
12737         * @param {String} query The query
12738         * @param {Boolean} forceAll true to force "all" query
12739         * @param {Boolean} cancel true to cancel the query
12740         * @param {Object} e The query event object
12741         */
12742         'beforequery': true,
12743          /**
12744          * @event add
12745          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12746         * @param {Roo.bootstrap.ComboBox} combo This combo box
12747         */
12748         'add' : true,
12749         /**
12750          * @event edit
12751          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12752         * @param {Roo.bootstrap.ComboBox} combo This combo box
12753         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12754         */
12755         'edit' : true,
12756         /**
12757          * @event remove
12758          * Fires when the remove value from the combobox array
12759         * @param {Roo.bootstrap.ComboBox} combo This combo box
12760         */
12761         'remove' : true,
12762         /**
12763          * @event afterremove
12764          * Fires when the remove value from the combobox array
12765         * @param {Roo.bootstrap.ComboBox} combo This combo box
12766         */
12767         'afterremove' : true,
12768         /**
12769          * @event specialfilter
12770          * Fires when specialfilter
12771             * @param {Roo.bootstrap.ComboBox} combo This combo box
12772             */
12773         'specialfilter' : true,
12774         /**
12775          * @event tick
12776          * Fires when tick the element
12777             * @param {Roo.bootstrap.ComboBox} combo This combo box
12778             */
12779         'tick' : true,
12780         /**
12781          * @event touchviewdisplay
12782          * Fires when touch view require special display (default is using displayField)
12783             * @param {Roo.bootstrap.ComboBox} combo This combo box
12784             * @param {Object} cfg set html .
12785             */
12786         'touchviewdisplay' : true
12787         
12788     });
12789     
12790     this.item = [];
12791     this.tickItems = [];
12792     
12793     this.selectedIndex = -1;
12794     if(this.mode == 'local'){
12795         if(config.queryDelay === undefined){
12796             this.queryDelay = 10;
12797         }
12798         if(config.minChars === undefined){
12799             this.minChars = 0;
12800         }
12801     }
12802 };
12803
12804 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12805      
12806     /**
12807      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12808      * rendering into an Roo.Editor, defaults to false)
12809      */
12810     /**
12811      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12812      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12813      */
12814     /**
12815      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12816      */
12817     /**
12818      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12819      * the dropdown list (defaults to undefined, with no header element)
12820      */
12821
12822      /**
12823      * @cfg {String/Roo.Template} tpl The template to use to render the output
12824      */
12825      
12826      /**
12827      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12828      */
12829     listWidth: undefined,
12830     /**
12831      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12832      * mode = 'remote' or 'text' if mode = 'local')
12833      */
12834     displayField: undefined,
12835     
12836     /**
12837      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12838      * mode = 'remote' or 'value' if mode = 'local'). 
12839      * Note: use of a valueField requires the user make a selection
12840      * in order for a value to be mapped.
12841      */
12842     valueField: undefined,
12843     /**
12844      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12845      */
12846     modalTitle : '',
12847     
12848     /**
12849      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12850      * field's data value (defaults to the underlying DOM element's name)
12851      */
12852     hiddenName: undefined,
12853     /**
12854      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12855      */
12856     listClass: '',
12857     /**
12858      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12859      */
12860     selectedClass: 'active',
12861     
12862     /**
12863      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12864      */
12865     shadow:'sides',
12866     /**
12867      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12868      * anchor positions (defaults to 'tl-bl')
12869      */
12870     listAlign: 'tl-bl?',
12871     /**
12872      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12873      */
12874     maxHeight: 300,
12875     /**
12876      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12877      * query specified by the allQuery config option (defaults to 'query')
12878      */
12879     triggerAction: 'query',
12880     /**
12881      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12882      * (defaults to 4, does not apply if editable = false)
12883      */
12884     minChars : 4,
12885     /**
12886      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12887      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12888      */
12889     typeAhead: false,
12890     /**
12891      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12892      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12893      */
12894     queryDelay: 500,
12895     /**
12896      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12897      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12898      */
12899     pageSize: 0,
12900     /**
12901      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12902      * when editable = true (defaults to false)
12903      */
12904     selectOnFocus:false,
12905     /**
12906      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12907      */
12908     queryParam: 'query',
12909     /**
12910      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12911      * when mode = 'remote' (defaults to 'Loading...')
12912      */
12913     loadingText: 'Loading...',
12914     /**
12915      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12916      */
12917     resizable: false,
12918     /**
12919      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12920      */
12921     handleHeight : 8,
12922     /**
12923      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12924      * traditional select (defaults to true)
12925      */
12926     editable: true,
12927     /**
12928      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12929      */
12930     allQuery: '',
12931     /**
12932      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12933      */
12934     mode: 'remote',
12935     /**
12936      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12937      * listWidth has a higher value)
12938      */
12939     minListWidth : 70,
12940     /**
12941      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12942      * allow the user to set arbitrary text into the field (defaults to false)
12943      */
12944     forceSelection:false,
12945     /**
12946      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12947      * if typeAhead = true (defaults to 250)
12948      */
12949     typeAheadDelay : 250,
12950     /**
12951      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12952      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12953      */
12954     valueNotFoundText : undefined,
12955     /**
12956      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12957      */
12958     blockFocus : false,
12959     
12960     /**
12961      * @cfg {Boolean} disableClear Disable showing of clear button.
12962      */
12963     disableClear : false,
12964     /**
12965      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12966      */
12967     alwaysQuery : false,
12968     
12969     /**
12970      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12971      */
12972     multiple : false,
12973     
12974     /**
12975      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12976      */
12977     invalidClass : "has-warning",
12978     
12979     /**
12980      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12981      */
12982     validClass : "has-success",
12983     
12984     /**
12985      * @cfg {Boolean} specialFilter (true|false) special filter default false
12986      */
12987     specialFilter : false,
12988     
12989     /**
12990      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12991      */
12992     mobileTouchView : true,
12993     
12994     /**
12995      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12996      */
12997     useNativeIOS : false,
12998     
12999     ios_options : false,
13000     
13001     //private
13002     addicon : false,
13003     editicon: false,
13004     
13005     page: 0,
13006     hasQuery: false,
13007     append: false,
13008     loadNext: false,
13009     autoFocus : true,
13010     tickable : false,
13011     btnPosition : 'right',
13012     triggerList : true,
13013     showToggleBtn : true,
13014     animate : true,
13015     emptyResultText: 'Empty',
13016     triggerText : 'Select',
13017     emptyTitle : '',
13018     
13019     // element that contains real text value.. (when hidden is used..)
13020     
13021     getAutoCreate : function()
13022     {   
13023         var cfg = false;
13024         //render
13025         /*
13026          * Render classic select for iso
13027          */
13028         
13029         if(Roo.isIOS && this.useNativeIOS){
13030             cfg = this.getAutoCreateNativeIOS();
13031             return cfg;
13032         }
13033         
13034         /*
13035          * Touch Devices
13036          */
13037         
13038         if(Roo.isTouch && this.mobileTouchView){
13039             cfg = this.getAutoCreateTouchView();
13040             return cfg;;
13041         }
13042         
13043         /*
13044          *  Normal ComboBox
13045          */
13046         if(!this.tickable){
13047             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13048             return cfg;
13049         }
13050         
13051         /*
13052          *  ComboBox with tickable selections
13053          */
13054              
13055         var align = this.labelAlign || this.parentLabelAlign();
13056         
13057         cfg = {
13058             cls : 'form-group roo-combobox-tickable' //input-group
13059         };
13060         
13061         var btn_text_select = '';
13062         var btn_text_done = '';
13063         var btn_text_cancel = '';
13064         
13065         if (this.btn_text_show) {
13066             btn_text_select = 'Select';
13067             btn_text_done = 'Done';
13068             btn_text_cancel = 'Cancel'; 
13069         }
13070         
13071         var buttons = {
13072             tag : 'div',
13073             cls : 'tickable-buttons',
13074             cn : [
13075                 {
13076                     tag : 'button',
13077                     type : 'button',
13078                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13079                     //html : this.triggerText
13080                     html: btn_text_select
13081                 },
13082                 {
13083                     tag : 'button',
13084                     type : 'button',
13085                     name : 'ok',
13086                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13087                     //html : 'Done'
13088                     html: btn_text_done
13089                 },
13090                 {
13091                     tag : 'button',
13092                     type : 'button',
13093                     name : 'cancel',
13094                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13095                     //html : 'Cancel'
13096                     html: btn_text_cancel
13097                 }
13098             ]
13099         };
13100         
13101         if(this.editable){
13102             buttons.cn.unshift({
13103                 tag: 'input',
13104                 cls: 'roo-select2-search-field-input'
13105             });
13106         }
13107         
13108         var _this = this;
13109         
13110         Roo.each(buttons.cn, function(c){
13111             if (_this.size) {
13112                 c.cls += ' btn-' + _this.size;
13113             }
13114
13115             if (_this.disabled) {
13116                 c.disabled = true;
13117             }
13118         });
13119         
13120         var box = {
13121             tag: 'div',
13122             cn: [
13123                 {
13124                     tag: 'input',
13125                     type : 'hidden',
13126                     cls: 'form-hidden-field'
13127                 },
13128                 {
13129                     tag: 'ul',
13130                     cls: 'roo-select2-choices',
13131                     cn:[
13132                         {
13133                             tag: 'li',
13134                             cls: 'roo-select2-search-field',
13135                             cn: [
13136                                 buttons
13137                             ]
13138                         }
13139                     ]
13140                 }
13141             ]
13142         };
13143         
13144         var combobox = {
13145             cls: 'roo-select2-container input-group roo-select2-container-multi',
13146             cn: [
13147                 box
13148 //                {
13149 //                    tag: 'ul',
13150 //                    cls: 'typeahead typeahead-long dropdown-menu',
13151 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13152 //                }
13153             ]
13154         };
13155         
13156         if(this.hasFeedback && !this.allowBlank){
13157             
13158             var feedback = {
13159                 tag: 'span',
13160                 cls: 'glyphicon form-control-feedback'
13161             };
13162
13163             combobox.cn.push(feedback);
13164         }
13165         
13166         
13167         if (align ==='left' && this.fieldLabel.length) {
13168             
13169             cfg.cls += ' roo-form-group-label-left';
13170             
13171             cfg.cn = [
13172                 {
13173                     tag : 'i',
13174                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13175                     tooltip : 'This field is required'
13176                 },
13177                 {
13178                     tag: 'label',
13179                     'for' :  id,
13180                     cls : 'control-label',
13181                     html : this.fieldLabel
13182
13183                 },
13184                 {
13185                     cls : "", 
13186                     cn: [
13187                         combobox
13188                     ]
13189                 }
13190
13191             ];
13192             
13193             var labelCfg = cfg.cn[1];
13194             var contentCfg = cfg.cn[2];
13195             
13196
13197             if(this.indicatorpos == 'right'){
13198                 
13199                 cfg.cn = [
13200                     {
13201                         tag: 'label',
13202                         'for' :  id,
13203                         cls : 'control-label',
13204                         cn : [
13205                             {
13206                                 tag : 'span',
13207                                 html : this.fieldLabel
13208                             },
13209                             {
13210                                 tag : 'i',
13211                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13212                                 tooltip : 'This field is required'
13213                             }
13214                         ]
13215                     },
13216                     {
13217                         cls : "",
13218                         cn: [
13219                             combobox
13220                         ]
13221                     }
13222
13223                 ];
13224                 
13225                 
13226                 
13227                 labelCfg = cfg.cn[0];
13228                 contentCfg = cfg.cn[1];
13229             
13230             }
13231             
13232             if(this.labelWidth > 12){
13233                 labelCfg.style = "width: " + this.labelWidth + 'px';
13234             }
13235             
13236             if(this.labelWidth < 13 && this.labelmd == 0){
13237                 this.labelmd = this.labelWidth;
13238             }
13239             
13240             if(this.labellg > 0){
13241                 labelCfg.cls += ' col-lg-' + this.labellg;
13242                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13243             }
13244             
13245             if(this.labelmd > 0){
13246                 labelCfg.cls += ' col-md-' + this.labelmd;
13247                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13248             }
13249             
13250             if(this.labelsm > 0){
13251                 labelCfg.cls += ' col-sm-' + this.labelsm;
13252                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13253             }
13254             
13255             if(this.labelxs > 0){
13256                 labelCfg.cls += ' col-xs-' + this.labelxs;
13257                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13258             }
13259                 
13260                 
13261         } else if ( this.fieldLabel.length) {
13262 //                Roo.log(" label");
13263                  cfg.cn = [
13264                     {
13265                         tag : 'i',
13266                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13267                         tooltip : 'This field is required'
13268                     },
13269                     {
13270                         tag: 'label',
13271                         //cls : 'input-group-addon',
13272                         html : this.fieldLabel
13273                     },
13274                     combobox
13275                 ];
13276                 
13277                 if(this.indicatorpos == 'right'){
13278                     cfg.cn = [
13279                         {
13280                             tag: 'label',
13281                             //cls : 'input-group-addon',
13282                             html : this.fieldLabel
13283                         },
13284                         {
13285                             tag : 'i',
13286                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13287                             tooltip : 'This field is required'
13288                         },
13289                         combobox
13290                     ];
13291                     
13292                 }
13293
13294         } else {
13295             
13296 //                Roo.log(" no label && no align");
13297                 cfg = combobox
13298                      
13299                 
13300         }
13301          
13302         var settings=this;
13303         ['xs','sm','md','lg'].map(function(size){
13304             if (settings[size]) {
13305                 cfg.cls += ' col-' + size + '-' + settings[size];
13306             }
13307         });
13308         
13309         return cfg;
13310         
13311     },
13312     
13313     _initEventsCalled : false,
13314     
13315     // private
13316     initEvents: function()
13317     {   
13318         if (this._initEventsCalled) { // as we call render... prevent looping...
13319             return;
13320         }
13321         this._initEventsCalled = true;
13322         
13323         if (!this.store) {
13324             throw "can not find store for combo";
13325         }
13326         
13327         this.indicator = this.indicatorEl();
13328         
13329         this.store = Roo.factory(this.store, Roo.data);
13330         this.store.parent = this;
13331         
13332         // if we are building from html. then this element is so complex, that we can not really
13333         // use the rendered HTML.
13334         // so we have to trash and replace the previous code.
13335         if (Roo.XComponent.build_from_html) {
13336             // remove this element....
13337             var e = this.el.dom, k=0;
13338             while (e ) { e = e.previousSibling;  ++k;}
13339
13340             this.el.remove();
13341             
13342             this.el=false;
13343             this.rendered = false;
13344             
13345             this.render(this.parent().getChildContainer(true), k);
13346         }
13347         
13348         if(Roo.isIOS && this.useNativeIOS){
13349             this.initIOSView();
13350             return;
13351         }
13352         
13353         /*
13354          * Touch Devices
13355          */
13356         
13357         if(Roo.isTouch && this.mobileTouchView){
13358             this.initTouchView();
13359             return;
13360         }
13361         
13362         if(this.tickable){
13363             this.initTickableEvents();
13364             return;
13365         }
13366         
13367         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13368         
13369         if(this.hiddenName){
13370             
13371             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13372             
13373             this.hiddenField.dom.value =
13374                 this.hiddenValue !== undefined ? this.hiddenValue :
13375                 this.value !== undefined ? this.value : '';
13376
13377             // prevent input submission
13378             this.el.dom.removeAttribute('name');
13379             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13380              
13381              
13382         }
13383         //if(Roo.isGecko){
13384         //    this.el.dom.setAttribute('autocomplete', 'off');
13385         //}
13386         
13387         var cls = 'x-combo-list';
13388         
13389         //this.list = new Roo.Layer({
13390         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13391         //});
13392         
13393         var _this = this;
13394         
13395         (function(){
13396             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13397             _this.list.setWidth(lw);
13398         }).defer(100);
13399         
13400         this.list.on('mouseover', this.onViewOver, this);
13401         this.list.on('mousemove', this.onViewMove, this);
13402         this.list.on('scroll', this.onViewScroll, this);
13403         
13404         /*
13405         this.list.swallowEvent('mousewheel');
13406         this.assetHeight = 0;
13407
13408         if(this.title){
13409             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13410             this.assetHeight += this.header.getHeight();
13411         }
13412
13413         this.innerList = this.list.createChild({cls:cls+'-inner'});
13414         this.innerList.on('mouseover', this.onViewOver, this);
13415         this.innerList.on('mousemove', this.onViewMove, this);
13416         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13417         
13418         if(this.allowBlank && !this.pageSize && !this.disableClear){
13419             this.footer = this.list.createChild({cls:cls+'-ft'});
13420             this.pageTb = new Roo.Toolbar(this.footer);
13421            
13422         }
13423         if(this.pageSize){
13424             this.footer = this.list.createChild({cls:cls+'-ft'});
13425             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13426                     {pageSize: this.pageSize});
13427             
13428         }
13429         
13430         if (this.pageTb && this.allowBlank && !this.disableClear) {
13431             var _this = this;
13432             this.pageTb.add(new Roo.Toolbar.Fill(), {
13433                 cls: 'x-btn-icon x-btn-clear',
13434                 text: '&#160;',
13435                 handler: function()
13436                 {
13437                     _this.collapse();
13438                     _this.clearValue();
13439                     _this.onSelect(false, -1);
13440                 }
13441             });
13442         }
13443         if (this.footer) {
13444             this.assetHeight += this.footer.getHeight();
13445         }
13446         */
13447             
13448         if(!this.tpl){
13449             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13450         }
13451
13452         this.view = new Roo.View(this.list, this.tpl, {
13453             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13454         });
13455         //this.view.wrapEl.setDisplayed(false);
13456         this.view.on('click', this.onViewClick, this);
13457         
13458         
13459         this.store.on('beforeload', this.onBeforeLoad, this);
13460         this.store.on('load', this.onLoad, this);
13461         this.store.on('loadexception', this.onLoadException, this);
13462         /*
13463         if(this.resizable){
13464             this.resizer = new Roo.Resizable(this.list,  {
13465                pinned:true, handles:'se'
13466             });
13467             this.resizer.on('resize', function(r, w, h){
13468                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13469                 this.listWidth = w;
13470                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13471                 this.restrictHeight();
13472             }, this);
13473             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13474         }
13475         */
13476         if(!this.editable){
13477             this.editable = true;
13478             this.setEditable(false);
13479         }
13480         
13481         /*
13482         
13483         if (typeof(this.events.add.listeners) != 'undefined') {
13484             
13485             this.addicon = this.wrap.createChild(
13486                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13487        
13488             this.addicon.on('click', function(e) {
13489                 this.fireEvent('add', this);
13490             }, this);
13491         }
13492         if (typeof(this.events.edit.listeners) != 'undefined') {
13493             
13494             this.editicon = this.wrap.createChild(
13495                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13496             if (this.addicon) {
13497                 this.editicon.setStyle('margin-left', '40px');
13498             }
13499             this.editicon.on('click', function(e) {
13500                 
13501                 // we fire even  if inothing is selected..
13502                 this.fireEvent('edit', this, this.lastData );
13503                 
13504             }, this);
13505         }
13506         */
13507         
13508         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13509             "up" : function(e){
13510                 this.inKeyMode = true;
13511                 this.selectPrev();
13512             },
13513
13514             "down" : function(e){
13515                 if(!this.isExpanded()){
13516                     this.onTriggerClick();
13517                 }else{
13518                     this.inKeyMode = true;
13519                     this.selectNext();
13520                 }
13521             },
13522
13523             "enter" : function(e){
13524 //                this.onViewClick();
13525                 //return true;
13526                 this.collapse();
13527                 
13528                 if(this.fireEvent("specialkey", this, e)){
13529                     this.onViewClick(false);
13530                 }
13531                 
13532                 return true;
13533             },
13534
13535             "esc" : function(e){
13536                 this.collapse();
13537             },
13538
13539             "tab" : function(e){
13540                 this.collapse();
13541                 
13542                 if(this.fireEvent("specialkey", this, e)){
13543                     this.onViewClick(false);
13544                 }
13545                 
13546                 return true;
13547             },
13548
13549             scope : this,
13550
13551             doRelay : function(foo, bar, hname){
13552                 if(hname == 'down' || this.scope.isExpanded()){
13553                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13554                 }
13555                 return true;
13556             },
13557
13558             forceKeyDown: true
13559         });
13560         
13561         
13562         this.queryDelay = Math.max(this.queryDelay || 10,
13563                 this.mode == 'local' ? 10 : 250);
13564         
13565         
13566         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13567         
13568         if(this.typeAhead){
13569             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13570         }
13571         if(this.editable !== false){
13572             this.inputEl().on("keyup", this.onKeyUp, this);
13573         }
13574         if(this.forceSelection){
13575             this.inputEl().on('blur', this.doForce, this);
13576         }
13577         
13578         if(this.multiple){
13579             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13580             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13581         }
13582     },
13583     
13584     initTickableEvents: function()
13585     {   
13586         this.createList();
13587         
13588         if(this.hiddenName){
13589             
13590             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13591             
13592             this.hiddenField.dom.value =
13593                 this.hiddenValue !== undefined ? this.hiddenValue :
13594                 this.value !== undefined ? this.value : '';
13595
13596             // prevent input submission
13597             this.el.dom.removeAttribute('name');
13598             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13599              
13600              
13601         }
13602         
13603 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13604         
13605         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13606         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13607         if(this.triggerList){
13608             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13609         }
13610          
13611         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13612         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13613         
13614         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13615         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13616         
13617         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13618         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13619         
13620         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13621         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13622         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13623         
13624         this.okBtn.hide();
13625         this.cancelBtn.hide();
13626         
13627         var _this = this;
13628         
13629         (function(){
13630             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13631             _this.list.setWidth(lw);
13632         }).defer(100);
13633         
13634         this.list.on('mouseover', this.onViewOver, this);
13635         this.list.on('mousemove', this.onViewMove, this);
13636         
13637         this.list.on('scroll', this.onViewScroll, this);
13638         
13639         if(!this.tpl){
13640             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13641                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13642         }
13643
13644         this.view = new Roo.View(this.list, this.tpl, {
13645             singleSelect:true,
13646             tickable:true,
13647             parent:this,
13648             store: this.store,
13649             selectedClass: this.selectedClass
13650         });
13651         
13652         //this.view.wrapEl.setDisplayed(false);
13653         this.view.on('click', this.onViewClick, this);
13654         
13655         
13656         
13657         this.store.on('beforeload', this.onBeforeLoad, this);
13658         this.store.on('load', this.onLoad, this);
13659         this.store.on('loadexception', this.onLoadException, this);
13660         
13661         if(this.editable){
13662             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13663                 "up" : function(e){
13664                     this.inKeyMode = true;
13665                     this.selectPrev();
13666                 },
13667
13668                 "down" : function(e){
13669                     this.inKeyMode = true;
13670                     this.selectNext();
13671                 },
13672
13673                 "enter" : function(e){
13674                     if(this.fireEvent("specialkey", this, e)){
13675                         this.onViewClick(false);
13676                     }
13677                     
13678                     return true;
13679                 },
13680
13681                 "esc" : function(e){
13682                     this.onTickableFooterButtonClick(e, false, false);
13683                 },
13684
13685                 "tab" : function(e){
13686                     this.fireEvent("specialkey", this, e);
13687                     
13688                     this.onTickableFooterButtonClick(e, false, false);
13689                     
13690                     return true;
13691                 },
13692
13693                 scope : this,
13694
13695                 doRelay : function(e, fn, key){
13696                     if(this.scope.isExpanded()){
13697                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13698                     }
13699                     return true;
13700                 },
13701
13702                 forceKeyDown: true
13703             });
13704         }
13705         
13706         this.queryDelay = Math.max(this.queryDelay || 10,
13707                 this.mode == 'local' ? 10 : 250);
13708         
13709         
13710         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13711         
13712         if(this.typeAhead){
13713             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13714         }
13715         
13716         if(this.editable !== false){
13717             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13718         }
13719         
13720         this.indicator = this.indicatorEl();
13721         
13722         if(this.indicator){
13723             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13724             this.indicator.hide();
13725         }
13726         
13727     },
13728
13729     onDestroy : function(){
13730         if(this.view){
13731             this.view.setStore(null);
13732             this.view.el.removeAllListeners();
13733             this.view.el.remove();
13734             this.view.purgeListeners();
13735         }
13736         if(this.list){
13737             this.list.dom.innerHTML  = '';
13738         }
13739         
13740         if(this.store){
13741             this.store.un('beforeload', this.onBeforeLoad, this);
13742             this.store.un('load', this.onLoad, this);
13743             this.store.un('loadexception', this.onLoadException, this);
13744         }
13745         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13746     },
13747
13748     // private
13749     fireKey : function(e){
13750         if(e.isNavKeyPress() && !this.list.isVisible()){
13751             this.fireEvent("specialkey", this, e);
13752         }
13753     },
13754
13755     // private
13756     onResize: function(w, h){
13757 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13758 //        
13759 //        if(typeof w != 'number'){
13760 //            // we do not handle it!?!?
13761 //            return;
13762 //        }
13763 //        var tw = this.trigger.getWidth();
13764 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13765 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13766 //        var x = w - tw;
13767 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13768 //            
13769 //        //this.trigger.setStyle('left', x+'px');
13770 //        
13771 //        if(this.list && this.listWidth === undefined){
13772 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13773 //            this.list.setWidth(lw);
13774 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13775 //        }
13776         
13777     
13778         
13779     },
13780
13781     /**
13782      * Allow or prevent the user from directly editing the field text.  If false is passed,
13783      * the user will only be able to select from the items defined in the dropdown list.  This method
13784      * is the runtime equivalent of setting the 'editable' config option at config time.
13785      * @param {Boolean} value True to allow the user to directly edit the field text
13786      */
13787     setEditable : function(value){
13788         if(value == this.editable){
13789             return;
13790         }
13791         this.editable = value;
13792         if(!value){
13793             this.inputEl().dom.setAttribute('readOnly', true);
13794             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13795             this.inputEl().addClass('x-combo-noedit');
13796         }else{
13797             this.inputEl().dom.setAttribute('readOnly', false);
13798             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13799             this.inputEl().removeClass('x-combo-noedit');
13800         }
13801     },
13802
13803     // private
13804     
13805     onBeforeLoad : function(combo,opts){
13806         if(!this.hasFocus){
13807             return;
13808         }
13809          if (!opts.add) {
13810             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13811          }
13812         this.restrictHeight();
13813         this.selectedIndex = -1;
13814     },
13815
13816     // private
13817     onLoad : function(){
13818         
13819         this.hasQuery = false;
13820         
13821         if(!this.hasFocus){
13822             return;
13823         }
13824         
13825         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13826             this.loading.hide();
13827         }
13828         
13829         if(this.store.getCount() > 0){
13830             
13831             this.expand();
13832             this.restrictHeight();
13833             if(this.lastQuery == this.allQuery){
13834                 if(this.editable && !this.tickable){
13835                     this.inputEl().dom.select();
13836                 }
13837                 
13838                 if(
13839                     !this.selectByValue(this.value, true) &&
13840                     this.autoFocus && 
13841                     (
13842                         !this.store.lastOptions ||
13843                         typeof(this.store.lastOptions.add) == 'undefined' || 
13844                         this.store.lastOptions.add != true
13845                     )
13846                 ){
13847                     this.select(0, true);
13848                 }
13849             }else{
13850                 if(this.autoFocus){
13851                     this.selectNext();
13852                 }
13853                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13854                     this.taTask.delay(this.typeAheadDelay);
13855                 }
13856             }
13857         }else{
13858             this.onEmptyResults();
13859         }
13860         
13861         //this.el.focus();
13862     },
13863     // private
13864     onLoadException : function()
13865     {
13866         this.hasQuery = false;
13867         
13868         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13869             this.loading.hide();
13870         }
13871         
13872         if(this.tickable && this.editable){
13873             return;
13874         }
13875         
13876         this.collapse();
13877         // only causes errors at present
13878         //Roo.log(this.store.reader.jsonData);
13879         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13880             // fixme
13881             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13882         //}
13883         
13884         
13885     },
13886     // private
13887     onTypeAhead : function(){
13888         if(this.store.getCount() > 0){
13889             var r = this.store.getAt(0);
13890             var newValue = r.data[this.displayField];
13891             var len = newValue.length;
13892             var selStart = this.getRawValue().length;
13893             
13894             if(selStart != len){
13895                 this.setRawValue(newValue);
13896                 this.selectText(selStart, newValue.length);
13897             }
13898         }
13899     },
13900
13901     // private
13902     onSelect : function(record, index){
13903         
13904         if(this.fireEvent('beforeselect', this, record, index) !== false){
13905         
13906             this.setFromData(index > -1 ? record.data : false);
13907             
13908             this.collapse();
13909             this.fireEvent('select', this, record, index);
13910         }
13911     },
13912
13913     /**
13914      * Returns the currently selected field value or empty string if no value is set.
13915      * @return {String} value The selected value
13916      */
13917     getValue : function()
13918     {
13919         if(Roo.isIOS && this.useNativeIOS){
13920             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13921         }
13922         
13923         if(this.multiple){
13924             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13925         }
13926         
13927         if(this.valueField){
13928             return typeof this.value != 'undefined' ? this.value : '';
13929         }else{
13930             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13931         }
13932     },
13933     
13934     getRawValue : function()
13935     {
13936         if(Roo.isIOS && this.useNativeIOS){
13937             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13938         }
13939         
13940         var v = this.inputEl().getValue();
13941         
13942         return v;
13943     },
13944
13945     /**
13946      * Clears any text/value currently set in the field
13947      */
13948     clearValue : function(){
13949         
13950         if(this.hiddenField){
13951             this.hiddenField.dom.value = '';
13952         }
13953         this.value = '';
13954         this.setRawValue('');
13955         this.lastSelectionText = '';
13956         this.lastData = false;
13957         
13958         var close = this.closeTriggerEl();
13959         
13960         if(close){
13961             close.hide();
13962         }
13963         
13964         this.validate();
13965         
13966     },
13967
13968     /**
13969      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13970      * will be displayed in the field.  If the value does not match the data value of an existing item,
13971      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13972      * Otherwise the field will be blank (although the value will still be set).
13973      * @param {String} value The value to match
13974      */
13975     setValue : function(v)
13976     {
13977         if(Roo.isIOS && this.useNativeIOS){
13978             this.setIOSValue(v);
13979             return;
13980         }
13981         
13982         if(this.multiple){
13983             this.syncValue();
13984             return;
13985         }
13986         
13987         var text = v;
13988         if(this.valueField){
13989             var r = this.findRecord(this.valueField, v);
13990             if(r){
13991                 text = r.data[this.displayField];
13992             }else if(this.valueNotFoundText !== undefined){
13993                 text = this.valueNotFoundText;
13994             }
13995         }
13996         this.lastSelectionText = text;
13997         if(this.hiddenField){
13998             this.hiddenField.dom.value = v;
13999         }
14000         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14001         this.value = v;
14002         
14003         var close = this.closeTriggerEl();
14004         
14005         if(close){
14006             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14007         }
14008         
14009         this.validate();
14010     },
14011     /**
14012      * @property {Object} the last set data for the element
14013      */
14014     
14015     lastData : false,
14016     /**
14017      * Sets the value of the field based on a object which is related to the record format for the store.
14018      * @param {Object} value the value to set as. or false on reset?
14019      */
14020     setFromData : function(o){
14021         
14022         if(this.multiple){
14023             this.addItem(o);
14024             return;
14025         }
14026             
14027         var dv = ''; // display value
14028         var vv = ''; // value value..
14029         this.lastData = o;
14030         if (this.displayField) {
14031             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14032         } else {
14033             // this is an error condition!!!
14034             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14035         }
14036         
14037         if(this.valueField){
14038             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14039         }
14040         
14041         var close = this.closeTriggerEl();
14042         
14043         if(close){
14044             if(dv.length || vv * 1 > 0){
14045                 close.show() ;
14046                 this.blockFocus=true;
14047             } else {
14048                 close.hide();
14049             }             
14050         }
14051         
14052         if(this.hiddenField){
14053             this.hiddenField.dom.value = vv;
14054             
14055             this.lastSelectionText = dv;
14056             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14057             this.value = vv;
14058             return;
14059         }
14060         // no hidden field.. - we store the value in 'value', but still display
14061         // display field!!!!
14062         this.lastSelectionText = dv;
14063         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14064         this.value = vv;
14065         
14066         
14067         
14068     },
14069     // private
14070     reset : function(){
14071         // overridden so that last data is reset..
14072         
14073         if(this.multiple){
14074             this.clearItem();
14075             return;
14076         }
14077         
14078         this.setValue(this.originalValue);
14079         //this.clearInvalid();
14080         this.lastData = false;
14081         if (this.view) {
14082             this.view.clearSelections();
14083         }
14084         
14085         this.validate();
14086     },
14087     // private
14088     findRecord : function(prop, value){
14089         var record;
14090         if(this.store.getCount() > 0){
14091             this.store.each(function(r){
14092                 if(r.data[prop] == value){
14093                     record = r;
14094                     return false;
14095                 }
14096                 return true;
14097             });
14098         }
14099         return record;
14100     },
14101     
14102     getName: function()
14103     {
14104         // returns hidden if it's set..
14105         if (!this.rendered) {return ''};
14106         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14107         
14108     },
14109     // private
14110     onViewMove : function(e, t){
14111         this.inKeyMode = false;
14112     },
14113
14114     // private
14115     onViewOver : function(e, t){
14116         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14117             return;
14118         }
14119         var item = this.view.findItemFromChild(t);
14120         
14121         if(item){
14122             var index = this.view.indexOf(item);
14123             this.select(index, false);
14124         }
14125     },
14126
14127     // private
14128     onViewClick : function(view, doFocus, el, e)
14129     {
14130         var index = this.view.getSelectedIndexes()[0];
14131         
14132         var r = this.store.getAt(index);
14133         
14134         if(this.tickable){
14135             
14136             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14137                 return;
14138             }
14139             
14140             var rm = false;
14141             var _this = this;
14142             
14143             Roo.each(this.tickItems, function(v,k){
14144                 
14145                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14146                     Roo.log(v);
14147                     _this.tickItems.splice(k, 1);
14148                     
14149                     if(typeof(e) == 'undefined' && view == false){
14150                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14151                     }
14152                     
14153                     rm = true;
14154                     return;
14155                 }
14156             });
14157             
14158             if(rm){
14159                 return;
14160             }
14161             
14162             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14163                 this.tickItems.push(r.data);
14164             }
14165             
14166             if(typeof(e) == 'undefined' && view == false){
14167                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14168             }
14169                     
14170             return;
14171         }
14172         
14173         if(r){
14174             this.onSelect(r, index);
14175         }
14176         if(doFocus !== false && !this.blockFocus){
14177             this.inputEl().focus();
14178         }
14179     },
14180
14181     // private
14182     restrictHeight : function(){
14183         //this.innerList.dom.style.height = '';
14184         //var inner = this.innerList.dom;
14185         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14186         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14187         //this.list.beginUpdate();
14188         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14189         this.list.alignTo(this.inputEl(), this.listAlign);
14190         this.list.alignTo(this.inputEl(), this.listAlign);
14191         //this.list.endUpdate();
14192     },
14193
14194     // private
14195     onEmptyResults : function(){
14196         
14197         if(this.tickable && this.editable){
14198             this.hasFocus = false;
14199             this.restrictHeight();
14200             return;
14201         }
14202         
14203         this.collapse();
14204     },
14205
14206     /**
14207      * Returns true if the dropdown list is expanded, else false.
14208      */
14209     isExpanded : function(){
14210         return this.list.isVisible();
14211     },
14212
14213     /**
14214      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14215      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14216      * @param {String} value The data value of the item to select
14217      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14218      * selected item if it is not currently in view (defaults to true)
14219      * @return {Boolean} True if the value matched an item in the list, else false
14220      */
14221     selectByValue : function(v, scrollIntoView){
14222         if(v !== undefined && v !== null){
14223             var r = this.findRecord(this.valueField || this.displayField, v);
14224             if(r){
14225                 this.select(this.store.indexOf(r), scrollIntoView);
14226                 return true;
14227             }
14228         }
14229         return false;
14230     },
14231
14232     /**
14233      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14234      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14235      * @param {Number} index The zero-based index of the list item to select
14236      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14237      * selected item if it is not currently in view (defaults to true)
14238      */
14239     select : function(index, scrollIntoView){
14240         this.selectedIndex = index;
14241         this.view.select(index);
14242         if(scrollIntoView !== false){
14243             var el = this.view.getNode(index);
14244             /*
14245              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14246              */
14247             if(el){
14248                 this.list.scrollChildIntoView(el, false);
14249             }
14250         }
14251     },
14252
14253     // private
14254     selectNext : function(){
14255         var ct = this.store.getCount();
14256         if(ct > 0){
14257             if(this.selectedIndex == -1){
14258                 this.select(0);
14259             }else if(this.selectedIndex < ct-1){
14260                 this.select(this.selectedIndex+1);
14261             }
14262         }
14263     },
14264
14265     // private
14266     selectPrev : function(){
14267         var ct = this.store.getCount();
14268         if(ct > 0){
14269             if(this.selectedIndex == -1){
14270                 this.select(0);
14271             }else if(this.selectedIndex != 0){
14272                 this.select(this.selectedIndex-1);
14273             }
14274         }
14275     },
14276
14277     // private
14278     onKeyUp : function(e){
14279         if(this.editable !== false && !e.isSpecialKey()){
14280             this.lastKey = e.getKey();
14281             this.dqTask.delay(this.queryDelay);
14282         }
14283     },
14284
14285     // private
14286     validateBlur : function(){
14287         return !this.list || !this.list.isVisible();   
14288     },
14289
14290     // private
14291     initQuery : function(){
14292         
14293         var v = this.getRawValue();
14294         
14295         if(this.tickable && this.editable){
14296             v = this.tickableInputEl().getValue();
14297         }
14298         
14299         this.doQuery(v);
14300     },
14301
14302     // private
14303     doForce : function(){
14304         if(this.inputEl().dom.value.length > 0){
14305             this.inputEl().dom.value =
14306                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14307              
14308         }
14309     },
14310
14311     /**
14312      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14313      * query allowing the query action to be canceled if needed.
14314      * @param {String} query The SQL query to execute
14315      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14316      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14317      * saved in the current store (defaults to false)
14318      */
14319     doQuery : function(q, forceAll){
14320         
14321         if(q === undefined || q === null){
14322             q = '';
14323         }
14324         var qe = {
14325             query: q,
14326             forceAll: forceAll,
14327             combo: this,
14328             cancel:false
14329         };
14330         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14331             return false;
14332         }
14333         q = qe.query;
14334         
14335         forceAll = qe.forceAll;
14336         if(forceAll === true || (q.length >= this.minChars)){
14337             
14338             this.hasQuery = true;
14339             
14340             if(this.lastQuery != q || this.alwaysQuery){
14341                 this.lastQuery = q;
14342                 if(this.mode == 'local'){
14343                     this.selectedIndex = -1;
14344                     if(forceAll){
14345                         this.store.clearFilter();
14346                     }else{
14347                         
14348                         if(this.specialFilter){
14349                             this.fireEvent('specialfilter', this);
14350                             this.onLoad();
14351                             return;
14352                         }
14353                         
14354                         this.store.filter(this.displayField, q);
14355                     }
14356                     
14357                     this.store.fireEvent("datachanged", this.store);
14358                     
14359                     this.onLoad();
14360                     
14361                     
14362                 }else{
14363                     
14364                     this.store.baseParams[this.queryParam] = q;
14365                     
14366                     var options = {params : this.getParams(q)};
14367                     
14368                     if(this.loadNext){
14369                         options.add = true;
14370                         options.params.start = this.page * this.pageSize;
14371                     }
14372                     
14373                     this.store.load(options);
14374                     
14375                     /*
14376                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14377                      *  we should expand the list on onLoad
14378                      *  so command out it
14379                      */
14380 //                    this.expand();
14381                 }
14382             }else{
14383                 this.selectedIndex = -1;
14384                 this.onLoad();   
14385             }
14386         }
14387         
14388         this.loadNext = false;
14389     },
14390     
14391     // private
14392     getParams : function(q){
14393         var p = {};
14394         //p[this.queryParam] = q;
14395         
14396         if(this.pageSize){
14397             p.start = 0;
14398             p.limit = this.pageSize;
14399         }
14400         return p;
14401     },
14402
14403     /**
14404      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14405      */
14406     collapse : function(){
14407         if(!this.isExpanded()){
14408             return;
14409         }
14410         
14411         this.list.hide();
14412         
14413         this.hasFocus = false;
14414         
14415         if(this.tickable){
14416             this.okBtn.hide();
14417             this.cancelBtn.hide();
14418             this.trigger.show();
14419             
14420             if(this.editable){
14421                 this.tickableInputEl().dom.value = '';
14422                 this.tickableInputEl().blur();
14423             }
14424             
14425         }
14426         
14427         Roo.get(document).un('mousedown', this.collapseIf, this);
14428         Roo.get(document).un('mousewheel', this.collapseIf, this);
14429         if (!this.editable) {
14430             Roo.get(document).un('keydown', this.listKeyPress, this);
14431         }
14432         this.fireEvent('collapse', this);
14433         
14434         this.validate();
14435     },
14436
14437     // private
14438     collapseIf : function(e){
14439         var in_combo  = e.within(this.el);
14440         var in_list =  e.within(this.list);
14441         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14442         
14443         if (in_combo || in_list || is_list) {
14444             //e.stopPropagation();
14445             return;
14446         }
14447         
14448         if(this.tickable){
14449             this.onTickableFooterButtonClick(e, false, false);
14450         }
14451
14452         this.collapse();
14453         
14454     },
14455
14456     /**
14457      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14458      */
14459     expand : function(){
14460        
14461         if(this.isExpanded() || !this.hasFocus){
14462             return;
14463         }
14464         
14465         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14466         this.list.setWidth(lw);
14467         
14468         Roo.log('expand');
14469         
14470         this.list.show();
14471         
14472         this.restrictHeight();
14473         
14474         if(this.tickable){
14475             
14476             this.tickItems = Roo.apply([], this.item);
14477             
14478             this.okBtn.show();
14479             this.cancelBtn.show();
14480             this.trigger.hide();
14481             
14482             if(this.editable){
14483                 this.tickableInputEl().focus();
14484             }
14485             
14486         }
14487         
14488         Roo.get(document).on('mousedown', this.collapseIf, this);
14489         Roo.get(document).on('mousewheel', this.collapseIf, this);
14490         if (!this.editable) {
14491             Roo.get(document).on('keydown', this.listKeyPress, this);
14492         }
14493         
14494         this.fireEvent('expand', this);
14495     },
14496
14497     // private
14498     // Implements the default empty TriggerField.onTriggerClick function
14499     onTriggerClick : function(e)
14500     {
14501         Roo.log('trigger click');
14502         
14503         if(this.disabled || !this.triggerList){
14504             return;
14505         }
14506         
14507         this.page = 0;
14508         this.loadNext = false;
14509         
14510         if(this.isExpanded()){
14511             this.collapse();
14512             if (!this.blockFocus) {
14513                 this.inputEl().focus();
14514             }
14515             
14516         }else {
14517             this.hasFocus = true;
14518             if(this.triggerAction == 'all') {
14519                 this.doQuery(this.allQuery, true);
14520             } else {
14521                 this.doQuery(this.getRawValue());
14522             }
14523             if (!this.blockFocus) {
14524                 this.inputEl().focus();
14525             }
14526         }
14527     },
14528     
14529     onTickableTriggerClick : function(e)
14530     {
14531         if(this.disabled){
14532             return;
14533         }
14534         
14535         this.page = 0;
14536         this.loadNext = false;
14537         this.hasFocus = true;
14538         
14539         if(this.triggerAction == 'all') {
14540             this.doQuery(this.allQuery, true);
14541         } else {
14542             this.doQuery(this.getRawValue());
14543         }
14544     },
14545     
14546     onSearchFieldClick : function(e)
14547     {
14548         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14549             this.onTickableFooterButtonClick(e, false, false);
14550             return;
14551         }
14552         
14553         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14554             return;
14555         }
14556         
14557         this.page = 0;
14558         this.loadNext = false;
14559         this.hasFocus = true;
14560         
14561         if(this.triggerAction == 'all') {
14562             this.doQuery(this.allQuery, true);
14563         } else {
14564             this.doQuery(this.getRawValue());
14565         }
14566     },
14567     
14568     listKeyPress : function(e)
14569     {
14570         //Roo.log('listkeypress');
14571         // scroll to first matching element based on key pres..
14572         if (e.isSpecialKey()) {
14573             return false;
14574         }
14575         var k = String.fromCharCode(e.getKey()).toUpperCase();
14576         //Roo.log(k);
14577         var match  = false;
14578         var csel = this.view.getSelectedNodes();
14579         var cselitem = false;
14580         if (csel.length) {
14581             var ix = this.view.indexOf(csel[0]);
14582             cselitem  = this.store.getAt(ix);
14583             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14584                 cselitem = false;
14585             }
14586             
14587         }
14588         
14589         this.store.each(function(v) { 
14590             if (cselitem) {
14591                 // start at existing selection.
14592                 if (cselitem.id == v.id) {
14593                     cselitem = false;
14594                 }
14595                 return true;
14596             }
14597                 
14598             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14599                 match = this.store.indexOf(v);
14600                 return false;
14601             }
14602             return true;
14603         }, this);
14604         
14605         if (match === false) {
14606             return true; // no more action?
14607         }
14608         // scroll to?
14609         this.view.select(match);
14610         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14611         sn.scrollIntoView(sn.dom.parentNode, false);
14612     },
14613     
14614     onViewScroll : function(e, t){
14615         
14616         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){
14617             return;
14618         }
14619         
14620         this.hasQuery = true;
14621         
14622         this.loading = this.list.select('.loading', true).first();
14623         
14624         if(this.loading === null){
14625             this.list.createChild({
14626                 tag: 'div',
14627                 cls: 'loading roo-select2-more-results roo-select2-active',
14628                 html: 'Loading more results...'
14629             });
14630             
14631             this.loading = this.list.select('.loading', true).first();
14632             
14633             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14634             
14635             this.loading.hide();
14636         }
14637         
14638         this.loading.show();
14639         
14640         var _combo = this;
14641         
14642         this.page++;
14643         this.loadNext = true;
14644         
14645         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14646         
14647         return;
14648     },
14649     
14650     addItem : function(o)
14651     {   
14652         var dv = ''; // display value
14653         
14654         if (this.displayField) {
14655             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14656         } else {
14657             // this is an error condition!!!
14658             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14659         }
14660         
14661         if(!dv.length){
14662             return;
14663         }
14664         
14665         var choice = this.choices.createChild({
14666             tag: 'li',
14667             cls: 'roo-select2-search-choice',
14668             cn: [
14669                 {
14670                     tag: 'div',
14671                     html: dv
14672                 },
14673                 {
14674                     tag: 'a',
14675                     href: '#',
14676                     cls: 'roo-select2-search-choice-close fa fa-times',
14677                     tabindex: '-1'
14678                 }
14679             ]
14680             
14681         }, this.searchField);
14682         
14683         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14684         
14685         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14686         
14687         this.item.push(o);
14688         
14689         this.lastData = o;
14690         
14691         this.syncValue();
14692         
14693         this.inputEl().dom.value = '';
14694         
14695         this.validate();
14696     },
14697     
14698     onRemoveItem : function(e, _self, o)
14699     {
14700         e.preventDefault();
14701         
14702         this.lastItem = Roo.apply([], this.item);
14703         
14704         var index = this.item.indexOf(o.data) * 1;
14705         
14706         if( index < 0){
14707             Roo.log('not this item?!');
14708             return;
14709         }
14710         
14711         this.item.splice(index, 1);
14712         o.item.remove();
14713         
14714         this.syncValue();
14715         
14716         this.fireEvent('remove', this, e);
14717         
14718         this.validate();
14719         
14720     },
14721     
14722     syncValue : function()
14723     {
14724         if(!this.item.length){
14725             this.clearValue();
14726             return;
14727         }
14728             
14729         var value = [];
14730         var _this = this;
14731         Roo.each(this.item, function(i){
14732             if(_this.valueField){
14733                 value.push(i[_this.valueField]);
14734                 return;
14735             }
14736
14737             value.push(i);
14738         });
14739
14740         this.value = value.join(',');
14741
14742         if(this.hiddenField){
14743             this.hiddenField.dom.value = this.value;
14744         }
14745         
14746         this.store.fireEvent("datachanged", this.store);
14747         
14748         this.validate();
14749     },
14750     
14751     clearItem : function()
14752     {
14753         if(!this.multiple){
14754             return;
14755         }
14756         
14757         this.item = [];
14758         
14759         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14760            c.remove();
14761         });
14762         
14763         this.syncValue();
14764         
14765         this.validate();
14766         
14767         if(this.tickable && !Roo.isTouch){
14768             this.view.refresh();
14769         }
14770     },
14771     
14772     inputEl: function ()
14773     {
14774         if(Roo.isIOS && this.useNativeIOS){
14775             return this.el.select('select.roo-ios-select', true).first();
14776         }
14777         
14778         if(Roo.isTouch && this.mobileTouchView){
14779             return this.el.select('input.form-control',true).first();
14780         }
14781         
14782         if(this.tickable){
14783             return this.searchField;
14784         }
14785         
14786         return this.el.select('input.form-control',true).first();
14787     },
14788     
14789     onTickableFooterButtonClick : function(e, btn, el)
14790     {
14791         e.preventDefault();
14792         
14793         this.lastItem = Roo.apply([], this.item);
14794         
14795         if(btn && btn.name == 'cancel'){
14796             this.tickItems = Roo.apply([], this.item);
14797             this.collapse();
14798             return;
14799         }
14800         
14801         this.clearItem();
14802         
14803         var _this = this;
14804         
14805         Roo.each(this.tickItems, function(o){
14806             _this.addItem(o);
14807         });
14808         
14809         this.collapse();
14810         
14811     },
14812     
14813     validate : function()
14814     {
14815         if(this.getVisibilityEl().hasClass('hidden')){
14816             return true;
14817         }
14818         
14819         var v = this.getRawValue();
14820         
14821         if(this.multiple){
14822             v = this.getValue();
14823         }
14824         
14825         if(this.disabled || this.allowBlank || v.length){
14826             this.markValid();
14827             return true;
14828         }
14829         
14830         this.markInvalid();
14831         return false;
14832     },
14833     
14834     tickableInputEl : function()
14835     {
14836         if(!this.tickable || !this.editable){
14837             return this.inputEl();
14838         }
14839         
14840         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14841     },
14842     
14843     
14844     getAutoCreateTouchView : function()
14845     {
14846         var id = Roo.id();
14847         
14848         var cfg = {
14849             cls: 'form-group' //input-group
14850         };
14851         
14852         var input =  {
14853             tag: 'input',
14854             id : id,
14855             type : this.inputType,
14856             cls : 'form-control x-combo-noedit',
14857             autocomplete: 'new-password',
14858             placeholder : this.placeholder || '',
14859             readonly : true
14860         };
14861         
14862         if (this.name) {
14863             input.name = this.name;
14864         }
14865         
14866         if (this.size) {
14867             input.cls += ' input-' + this.size;
14868         }
14869         
14870         if (this.disabled) {
14871             input.disabled = true;
14872         }
14873         
14874         var inputblock = {
14875             cls : '',
14876             cn : [
14877                 input
14878             ]
14879         };
14880         
14881         if(this.before){
14882             inputblock.cls += ' input-group';
14883             
14884             inputblock.cn.unshift({
14885                 tag :'span',
14886                 cls : 'input-group-addon',
14887                 html : this.before
14888             });
14889         }
14890         
14891         if(this.removable && !this.multiple){
14892             inputblock.cls += ' roo-removable';
14893             
14894             inputblock.cn.push({
14895                 tag: 'button',
14896                 html : 'x',
14897                 cls : 'roo-combo-removable-btn close'
14898             });
14899         }
14900
14901         if(this.hasFeedback && !this.allowBlank){
14902             
14903             inputblock.cls += ' has-feedback';
14904             
14905             inputblock.cn.push({
14906                 tag: 'span',
14907                 cls: 'glyphicon form-control-feedback'
14908             });
14909             
14910         }
14911         
14912         if (this.after) {
14913             
14914             inputblock.cls += (this.before) ? '' : ' input-group';
14915             
14916             inputblock.cn.push({
14917                 tag :'span',
14918                 cls : 'input-group-addon',
14919                 html : this.after
14920             });
14921         }
14922
14923         var box = {
14924             tag: 'div',
14925             cn: [
14926                 {
14927                     tag: 'input',
14928                     type : 'hidden',
14929                     cls: 'form-hidden-field'
14930                 },
14931                 inputblock
14932             ]
14933             
14934         };
14935         
14936         if(this.multiple){
14937             box = {
14938                 tag: 'div',
14939                 cn: [
14940                     {
14941                         tag: 'input',
14942                         type : 'hidden',
14943                         cls: 'form-hidden-field'
14944                     },
14945                     {
14946                         tag: 'ul',
14947                         cls: 'roo-select2-choices',
14948                         cn:[
14949                             {
14950                                 tag: 'li',
14951                                 cls: 'roo-select2-search-field',
14952                                 cn: [
14953
14954                                     inputblock
14955                                 ]
14956                             }
14957                         ]
14958                     }
14959                 ]
14960             }
14961         };
14962         
14963         var combobox = {
14964             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14965             cn: [
14966                 box
14967             ]
14968         };
14969         
14970         if(!this.multiple && this.showToggleBtn){
14971             
14972             var caret = {
14973                         tag: 'span',
14974                         cls: 'caret'
14975             };
14976             
14977             if (this.caret != false) {
14978                 caret = {
14979                      tag: 'i',
14980                      cls: 'fa fa-' + this.caret
14981                 };
14982                 
14983             }
14984             
14985             combobox.cn.push({
14986                 tag :'span',
14987                 cls : 'input-group-addon btn dropdown-toggle',
14988                 cn : [
14989                     caret,
14990                     {
14991                         tag: 'span',
14992                         cls: 'combobox-clear',
14993                         cn  : [
14994                             {
14995                                 tag : 'i',
14996                                 cls: 'icon-remove'
14997                             }
14998                         ]
14999                     }
15000                 ]
15001
15002             })
15003         }
15004         
15005         if(this.multiple){
15006             combobox.cls += ' roo-select2-container-multi';
15007         }
15008         
15009         var align = this.labelAlign || this.parentLabelAlign();
15010         
15011         if (align ==='left' && this.fieldLabel.length) {
15012
15013             cfg.cn = [
15014                 {
15015                    tag : 'i',
15016                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15017                    tooltip : 'This field is required'
15018                 },
15019                 {
15020                     tag: 'label',
15021                     cls : 'control-label',
15022                     html : this.fieldLabel
15023
15024                 },
15025                 {
15026                     cls : '', 
15027                     cn: [
15028                         combobox
15029                     ]
15030                 }
15031             ];
15032             
15033             var labelCfg = cfg.cn[1];
15034             var contentCfg = cfg.cn[2];
15035             
15036
15037             if(this.indicatorpos == 'right'){
15038                 cfg.cn = [
15039                     {
15040                         tag: 'label',
15041                         'for' :  id,
15042                         cls : 'control-label',
15043                         cn : [
15044                             {
15045                                 tag : 'span',
15046                                 html : this.fieldLabel
15047                             },
15048                             {
15049                                 tag : 'i',
15050                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15051                                 tooltip : 'This field is required'
15052                             }
15053                         ]
15054                     },
15055                     {
15056                         cls : "",
15057                         cn: [
15058                             combobox
15059                         ]
15060                     }
15061
15062                 ];
15063                 
15064                 labelCfg = cfg.cn[0];
15065                 contentCfg = cfg.cn[1];
15066             }
15067             
15068            
15069             
15070             if(this.labelWidth > 12){
15071                 labelCfg.style = "width: " + this.labelWidth + 'px';
15072             }
15073             
15074             if(this.labelWidth < 13 && this.labelmd == 0){
15075                 this.labelmd = this.labelWidth;
15076             }
15077             
15078             if(this.labellg > 0){
15079                 labelCfg.cls += ' col-lg-' + this.labellg;
15080                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15081             }
15082             
15083             if(this.labelmd > 0){
15084                 labelCfg.cls += ' col-md-' + this.labelmd;
15085                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15086             }
15087             
15088             if(this.labelsm > 0){
15089                 labelCfg.cls += ' col-sm-' + this.labelsm;
15090                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15091             }
15092             
15093             if(this.labelxs > 0){
15094                 labelCfg.cls += ' col-xs-' + this.labelxs;
15095                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15096             }
15097                 
15098                 
15099         } else if ( this.fieldLabel.length) {
15100             cfg.cn = [
15101                 {
15102                    tag : 'i',
15103                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15104                    tooltip : 'This field is required'
15105                 },
15106                 {
15107                     tag: 'label',
15108                     cls : 'control-label',
15109                     html : this.fieldLabel
15110
15111                 },
15112                 {
15113                     cls : '', 
15114                     cn: [
15115                         combobox
15116                     ]
15117                 }
15118             ];
15119             
15120             if(this.indicatorpos == 'right'){
15121                 cfg.cn = [
15122                     {
15123                         tag: 'label',
15124                         cls : 'control-label',
15125                         html : this.fieldLabel,
15126                         cn : [
15127                             {
15128                                tag : 'i',
15129                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15130                                tooltip : 'This field is required'
15131                             }
15132                         ]
15133                     },
15134                     {
15135                         cls : '', 
15136                         cn: [
15137                             combobox
15138                         ]
15139                     }
15140                 ];
15141             }
15142         } else {
15143             cfg.cn = combobox;    
15144         }
15145         
15146         
15147         var settings = this;
15148         
15149         ['xs','sm','md','lg'].map(function(size){
15150             if (settings[size]) {
15151                 cfg.cls += ' col-' + size + '-' + settings[size];
15152             }
15153         });
15154         
15155         return cfg;
15156     },
15157     
15158     initTouchView : function()
15159     {
15160         this.renderTouchView();
15161         
15162         this.touchViewEl.on('scroll', function(){
15163             this.el.dom.scrollTop = 0;
15164         }, this);
15165         
15166         this.originalValue = this.getValue();
15167         
15168         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15169         
15170         this.inputEl().on("click", this.showTouchView, this);
15171         if (this.triggerEl) {
15172             this.triggerEl.on("click", this.showTouchView, this);
15173         }
15174         
15175         
15176         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15177         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15178         
15179         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15180         
15181         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15182         this.store.on('load', this.onTouchViewLoad, this);
15183         this.store.on('loadexception', this.onTouchViewLoadException, this);
15184         
15185         if(this.hiddenName){
15186             
15187             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15188             
15189             this.hiddenField.dom.value =
15190                 this.hiddenValue !== undefined ? this.hiddenValue :
15191                 this.value !== undefined ? this.value : '';
15192         
15193             this.el.dom.removeAttribute('name');
15194             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15195         }
15196         
15197         if(this.multiple){
15198             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15199             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15200         }
15201         
15202         if(this.removable && !this.multiple){
15203             var close = this.closeTriggerEl();
15204             if(close){
15205                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15206                 close.on('click', this.removeBtnClick, this, close);
15207             }
15208         }
15209         /*
15210          * fix the bug in Safari iOS8
15211          */
15212         this.inputEl().on("focus", function(e){
15213             document.activeElement.blur();
15214         }, this);
15215         
15216         return;
15217         
15218         
15219     },
15220     
15221     renderTouchView : function()
15222     {
15223         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15224         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15225         
15226         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15227         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15228         
15229         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15230         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15231         this.touchViewBodyEl.setStyle('overflow', 'auto');
15232         
15233         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15234         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15235         
15236         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15237         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15238         
15239     },
15240     
15241     showTouchView : function()
15242     {
15243         if(this.disabled){
15244             return;
15245         }
15246         
15247         this.touchViewHeaderEl.hide();
15248
15249         if(this.modalTitle.length){
15250             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15251             this.touchViewHeaderEl.show();
15252         }
15253
15254         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15255         this.touchViewEl.show();
15256
15257         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15258         
15259         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15260         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15261
15262         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15263
15264         if(this.modalTitle.length){
15265             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15266         }
15267         
15268         this.touchViewBodyEl.setHeight(bodyHeight);
15269
15270         if(this.animate){
15271             var _this = this;
15272             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15273         }else{
15274             this.touchViewEl.addClass('in');
15275         }
15276
15277         this.doTouchViewQuery();
15278         
15279     },
15280     
15281     hideTouchView : function()
15282     {
15283         this.touchViewEl.removeClass('in');
15284
15285         if(this.animate){
15286             var _this = this;
15287             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15288         }else{
15289             this.touchViewEl.setStyle('display', 'none');
15290         }
15291         
15292     },
15293     
15294     setTouchViewValue : function()
15295     {
15296         if(this.multiple){
15297             this.clearItem();
15298         
15299             var _this = this;
15300
15301             Roo.each(this.tickItems, function(o){
15302                 this.addItem(o);
15303             }, this);
15304         }
15305         
15306         this.hideTouchView();
15307     },
15308     
15309     doTouchViewQuery : function()
15310     {
15311         var qe = {
15312             query: '',
15313             forceAll: true,
15314             combo: this,
15315             cancel:false
15316         };
15317         
15318         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15319             return false;
15320         }
15321         
15322         if(!this.alwaysQuery || this.mode == 'local'){
15323             this.onTouchViewLoad();
15324             return;
15325         }
15326         
15327         this.store.load();
15328     },
15329     
15330     onTouchViewBeforeLoad : function(combo,opts)
15331     {
15332         return;
15333     },
15334
15335     // private
15336     onTouchViewLoad : function()
15337     {
15338         if(this.store.getCount() < 1){
15339             this.onTouchViewEmptyResults();
15340             return;
15341         }
15342         
15343         this.clearTouchView();
15344         
15345         var rawValue = this.getRawValue();
15346         
15347         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15348         
15349         this.tickItems = [];
15350         
15351         this.store.data.each(function(d, rowIndex){
15352             var row = this.touchViewListGroup.createChild(template);
15353             
15354             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15355                 row.addClass(d.data.cls);
15356             }
15357             
15358             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15359                 var cfg = {
15360                     data : d.data,
15361                     html : d.data[this.displayField]
15362                 };
15363                 
15364                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15365                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15366                 }
15367             }
15368             row.removeClass('selected');
15369             if(!this.multiple && this.valueField &&
15370                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15371             {
15372                 // radio buttons..
15373                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15374                 row.addClass('selected');
15375             }
15376             
15377             if(this.multiple && this.valueField &&
15378                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15379             {
15380                 
15381                 // checkboxes...
15382                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15383                 this.tickItems.push(d.data);
15384             }
15385             
15386             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15387             
15388         }, this);
15389         
15390         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15391         
15392         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15393
15394         if(this.modalTitle.length){
15395             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15396         }
15397
15398         var listHeight = this.touchViewListGroup.getHeight();
15399         
15400         var _this = this;
15401         
15402         if(firstChecked && listHeight > bodyHeight){
15403             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15404         }
15405         
15406     },
15407     
15408     onTouchViewLoadException : function()
15409     {
15410         this.hideTouchView();
15411     },
15412     
15413     onTouchViewEmptyResults : function()
15414     {
15415         this.clearTouchView();
15416         
15417         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15418         
15419         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15420         
15421     },
15422     
15423     clearTouchView : function()
15424     {
15425         this.touchViewListGroup.dom.innerHTML = '';
15426     },
15427     
15428     onTouchViewClick : function(e, el, o)
15429     {
15430         e.preventDefault();
15431         
15432         var row = o.row;
15433         var rowIndex = o.rowIndex;
15434         
15435         var r = this.store.getAt(rowIndex);
15436         
15437         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15438             
15439             if(!this.multiple){
15440                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15441                     c.dom.removeAttribute('checked');
15442                 }, this);
15443
15444                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15445
15446                 this.setFromData(r.data);
15447
15448                 var close = this.closeTriggerEl();
15449
15450                 if(close){
15451                     close.show();
15452                 }
15453
15454                 this.hideTouchView();
15455
15456                 this.fireEvent('select', this, r, rowIndex);
15457
15458                 return;
15459             }
15460
15461             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15462                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15463                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15464                 return;
15465             }
15466
15467             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15468             this.addItem(r.data);
15469             this.tickItems.push(r.data);
15470         }
15471     },
15472     
15473     getAutoCreateNativeIOS : function()
15474     {
15475         var cfg = {
15476             cls: 'form-group' //input-group,
15477         };
15478         
15479         var combobox =  {
15480             tag: 'select',
15481             cls : 'roo-ios-select'
15482         };
15483         
15484         if (this.name) {
15485             combobox.name = this.name;
15486         }
15487         
15488         if (this.disabled) {
15489             combobox.disabled = true;
15490         }
15491         
15492         var settings = this;
15493         
15494         ['xs','sm','md','lg'].map(function(size){
15495             if (settings[size]) {
15496                 cfg.cls += ' col-' + size + '-' + settings[size];
15497             }
15498         });
15499         
15500         cfg.cn = combobox;
15501         
15502         return cfg;
15503         
15504     },
15505     
15506     initIOSView : function()
15507     {
15508         this.store.on('load', this.onIOSViewLoad, this);
15509         
15510         return;
15511     },
15512     
15513     onIOSViewLoad : function()
15514     {
15515         if(this.store.getCount() < 1){
15516             return;
15517         }
15518         
15519         this.clearIOSView();
15520         
15521         if(this.allowBlank) {
15522             
15523             var default_text = '-- SELECT --';
15524             
15525             if(this.placeholder.length){
15526                 default_text = this.placeholder;
15527             }
15528             
15529             if(this.emptyTitle.length){
15530                 default_text += ' - ' + this.emptyTitle + ' -';
15531             }
15532             
15533             var opt = this.inputEl().createChild({
15534                 tag: 'option',
15535                 value : 0,
15536                 html : default_text
15537             });
15538             
15539             var o = {};
15540             o[this.valueField] = 0;
15541             o[this.displayField] = default_text;
15542             
15543             this.ios_options.push({
15544                 data : o,
15545                 el : opt
15546             });
15547             
15548         }
15549         
15550         this.store.data.each(function(d, rowIndex){
15551             
15552             var html = '';
15553             
15554             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15555                 html = d.data[this.displayField];
15556             }
15557             
15558             var value = '';
15559             
15560             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15561                 value = d.data[this.valueField];
15562             }
15563             
15564             var option = {
15565                 tag: 'option',
15566                 value : value,
15567                 html : html
15568             };
15569             
15570             if(this.value == d.data[this.valueField]){
15571                 option['selected'] = true;
15572             }
15573             
15574             var opt = this.inputEl().createChild(option);
15575             
15576             this.ios_options.push({
15577                 data : d.data,
15578                 el : opt
15579             });
15580             
15581         }, this);
15582         
15583         this.inputEl().on('change', function(){
15584            this.fireEvent('select', this);
15585         }, this);
15586         
15587     },
15588     
15589     clearIOSView: function()
15590     {
15591         this.inputEl().dom.innerHTML = '';
15592         
15593         this.ios_options = [];
15594     },
15595     
15596     setIOSValue: function(v)
15597     {
15598         this.value = v;
15599         
15600         if(!this.ios_options){
15601             return;
15602         }
15603         
15604         Roo.each(this.ios_options, function(opts){
15605            
15606            opts.el.dom.removeAttribute('selected');
15607            
15608            if(opts.data[this.valueField] != v){
15609                return;
15610            }
15611            
15612            opts.el.dom.setAttribute('selected', true);
15613            
15614         }, this);
15615     }
15616
15617     /** 
15618     * @cfg {Boolean} grow 
15619     * @hide 
15620     */
15621     /** 
15622     * @cfg {Number} growMin 
15623     * @hide 
15624     */
15625     /** 
15626     * @cfg {Number} growMax 
15627     * @hide 
15628     */
15629     /**
15630      * @hide
15631      * @method autoSize
15632      */
15633 });
15634
15635 Roo.apply(Roo.bootstrap.ComboBox,  {
15636     
15637     header : {
15638         tag: 'div',
15639         cls: 'modal-header',
15640         cn: [
15641             {
15642                 tag: 'h4',
15643                 cls: 'modal-title'
15644             }
15645         ]
15646     },
15647     
15648     body : {
15649         tag: 'div',
15650         cls: 'modal-body',
15651         cn: [
15652             {
15653                 tag: 'ul',
15654                 cls: 'list-group'
15655             }
15656         ]
15657     },
15658     
15659     listItemRadio : {
15660         tag: 'li',
15661         cls: 'list-group-item',
15662         cn: [
15663             {
15664                 tag: 'span',
15665                 cls: 'roo-combobox-list-group-item-value'
15666             },
15667             {
15668                 tag: 'div',
15669                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15670                 cn: [
15671                     {
15672                         tag: 'input',
15673                         type: 'radio'
15674                     },
15675                     {
15676                         tag: 'label'
15677                     }
15678                 ]
15679             }
15680         ]
15681     },
15682     
15683     listItemCheckbox : {
15684         tag: 'li',
15685         cls: 'list-group-item',
15686         cn: [
15687             {
15688                 tag: 'span',
15689                 cls: 'roo-combobox-list-group-item-value'
15690             },
15691             {
15692                 tag: 'div',
15693                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15694                 cn: [
15695                     {
15696                         tag: 'input',
15697                         type: 'checkbox'
15698                     },
15699                     {
15700                         tag: 'label'
15701                     }
15702                 ]
15703             }
15704         ]
15705     },
15706     
15707     emptyResult : {
15708         tag: 'div',
15709         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15710     },
15711     
15712     footer : {
15713         tag: 'div',
15714         cls: 'modal-footer',
15715         cn: [
15716             {
15717                 tag: 'div',
15718                 cls: 'row',
15719                 cn: [
15720                     {
15721                         tag: 'div',
15722                         cls: 'col-xs-6 text-left',
15723                         cn: {
15724                             tag: 'button',
15725                             cls: 'btn btn-danger roo-touch-view-cancel',
15726                             html: 'Cancel'
15727                         }
15728                     },
15729                     {
15730                         tag: 'div',
15731                         cls: 'col-xs-6 text-right',
15732                         cn: {
15733                             tag: 'button',
15734                             cls: 'btn btn-success roo-touch-view-ok',
15735                             html: 'OK'
15736                         }
15737                     }
15738                 ]
15739             }
15740         ]
15741         
15742     }
15743 });
15744
15745 Roo.apply(Roo.bootstrap.ComboBox,  {
15746     
15747     touchViewTemplate : {
15748         tag: 'div',
15749         cls: 'modal fade roo-combobox-touch-view',
15750         cn: [
15751             {
15752                 tag: 'div',
15753                 cls: 'modal-dialog',
15754                 style : 'position:fixed', // we have to fix position....
15755                 cn: [
15756                     {
15757                         tag: 'div',
15758                         cls: 'modal-content',
15759                         cn: [
15760                             Roo.bootstrap.ComboBox.header,
15761                             Roo.bootstrap.ComboBox.body,
15762                             Roo.bootstrap.ComboBox.footer
15763                         ]
15764                     }
15765                 ]
15766             }
15767         ]
15768     }
15769 });/*
15770  * Based on:
15771  * Ext JS Library 1.1.1
15772  * Copyright(c) 2006-2007, Ext JS, LLC.
15773  *
15774  * Originally Released Under LGPL - original licence link has changed is not relivant.
15775  *
15776  * Fork - LGPL
15777  * <script type="text/javascript">
15778  */
15779
15780 /**
15781  * @class Roo.View
15782  * @extends Roo.util.Observable
15783  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15784  * This class also supports single and multi selection modes. <br>
15785  * Create a data model bound view:
15786  <pre><code>
15787  var store = new Roo.data.Store(...);
15788
15789  var view = new Roo.View({
15790     el : "my-element",
15791     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15792  
15793     singleSelect: true,
15794     selectedClass: "ydataview-selected",
15795     store: store
15796  });
15797
15798  // listen for node click?
15799  view.on("click", function(vw, index, node, e){
15800  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15801  });
15802
15803  // load XML data
15804  dataModel.load("foobar.xml");
15805  </code></pre>
15806  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15807  * <br><br>
15808  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15809  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15810  * 
15811  * Note: old style constructor is still suported (container, template, config)
15812  * 
15813  * @constructor
15814  * Create a new View
15815  * @param {Object} config The config object
15816  * 
15817  */
15818 Roo.View = function(config, depreciated_tpl, depreciated_config){
15819     
15820     this.parent = false;
15821     
15822     if (typeof(depreciated_tpl) == 'undefined') {
15823         // new way.. - universal constructor.
15824         Roo.apply(this, config);
15825         this.el  = Roo.get(this.el);
15826     } else {
15827         // old format..
15828         this.el  = Roo.get(config);
15829         this.tpl = depreciated_tpl;
15830         Roo.apply(this, depreciated_config);
15831     }
15832     this.wrapEl  = this.el.wrap().wrap();
15833     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15834     
15835     
15836     if(typeof(this.tpl) == "string"){
15837         this.tpl = new Roo.Template(this.tpl);
15838     } else {
15839         // support xtype ctors..
15840         this.tpl = new Roo.factory(this.tpl, Roo);
15841     }
15842     
15843     
15844     this.tpl.compile();
15845     
15846     /** @private */
15847     this.addEvents({
15848         /**
15849          * @event beforeclick
15850          * Fires before a click is processed. Returns false to cancel the default action.
15851          * @param {Roo.View} this
15852          * @param {Number} index The index of the target node
15853          * @param {HTMLElement} node The target node
15854          * @param {Roo.EventObject} e The raw event object
15855          */
15856             "beforeclick" : true,
15857         /**
15858          * @event click
15859          * Fires when a template node is clicked.
15860          * @param {Roo.View} this
15861          * @param {Number} index The index of the target node
15862          * @param {HTMLElement} node The target node
15863          * @param {Roo.EventObject} e The raw event object
15864          */
15865             "click" : true,
15866         /**
15867          * @event dblclick
15868          * Fires when a template node is double clicked.
15869          * @param {Roo.View} this
15870          * @param {Number} index The index of the target node
15871          * @param {HTMLElement} node The target node
15872          * @param {Roo.EventObject} e The raw event object
15873          */
15874             "dblclick" : true,
15875         /**
15876          * @event contextmenu
15877          * Fires when a template node is right clicked.
15878          * @param {Roo.View} this
15879          * @param {Number} index The index of the target node
15880          * @param {HTMLElement} node The target node
15881          * @param {Roo.EventObject} e The raw event object
15882          */
15883             "contextmenu" : true,
15884         /**
15885          * @event selectionchange
15886          * Fires when the selected nodes change.
15887          * @param {Roo.View} this
15888          * @param {Array} selections Array of the selected nodes
15889          */
15890             "selectionchange" : true,
15891     
15892         /**
15893          * @event beforeselect
15894          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15895          * @param {Roo.View} this
15896          * @param {HTMLElement} node The node to be selected
15897          * @param {Array} selections Array of currently selected nodes
15898          */
15899             "beforeselect" : true,
15900         /**
15901          * @event preparedata
15902          * Fires on every row to render, to allow you to change the data.
15903          * @param {Roo.View} this
15904          * @param {Object} data to be rendered (change this)
15905          */
15906           "preparedata" : true
15907           
15908           
15909         });
15910
15911
15912
15913     this.el.on({
15914         "click": this.onClick,
15915         "dblclick": this.onDblClick,
15916         "contextmenu": this.onContextMenu,
15917         scope:this
15918     });
15919
15920     this.selections = [];
15921     this.nodes = [];
15922     this.cmp = new Roo.CompositeElementLite([]);
15923     if(this.store){
15924         this.store = Roo.factory(this.store, Roo.data);
15925         this.setStore(this.store, true);
15926     }
15927     
15928     if ( this.footer && this.footer.xtype) {
15929            
15930          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15931         
15932         this.footer.dataSource = this.store;
15933         this.footer.container = fctr;
15934         this.footer = Roo.factory(this.footer, Roo);
15935         fctr.insertFirst(this.el);
15936         
15937         // this is a bit insane - as the paging toolbar seems to detach the el..
15938 //        dom.parentNode.parentNode.parentNode
15939          // they get detached?
15940     }
15941     
15942     
15943     Roo.View.superclass.constructor.call(this);
15944     
15945     
15946 };
15947
15948 Roo.extend(Roo.View, Roo.util.Observable, {
15949     
15950      /**
15951      * @cfg {Roo.data.Store} store Data store to load data from.
15952      */
15953     store : false,
15954     
15955     /**
15956      * @cfg {String|Roo.Element} el The container element.
15957      */
15958     el : '',
15959     
15960     /**
15961      * @cfg {String|Roo.Template} tpl The template used by this View 
15962      */
15963     tpl : false,
15964     /**
15965      * @cfg {String} dataName the named area of the template to use as the data area
15966      *                          Works with domtemplates roo-name="name"
15967      */
15968     dataName: false,
15969     /**
15970      * @cfg {String} selectedClass The css class to add to selected nodes
15971      */
15972     selectedClass : "x-view-selected",
15973      /**
15974      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15975      */
15976     emptyText : "",
15977     
15978     /**
15979      * @cfg {String} text to display on mask (default Loading)
15980      */
15981     mask : false,
15982     /**
15983      * @cfg {Boolean} multiSelect Allow multiple selection
15984      */
15985     multiSelect : false,
15986     /**
15987      * @cfg {Boolean} singleSelect Allow single selection
15988      */
15989     singleSelect:  false,
15990     
15991     /**
15992      * @cfg {Boolean} toggleSelect - selecting 
15993      */
15994     toggleSelect : false,
15995     
15996     /**
15997      * @cfg {Boolean} tickable - selecting 
15998      */
15999     tickable : false,
16000     
16001     /**
16002      * Returns the element this view is bound to.
16003      * @return {Roo.Element}
16004      */
16005     getEl : function(){
16006         return this.wrapEl;
16007     },
16008     
16009     
16010
16011     /**
16012      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16013      */
16014     refresh : function(){
16015         //Roo.log('refresh');
16016         var t = this.tpl;
16017         
16018         // if we are using something like 'domtemplate', then
16019         // the what gets used is:
16020         // t.applySubtemplate(NAME, data, wrapping data..)
16021         // the outer template then get' applied with
16022         //     the store 'extra data'
16023         // and the body get's added to the
16024         //      roo-name="data" node?
16025         //      <span class='roo-tpl-{name}'></span> ?????
16026         
16027         
16028         
16029         this.clearSelections();
16030         this.el.update("");
16031         var html = [];
16032         var records = this.store.getRange();
16033         if(records.length < 1) {
16034             
16035             // is this valid??  = should it render a template??
16036             
16037             this.el.update(this.emptyText);
16038             return;
16039         }
16040         var el = this.el;
16041         if (this.dataName) {
16042             this.el.update(t.apply(this.store.meta)); //????
16043             el = this.el.child('.roo-tpl-' + this.dataName);
16044         }
16045         
16046         for(var i = 0, len = records.length; i < len; i++){
16047             var data = this.prepareData(records[i].data, i, records[i]);
16048             this.fireEvent("preparedata", this, data, i, records[i]);
16049             
16050             var d = Roo.apply({}, data);
16051             
16052             if(this.tickable){
16053                 Roo.apply(d, {'roo-id' : Roo.id()});
16054                 
16055                 var _this = this;
16056             
16057                 Roo.each(this.parent.item, function(item){
16058                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16059                         return;
16060                     }
16061                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16062                 });
16063             }
16064             
16065             html[html.length] = Roo.util.Format.trim(
16066                 this.dataName ?
16067                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16068                     t.apply(d)
16069             );
16070         }
16071         
16072         
16073         
16074         el.update(html.join(""));
16075         this.nodes = el.dom.childNodes;
16076         this.updateIndexes(0);
16077     },
16078     
16079
16080     /**
16081      * Function to override to reformat the data that is sent to
16082      * the template for each node.
16083      * DEPRICATED - use the preparedata event handler.
16084      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16085      * a JSON object for an UpdateManager bound view).
16086      */
16087     prepareData : function(data, index, record)
16088     {
16089         this.fireEvent("preparedata", this, data, index, record);
16090         return data;
16091     },
16092
16093     onUpdate : function(ds, record){
16094         // Roo.log('on update');   
16095         this.clearSelections();
16096         var index = this.store.indexOf(record);
16097         var n = this.nodes[index];
16098         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16099         n.parentNode.removeChild(n);
16100         this.updateIndexes(index, index);
16101     },
16102
16103     
16104     
16105 // --------- FIXME     
16106     onAdd : function(ds, records, index)
16107     {
16108         //Roo.log(['on Add', ds, records, index] );        
16109         this.clearSelections();
16110         if(this.nodes.length == 0){
16111             this.refresh();
16112             return;
16113         }
16114         var n = this.nodes[index];
16115         for(var i = 0, len = records.length; i < len; i++){
16116             var d = this.prepareData(records[i].data, i, records[i]);
16117             if(n){
16118                 this.tpl.insertBefore(n, d);
16119             }else{
16120                 
16121                 this.tpl.append(this.el, d);
16122             }
16123         }
16124         this.updateIndexes(index);
16125     },
16126
16127     onRemove : function(ds, record, index){
16128        // Roo.log('onRemove');
16129         this.clearSelections();
16130         var el = this.dataName  ?
16131             this.el.child('.roo-tpl-' + this.dataName) :
16132             this.el; 
16133         
16134         el.dom.removeChild(this.nodes[index]);
16135         this.updateIndexes(index);
16136     },
16137
16138     /**
16139      * Refresh an individual node.
16140      * @param {Number} index
16141      */
16142     refreshNode : function(index){
16143         this.onUpdate(this.store, this.store.getAt(index));
16144     },
16145
16146     updateIndexes : function(startIndex, endIndex){
16147         var ns = this.nodes;
16148         startIndex = startIndex || 0;
16149         endIndex = endIndex || ns.length - 1;
16150         for(var i = startIndex; i <= endIndex; i++){
16151             ns[i].nodeIndex = i;
16152         }
16153     },
16154
16155     /**
16156      * Changes the data store this view uses and refresh the view.
16157      * @param {Store} store
16158      */
16159     setStore : function(store, initial){
16160         if(!initial && this.store){
16161             this.store.un("datachanged", this.refresh);
16162             this.store.un("add", this.onAdd);
16163             this.store.un("remove", this.onRemove);
16164             this.store.un("update", this.onUpdate);
16165             this.store.un("clear", this.refresh);
16166             this.store.un("beforeload", this.onBeforeLoad);
16167             this.store.un("load", this.onLoad);
16168             this.store.un("loadexception", this.onLoad);
16169         }
16170         if(store){
16171           
16172             store.on("datachanged", this.refresh, this);
16173             store.on("add", this.onAdd, this);
16174             store.on("remove", this.onRemove, this);
16175             store.on("update", this.onUpdate, this);
16176             store.on("clear", this.refresh, this);
16177             store.on("beforeload", this.onBeforeLoad, this);
16178             store.on("load", this.onLoad, this);
16179             store.on("loadexception", this.onLoad, this);
16180         }
16181         
16182         if(store){
16183             this.refresh();
16184         }
16185     },
16186     /**
16187      * onbeforeLoad - masks the loading area.
16188      *
16189      */
16190     onBeforeLoad : function(store,opts)
16191     {
16192          //Roo.log('onBeforeLoad');   
16193         if (!opts.add) {
16194             this.el.update("");
16195         }
16196         this.el.mask(this.mask ? this.mask : "Loading" ); 
16197     },
16198     onLoad : function ()
16199     {
16200         this.el.unmask();
16201     },
16202     
16203
16204     /**
16205      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16206      * @param {HTMLElement} node
16207      * @return {HTMLElement} The template node
16208      */
16209     findItemFromChild : function(node){
16210         var el = this.dataName  ?
16211             this.el.child('.roo-tpl-' + this.dataName,true) :
16212             this.el.dom; 
16213         
16214         if(!node || node.parentNode == el){
16215                     return node;
16216             }
16217             var p = node.parentNode;
16218             while(p && p != el){
16219             if(p.parentNode == el){
16220                 return p;
16221             }
16222             p = p.parentNode;
16223         }
16224             return null;
16225     },
16226
16227     /** @ignore */
16228     onClick : function(e){
16229         var item = this.findItemFromChild(e.getTarget());
16230         if(item){
16231             var index = this.indexOf(item);
16232             if(this.onItemClick(item, index, e) !== false){
16233                 this.fireEvent("click", this, index, item, e);
16234             }
16235         }else{
16236             this.clearSelections();
16237         }
16238     },
16239
16240     /** @ignore */
16241     onContextMenu : function(e){
16242         var item = this.findItemFromChild(e.getTarget());
16243         if(item){
16244             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16245         }
16246     },
16247
16248     /** @ignore */
16249     onDblClick : function(e){
16250         var item = this.findItemFromChild(e.getTarget());
16251         if(item){
16252             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16253         }
16254     },
16255
16256     onItemClick : function(item, index, e)
16257     {
16258         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16259             return false;
16260         }
16261         if (this.toggleSelect) {
16262             var m = this.isSelected(item) ? 'unselect' : 'select';
16263             //Roo.log(m);
16264             var _t = this;
16265             _t[m](item, true, false);
16266             return true;
16267         }
16268         if(this.multiSelect || this.singleSelect){
16269             if(this.multiSelect && e.shiftKey && this.lastSelection){
16270                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16271             }else{
16272                 this.select(item, this.multiSelect && e.ctrlKey);
16273                 this.lastSelection = item;
16274             }
16275             
16276             if(!this.tickable){
16277                 e.preventDefault();
16278             }
16279             
16280         }
16281         return true;
16282     },
16283
16284     /**
16285      * Get the number of selected nodes.
16286      * @return {Number}
16287      */
16288     getSelectionCount : function(){
16289         return this.selections.length;
16290     },
16291
16292     /**
16293      * Get the currently selected nodes.
16294      * @return {Array} An array of HTMLElements
16295      */
16296     getSelectedNodes : function(){
16297         return this.selections;
16298     },
16299
16300     /**
16301      * Get the indexes of the selected nodes.
16302      * @return {Array}
16303      */
16304     getSelectedIndexes : function(){
16305         var indexes = [], s = this.selections;
16306         for(var i = 0, len = s.length; i < len; i++){
16307             indexes.push(s[i].nodeIndex);
16308         }
16309         return indexes;
16310     },
16311
16312     /**
16313      * Clear all selections
16314      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16315      */
16316     clearSelections : function(suppressEvent){
16317         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16318             this.cmp.elements = this.selections;
16319             this.cmp.removeClass(this.selectedClass);
16320             this.selections = [];
16321             if(!suppressEvent){
16322                 this.fireEvent("selectionchange", this, this.selections);
16323             }
16324         }
16325     },
16326
16327     /**
16328      * Returns true if the passed node is selected
16329      * @param {HTMLElement/Number} node The node or node index
16330      * @return {Boolean}
16331      */
16332     isSelected : function(node){
16333         var s = this.selections;
16334         if(s.length < 1){
16335             return false;
16336         }
16337         node = this.getNode(node);
16338         return s.indexOf(node) !== -1;
16339     },
16340
16341     /**
16342      * Selects nodes.
16343      * @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
16344      * @param {Boolean} keepExisting (optional) true to keep existing selections
16345      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16346      */
16347     select : function(nodeInfo, keepExisting, suppressEvent){
16348         if(nodeInfo instanceof Array){
16349             if(!keepExisting){
16350                 this.clearSelections(true);
16351             }
16352             for(var i = 0, len = nodeInfo.length; i < len; i++){
16353                 this.select(nodeInfo[i], true, true);
16354             }
16355             return;
16356         } 
16357         var node = this.getNode(nodeInfo);
16358         if(!node || this.isSelected(node)){
16359             return; // already selected.
16360         }
16361         if(!keepExisting){
16362             this.clearSelections(true);
16363         }
16364         
16365         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16366             Roo.fly(node).addClass(this.selectedClass);
16367             this.selections.push(node);
16368             if(!suppressEvent){
16369                 this.fireEvent("selectionchange", this, this.selections);
16370             }
16371         }
16372         
16373         
16374     },
16375       /**
16376      * Unselects nodes.
16377      * @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
16378      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16379      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16380      */
16381     unselect : function(nodeInfo, keepExisting, suppressEvent)
16382     {
16383         if(nodeInfo instanceof Array){
16384             Roo.each(this.selections, function(s) {
16385                 this.unselect(s, nodeInfo);
16386             }, this);
16387             return;
16388         }
16389         var node = this.getNode(nodeInfo);
16390         if(!node || !this.isSelected(node)){
16391             //Roo.log("not selected");
16392             return; // not selected.
16393         }
16394         // fireevent???
16395         var ns = [];
16396         Roo.each(this.selections, function(s) {
16397             if (s == node ) {
16398                 Roo.fly(node).removeClass(this.selectedClass);
16399
16400                 return;
16401             }
16402             ns.push(s);
16403         },this);
16404         
16405         this.selections= ns;
16406         this.fireEvent("selectionchange", this, this.selections);
16407     },
16408
16409     /**
16410      * Gets a template node.
16411      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16412      * @return {HTMLElement} The node or null if it wasn't found
16413      */
16414     getNode : function(nodeInfo){
16415         if(typeof nodeInfo == "string"){
16416             return document.getElementById(nodeInfo);
16417         }else if(typeof nodeInfo == "number"){
16418             return this.nodes[nodeInfo];
16419         }
16420         return nodeInfo;
16421     },
16422
16423     /**
16424      * Gets a range template nodes.
16425      * @param {Number} startIndex
16426      * @param {Number} endIndex
16427      * @return {Array} An array of nodes
16428      */
16429     getNodes : function(start, end){
16430         var ns = this.nodes;
16431         start = start || 0;
16432         end = typeof end == "undefined" ? ns.length - 1 : end;
16433         var nodes = [];
16434         if(start <= end){
16435             for(var i = start; i <= end; i++){
16436                 nodes.push(ns[i]);
16437             }
16438         } else{
16439             for(var i = start; i >= end; i--){
16440                 nodes.push(ns[i]);
16441             }
16442         }
16443         return nodes;
16444     },
16445
16446     /**
16447      * Finds the index of the passed node
16448      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16449      * @return {Number} The index of the node or -1
16450      */
16451     indexOf : function(node){
16452         node = this.getNode(node);
16453         if(typeof node.nodeIndex == "number"){
16454             return node.nodeIndex;
16455         }
16456         var ns = this.nodes;
16457         for(var i = 0, len = ns.length; i < len; i++){
16458             if(ns[i] == node){
16459                 return i;
16460             }
16461         }
16462         return -1;
16463     }
16464 });
16465 /*
16466  * - LGPL
16467  *
16468  * based on jquery fullcalendar
16469  * 
16470  */
16471
16472 Roo.bootstrap = Roo.bootstrap || {};
16473 /**
16474  * @class Roo.bootstrap.Calendar
16475  * @extends Roo.bootstrap.Component
16476  * Bootstrap Calendar class
16477  * @cfg {Boolean} loadMask (true|false) default false
16478  * @cfg {Object} header generate the user specific header of the calendar, default false
16479
16480  * @constructor
16481  * Create a new Container
16482  * @param {Object} config The config object
16483  */
16484
16485
16486
16487 Roo.bootstrap.Calendar = function(config){
16488     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16489      this.addEvents({
16490         /**
16491              * @event select
16492              * Fires when a date is selected
16493              * @param {DatePicker} this
16494              * @param {Date} date The selected date
16495              */
16496         'select': true,
16497         /**
16498              * @event monthchange
16499              * Fires when the displayed month changes 
16500              * @param {DatePicker} this
16501              * @param {Date} date The selected month
16502              */
16503         'monthchange': true,
16504         /**
16505              * @event evententer
16506              * Fires when mouse over an event
16507              * @param {Calendar} this
16508              * @param {event} Event
16509              */
16510         'evententer': true,
16511         /**
16512              * @event eventleave
16513              * Fires when the mouse leaves an
16514              * @param {Calendar} this
16515              * @param {event}
16516              */
16517         'eventleave': true,
16518         /**
16519              * @event eventclick
16520              * Fires when the mouse click an
16521              * @param {Calendar} this
16522              * @param {event}
16523              */
16524         'eventclick': true
16525         
16526     });
16527
16528 };
16529
16530 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16531     
16532      /**
16533      * @cfg {Number} startDay
16534      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16535      */
16536     startDay : 0,
16537     
16538     loadMask : false,
16539     
16540     header : false,
16541       
16542     getAutoCreate : function(){
16543         
16544         
16545         var fc_button = function(name, corner, style, content ) {
16546             return Roo.apply({},{
16547                 tag : 'span',
16548                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16549                          (corner.length ?
16550                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16551                             ''
16552                         ),
16553                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16554                 unselectable: 'on'
16555             });
16556         };
16557         
16558         var header = {};
16559         
16560         if(!this.header){
16561             header = {
16562                 tag : 'table',
16563                 cls : 'fc-header',
16564                 style : 'width:100%',
16565                 cn : [
16566                     {
16567                         tag: 'tr',
16568                         cn : [
16569                             {
16570                                 tag : 'td',
16571                                 cls : 'fc-header-left',
16572                                 cn : [
16573                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16574                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16575                                     { tag: 'span', cls: 'fc-header-space' },
16576                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16577
16578
16579                                 ]
16580                             },
16581
16582                             {
16583                                 tag : 'td',
16584                                 cls : 'fc-header-center',
16585                                 cn : [
16586                                     {
16587                                         tag: 'span',
16588                                         cls: 'fc-header-title',
16589                                         cn : {
16590                                             tag: 'H2',
16591                                             html : 'month / year'
16592                                         }
16593                                     }
16594
16595                                 ]
16596                             },
16597                             {
16598                                 tag : 'td',
16599                                 cls : 'fc-header-right',
16600                                 cn : [
16601                               /*      fc_button('month', 'left', '', 'month' ),
16602                                     fc_button('week', '', '', 'week' ),
16603                                     fc_button('day', 'right', '', 'day' )
16604                                 */    
16605
16606                                 ]
16607                             }
16608
16609                         ]
16610                     }
16611                 ]
16612             };
16613         }
16614         
16615         header = this.header;
16616         
16617        
16618         var cal_heads = function() {
16619             var ret = [];
16620             // fixme - handle this.
16621             
16622             for (var i =0; i < Date.dayNames.length; i++) {
16623                 var d = Date.dayNames[i];
16624                 ret.push({
16625                     tag: 'th',
16626                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16627                     html : d.substring(0,3)
16628                 });
16629                 
16630             }
16631             ret[0].cls += ' fc-first';
16632             ret[6].cls += ' fc-last';
16633             return ret;
16634         };
16635         var cal_cell = function(n) {
16636             return  {
16637                 tag: 'td',
16638                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16639                 cn : [
16640                     {
16641                         cn : [
16642                             {
16643                                 cls: 'fc-day-number',
16644                                 html: 'D'
16645                             },
16646                             {
16647                                 cls: 'fc-day-content',
16648                              
16649                                 cn : [
16650                                      {
16651                                         style: 'position: relative;' // height: 17px;
16652                                     }
16653                                 ]
16654                             }
16655                             
16656                             
16657                         ]
16658                     }
16659                 ]
16660                 
16661             }
16662         };
16663         var cal_rows = function() {
16664             
16665             var ret = [];
16666             for (var r = 0; r < 6; r++) {
16667                 var row= {
16668                     tag : 'tr',
16669                     cls : 'fc-week',
16670                     cn : []
16671                 };
16672                 
16673                 for (var i =0; i < Date.dayNames.length; i++) {
16674                     var d = Date.dayNames[i];
16675                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16676
16677                 }
16678                 row.cn[0].cls+=' fc-first';
16679                 row.cn[0].cn[0].style = 'min-height:90px';
16680                 row.cn[6].cls+=' fc-last';
16681                 ret.push(row);
16682                 
16683             }
16684             ret[0].cls += ' fc-first';
16685             ret[4].cls += ' fc-prev-last';
16686             ret[5].cls += ' fc-last';
16687             return ret;
16688             
16689         };
16690         
16691         var cal_table = {
16692             tag: 'table',
16693             cls: 'fc-border-separate',
16694             style : 'width:100%',
16695             cellspacing  : 0,
16696             cn : [
16697                 { 
16698                     tag: 'thead',
16699                     cn : [
16700                         { 
16701                             tag: 'tr',
16702                             cls : 'fc-first fc-last',
16703                             cn : cal_heads()
16704                         }
16705                     ]
16706                 },
16707                 { 
16708                     tag: 'tbody',
16709                     cn : cal_rows()
16710                 }
16711                   
16712             ]
16713         };
16714          
16715          var cfg = {
16716             cls : 'fc fc-ltr',
16717             cn : [
16718                 header,
16719                 {
16720                     cls : 'fc-content',
16721                     style : "position: relative;",
16722                     cn : [
16723                         {
16724                             cls : 'fc-view fc-view-month fc-grid',
16725                             style : 'position: relative',
16726                             unselectable : 'on',
16727                             cn : [
16728                                 {
16729                                     cls : 'fc-event-container',
16730                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16731                                 },
16732                                 cal_table
16733                             ]
16734                         }
16735                     ]
16736     
16737                 }
16738            ] 
16739             
16740         };
16741         
16742          
16743         
16744         return cfg;
16745     },
16746     
16747     
16748     initEvents : function()
16749     {
16750         if(!this.store){
16751             throw "can not find store for calendar";
16752         }
16753         
16754         var mark = {
16755             tag: "div",
16756             cls:"x-dlg-mask",
16757             style: "text-align:center",
16758             cn: [
16759                 {
16760                     tag: "div",
16761                     style: "background-color:white;width:50%;margin:250 auto",
16762                     cn: [
16763                         {
16764                             tag: "img",
16765                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16766                         },
16767                         {
16768                             tag: "span",
16769                             html: "Loading"
16770                         }
16771                         
16772                     ]
16773                 }
16774             ]
16775         };
16776         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16777         
16778         var size = this.el.select('.fc-content', true).first().getSize();
16779         this.maskEl.setSize(size.width, size.height);
16780         this.maskEl.enableDisplayMode("block");
16781         if(!this.loadMask){
16782             this.maskEl.hide();
16783         }
16784         
16785         this.store = Roo.factory(this.store, Roo.data);
16786         this.store.on('load', this.onLoad, this);
16787         this.store.on('beforeload', this.onBeforeLoad, this);
16788         
16789         this.resize();
16790         
16791         this.cells = this.el.select('.fc-day',true);
16792         //Roo.log(this.cells);
16793         this.textNodes = this.el.query('.fc-day-number');
16794         this.cells.addClassOnOver('fc-state-hover');
16795         
16796         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16797         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16798         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16799         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16800         
16801         this.on('monthchange', this.onMonthChange, this);
16802         
16803         this.update(new Date().clearTime());
16804     },
16805     
16806     resize : function() {
16807         var sz  = this.el.getSize();
16808         
16809         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16810         this.el.select('.fc-day-content div',true).setHeight(34);
16811     },
16812     
16813     
16814     // private
16815     showPrevMonth : function(e){
16816         this.update(this.activeDate.add("mo", -1));
16817     },
16818     showToday : function(e){
16819         this.update(new Date().clearTime());
16820     },
16821     // private
16822     showNextMonth : function(e){
16823         this.update(this.activeDate.add("mo", 1));
16824     },
16825
16826     // private
16827     showPrevYear : function(){
16828         this.update(this.activeDate.add("y", -1));
16829     },
16830
16831     // private
16832     showNextYear : function(){
16833         this.update(this.activeDate.add("y", 1));
16834     },
16835
16836     
16837    // private
16838     update : function(date)
16839     {
16840         var vd = this.activeDate;
16841         this.activeDate = date;
16842 //        if(vd && this.el){
16843 //            var t = date.getTime();
16844 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16845 //                Roo.log('using add remove');
16846 //                
16847 //                this.fireEvent('monthchange', this, date);
16848 //                
16849 //                this.cells.removeClass("fc-state-highlight");
16850 //                this.cells.each(function(c){
16851 //                   if(c.dateValue == t){
16852 //                       c.addClass("fc-state-highlight");
16853 //                       setTimeout(function(){
16854 //                            try{c.dom.firstChild.focus();}catch(e){}
16855 //                       }, 50);
16856 //                       return false;
16857 //                   }
16858 //                   return true;
16859 //                });
16860 //                return;
16861 //            }
16862 //        }
16863         
16864         var days = date.getDaysInMonth();
16865         
16866         var firstOfMonth = date.getFirstDateOfMonth();
16867         var startingPos = firstOfMonth.getDay()-this.startDay;
16868         
16869         if(startingPos < this.startDay){
16870             startingPos += 7;
16871         }
16872         
16873         var pm = date.add(Date.MONTH, -1);
16874         var prevStart = pm.getDaysInMonth()-startingPos;
16875 //        
16876         this.cells = this.el.select('.fc-day',true);
16877         this.textNodes = this.el.query('.fc-day-number');
16878         this.cells.addClassOnOver('fc-state-hover');
16879         
16880         var cells = this.cells.elements;
16881         var textEls = this.textNodes;
16882         
16883         Roo.each(cells, function(cell){
16884             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16885         });
16886         
16887         days += startingPos;
16888
16889         // convert everything to numbers so it's fast
16890         var day = 86400000;
16891         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16892         //Roo.log(d);
16893         //Roo.log(pm);
16894         //Roo.log(prevStart);
16895         
16896         var today = new Date().clearTime().getTime();
16897         var sel = date.clearTime().getTime();
16898         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16899         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16900         var ddMatch = this.disabledDatesRE;
16901         var ddText = this.disabledDatesText;
16902         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16903         var ddaysText = this.disabledDaysText;
16904         var format = this.format;
16905         
16906         var setCellClass = function(cal, cell){
16907             cell.row = 0;
16908             cell.events = [];
16909             cell.more = [];
16910             //Roo.log('set Cell Class');
16911             cell.title = "";
16912             var t = d.getTime();
16913             
16914             //Roo.log(d);
16915             
16916             cell.dateValue = t;
16917             if(t == today){
16918                 cell.className += " fc-today";
16919                 cell.className += " fc-state-highlight";
16920                 cell.title = cal.todayText;
16921             }
16922             if(t == sel){
16923                 // disable highlight in other month..
16924                 //cell.className += " fc-state-highlight";
16925                 
16926             }
16927             // disabling
16928             if(t < min) {
16929                 cell.className = " fc-state-disabled";
16930                 cell.title = cal.minText;
16931                 return;
16932             }
16933             if(t > max) {
16934                 cell.className = " fc-state-disabled";
16935                 cell.title = cal.maxText;
16936                 return;
16937             }
16938             if(ddays){
16939                 if(ddays.indexOf(d.getDay()) != -1){
16940                     cell.title = ddaysText;
16941                     cell.className = " fc-state-disabled";
16942                 }
16943             }
16944             if(ddMatch && format){
16945                 var fvalue = d.dateFormat(format);
16946                 if(ddMatch.test(fvalue)){
16947                     cell.title = ddText.replace("%0", fvalue);
16948                     cell.className = " fc-state-disabled";
16949                 }
16950             }
16951             
16952             if (!cell.initialClassName) {
16953                 cell.initialClassName = cell.dom.className;
16954             }
16955             
16956             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16957         };
16958
16959         var i = 0;
16960         
16961         for(; i < startingPos; i++) {
16962             textEls[i].innerHTML = (++prevStart);
16963             d.setDate(d.getDate()+1);
16964             
16965             cells[i].className = "fc-past fc-other-month";
16966             setCellClass(this, cells[i]);
16967         }
16968         
16969         var intDay = 0;
16970         
16971         for(; i < days; i++){
16972             intDay = i - startingPos + 1;
16973             textEls[i].innerHTML = (intDay);
16974             d.setDate(d.getDate()+1);
16975             
16976             cells[i].className = ''; // "x-date-active";
16977             setCellClass(this, cells[i]);
16978         }
16979         var extraDays = 0;
16980         
16981         for(; i < 42; i++) {
16982             textEls[i].innerHTML = (++extraDays);
16983             d.setDate(d.getDate()+1);
16984             
16985             cells[i].className = "fc-future fc-other-month";
16986             setCellClass(this, cells[i]);
16987         }
16988         
16989         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16990         
16991         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16992         
16993         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16994         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16995         
16996         if(totalRows != 6){
16997             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16998             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16999         }
17000         
17001         this.fireEvent('monthchange', this, date);
17002         
17003         
17004         /*
17005         if(!this.internalRender){
17006             var main = this.el.dom.firstChild;
17007             var w = main.offsetWidth;
17008             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17009             Roo.fly(main).setWidth(w);
17010             this.internalRender = true;
17011             // opera does not respect the auto grow header center column
17012             // then, after it gets a width opera refuses to recalculate
17013             // without a second pass
17014             if(Roo.isOpera && !this.secondPass){
17015                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17016                 this.secondPass = true;
17017                 this.update.defer(10, this, [date]);
17018             }
17019         }
17020         */
17021         
17022     },
17023     
17024     findCell : function(dt) {
17025         dt = dt.clearTime().getTime();
17026         var ret = false;
17027         this.cells.each(function(c){
17028             //Roo.log("check " +c.dateValue + '?=' + dt);
17029             if(c.dateValue == dt){
17030                 ret = c;
17031                 return false;
17032             }
17033             return true;
17034         });
17035         
17036         return ret;
17037     },
17038     
17039     findCells : function(ev) {
17040         var s = ev.start.clone().clearTime().getTime();
17041        // Roo.log(s);
17042         var e= ev.end.clone().clearTime().getTime();
17043        // Roo.log(e);
17044         var ret = [];
17045         this.cells.each(function(c){
17046              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17047             
17048             if(c.dateValue > e){
17049                 return ;
17050             }
17051             if(c.dateValue < s){
17052                 return ;
17053             }
17054             ret.push(c);
17055         });
17056         
17057         return ret;    
17058     },
17059     
17060 //    findBestRow: function(cells)
17061 //    {
17062 //        var ret = 0;
17063 //        
17064 //        for (var i =0 ; i < cells.length;i++) {
17065 //            ret  = Math.max(cells[i].rows || 0,ret);
17066 //        }
17067 //        return ret;
17068 //        
17069 //    },
17070     
17071     
17072     addItem : function(ev)
17073     {
17074         // look for vertical location slot in
17075         var cells = this.findCells(ev);
17076         
17077 //        ev.row = this.findBestRow(cells);
17078         
17079         // work out the location.
17080         
17081         var crow = false;
17082         var rows = [];
17083         for(var i =0; i < cells.length; i++) {
17084             
17085             cells[i].row = cells[0].row;
17086             
17087             if(i == 0){
17088                 cells[i].row = cells[i].row + 1;
17089             }
17090             
17091             if (!crow) {
17092                 crow = {
17093                     start : cells[i],
17094                     end :  cells[i]
17095                 };
17096                 continue;
17097             }
17098             if (crow.start.getY() == cells[i].getY()) {
17099                 // on same row.
17100                 crow.end = cells[i];
17101                 continue;
17102             }
17103             // different row.
17104             rows.push(crow);
17105             crow = {
17106                 start: cells[i],
17107                 end : cells[i]
17108             };
17109             
17110         }
17111         
17112         rows.push(crow);
17113         ev.els = [];
17114         ev.rows = rows;
17115         ev.cells = cells;
17116         
17117         cells[0].events.push(ev);
17118         
17119         this.calevents.push(ev);
17120     },
17121     
17122     clearEvents: function() {
17123         
17124         if(!this.calevents){
17125             return;
17126         }
17127         
17128         Roo.each(this.cells.elements, function(c){
17129             c.row = 0;
17130             c.events = [];
17131             c.more = [];
17132         });
17133         
17134         Roo.each(this.calevents, function(e) {
17135             Roo.each(e.els, function(el) {
17136                 el.un('mouseenter' ,this.onEventEnter, this);
17137                 el.un('mouseleave' ,this.onEventLeave, this);
17138                 el.remove();
17139             },this);
17140         },this);
17141         
17142         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17143             e.remove();
17144         });
17145         
17146     },
17147     
17148     renderEvents: function()
17149     {   
17150         var _this = this;
17151         
17152         this.cells.each(function(c) {
17153             
17154             if(c.row < 5){
17155                 return;
17156             }
17157             
17158             var ev = c.events;
17159             
17160             var r = 4;
17161             if(c.row != c.events.length){
17162                 r = 4 - (4 - (c.row - c.events.length));
17163             }
17164             
17165             c.events = ev.slice(0, r);
17166             c.more = ev.slice(r);
17167             
17168             if(c.more.length && c.more.length == 1){
17169                 c.events.push(c.more.pop());
17170             }
17171             
17172             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17173             
17174         });
17175             
17176         this.cells.each(function(c) {
17177             
17178             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17179             
17180             
17181             for (var e = 0; e < c.events.length; e++){
17182                 var ev = c.events[e];
17183                 var rows = ev.rows;
17184                 
17185                 for(var i = 0; i < rows.length; i++) {
17186                 
17187                     // how many rows should it span..
17188
17189                     var  cfg = {
17190                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17191                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17192
17193                         unselectable : "on",
17194                         cn : [
17195                             {
17196                                 cls: 'fc-event-inner',
17197                                 cn : [
17198     //                                {
17199     //                                  tag:'span',
17200     //                                  cls: 'fc-event-time',
17201     //                                  html : cells.length > 1 ? '' : ev.time
17202     //                                },
17203                                     {
17204                                       tag:'span',
17205                                       cls: 'fc-event-title',
17206                                       html : String.format('{0}', ev.title)
17207                                     }
17208
17209
17210                                 ]
17211                             },
17212                             {
17213                                 cls: 'ui-resizable-handle ui-resizable-e',
17214                                 html : '&nbsp;&nbsp;&nbsp'
17215                             }
17216
17217                         ]
17218                     };
17219
17220                     if (i == 0) {
17221                         cfg.cls += ' fc-event-start';
17222                     }
17223                     if ((i+1) == rows.length) {
17224                         cfg.cls += ' fc-event-end';
17225                     }
17226
17227                     var ctr = _this.el.select('.fc-event-container',true).first();
17228                     var cg = ctr.createChild(cfg);
17229
17230                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17231                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17232
17233                     var r = (c.more.length) ? 1 : 0;
17234                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17235                     cg.setWidth(ebox.right - sbox.x -2);
17236
17237                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17238                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17239                     cg.on('click', _this.onEventClick, _this, ev);
17240
17241                     ev.els.push(cg);
17242                     
17243                 }
17244                 
17245             }
17246             
17247             
17248             if(c.more.length){
17249                 var  cfg = {
17250                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17251                     style : 'position: absolute',
17252                     unselectable : "on",
17253                     cn : [
17254                         {
17255                             cls: 'fc-event-inner',
17256                             cn : [
17257                                 {
17258                                   tag:'span',
17259                                   cls: 'fc-event-title',
17260                                   html : 'More'
17261                                 }
17262
17263
17264                             ]
17265                         },
17266                         {
17267                             cls: 'ui-resizable-handle ui-resizable-e',
17268                             html : '&nbsp;&nbsp;&nbsp'
17269                         }
17270
17271                     ]
17272                 };
17273
17274                 var ctr = _this.el.select('.fc-event-container',true).first();
17275                 var cg = ctr.createChild(cfg);
17276
17277                 var sbox = c.select('.fc-day-content',true).first().getBox();
17278                 var ebox = c.select('.fc-day-content',true).first().getBox();
17279                 //Roo.log(cg);
17280                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17281                 cg.setWidth(ebox.right - sbox.x -2);
17282
17283                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17284                 
17285             }
17286             
17287         });
17288         
17289         
17290         
17291     },
17292     
17293     onEventEnter: function (e, el,event,d) {
17294         this.fireEvent('evententer', this, el, event);
17295     },
17296     
17297     onEventLeave: function (e, el,event,d) {
17298         this.fireEvent('eventleave', this, el, event);
17299     },
17300     
17301     onEventClick: function (e, el,event,d) {
17302         this.fireEvent('eventclick', this, el, event);
17303     },
17304     
17305     onMonthChange: function () {
17306         this.store.load();
17307     },
17308     
17309     onMoreEventClick: function(e, el, more)
17310     {
17311         var _this = this;
17312         
17313         this.calpopover.placement = 'right';
17314         this.calpopover.setTitle('More');
17315         
17316         this.calpopover.setContent('');
17317         
17318         var ctr = this.calpopover.el.select('.popover-content', true).first();
17319         
17320         Roo.each(more, function(m){
17321             var cfg = {
17322                 cls : 'fc-event-hori fc-event-draggable',
17323                 html : m.title
17324             };
17325             var cg = ctr.createChild(cfg);
17326             
17327             cg.on('click', _this.onEventClick, _this, m);
17328         });
17329         
17330         this.calpopover.show(el);
17331         
17332         
17333     },
17334     
17335     onLoad: function () 
17336     {   
17337         this.calevents = [];
17338         var cal = this;
17339         
17340         if(this.store.getCount() > 0){
17341             this.store.data.each(function(d){
17342                cal.addItem({
17343                     id : d.data.id,
17344                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17345                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17346                     time : d.data.start_time,
17347                     title : d.data.title,
17348                     description : d.data.description,
17349                     venue : d.data.venue
17350                 });
17351             });
17352         }
17353         
17354         this.renderEvents();
17355         
17356         if(this.calevents.length && this.loadMask){
17357             this.maskEl.hide();
17358         }
17359     },
17360     
17361     onBeforeLoad: function()
17362     {
17363         this.clearEvents();
17364         if(this.loadMask){
17365             this.maskEl.show();
17366         }
17367     }
17368 });
17369
17370  
17371  /*
17372  * - LGPL
17373  *
17374  * element
17375  * 
17376  */
17377
17378 /**
17379  * @class Roo.bootstrap.Popover
17380  * @extends Roo.bootstrap.Component
17381  * Bootstrap Popover class
17382  * @cfg {String} html contents of the popover   (or false to use children..)
17383  * @cfg {String} title of popover (or false to hide)
17384  * @cfg {String} placement how it is placed
17385  * @cfg {String} trigger click || hover (or false to trigger manually)
17386  * @cfg {String} over what (parent or false to trigger manually.)
17387  * @cfg {Number} delay - delay before showing
17388  
17389  * @constructor
17390  * Create a new Popover
17391  * @param {Object} config The config object
17392  */
17393
17394 Roo.bootstrap.Popover = function(config){
17395     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17396     
17397     this.addEvents({
17398         // raw events
17399          /**
17400          * @event show
17401          * After the popover show
17402          * 
17403          * @param {Roo.bootstrap.Popover} this
17404          */
17405         "show" : true,
17406         /**
17407          * @event hide
17408          * After the popover hide
17409          * 
17410          * @param {Roo.bootstrap.Popover} this
17411          */
17412         "hide" : true
17413     });
17414 };
17415
17416 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17417     
17418     title: 'Fill in a title',
17419     html: false,
17420     
17421     placement : 'right',
17422     trigger : 'hover', // hover
17423     
17424     delay : 0,
17425     
17426     over: 'parent',
17427     
17428     can_build_overlaid : false,
17429     
17430     getChildContainer : function()
17431     {
17432         return this.el.select('.popover-content',true).first();
17433     },
17434     
17435     getAutoCreate : function(){
17436          
17437         var cfg = {
17438            cls : 'popover roo-dynamic',
17439            style: 'display:block',
17440            cn : [
17441                 {
17442                     cls : 'arrow'
17443                 },
17444                 {
17445                     cls : 'popover-inner',
17446                     cn : [
17447                         {
17448                             tag: 'h3',
17449                             cls: 'popover-title',
17450                             html : this.title
17451                         },
17452                         {
17453                             cls : 'popover-content',
17454                             html : this.html
17455                         }
17456                     ]
17457                     
17458                 }
17459            ]
17460         };
17461         
17462         return cfg;
17463     },
17464     setTitle: function(str)
17465     {
17466         this.title = str;
17467         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17468     },
17469     setContent: function(str)
17470     {
17471         this.html = str;
17472         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17473     },
17474     // as it get's added to the bottom of the page.
17475     onRender : function(ct, position)
17476     {
17477         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17478         if(!this.el){
17479             var cfg = Roo.apply({},  this.getAutoCreate());
17480             cfg.id = Roo.id();
17481             
17482             if (this.cls) {
17483                 cfg.cls += ' ' + this.cls;
17484             }
17485             if (this.style) {
17486                 cfg.style = this.style;
17487             }
17488             //Roo.log("adding to ");
17489             this.el = Roo.get(document.body).createChild(cfg, position);
17490 //            Roo.log(this.el);
17491         }
17492         this.initEvents();
17493     },
17494     
17495     initEvents : function()
17496     {
17497         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17498         this.el.enableDisplayMode('block');
17499         this.el.hide();
17500         if (this.over === false) {
17501             return; 
17502         }
17503         if (this.triggers === false) {
17504             return;
17505         }
17506         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17507         var triggers = this.trigger ? this.trigger.split(' ') : [];
17508         Roo.each(triggers, function(trigger) {
17509         
17510             if (trigger == 'click') {
17511                 on_el.on('click', this.toggle, this);
17512             } else if (trigger != 'manual') {
17513                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17514                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17515       
17516                 on_el.on(eventIn  ,this.enter, this);
17517                 on_el.on(eventOut, this.leave, this);
17518             }
17519         }, this);
17520         
17521     },
17522     
17523     
17524     // private
17525     timeout : null,
17526     hoverState : null,
17527     
17528     toggle : function () {
17529         this.hoverState == 'in' ? this.leave() : this.enter();
17530     },
17531     
17532     enter : function () {
17533         
17534         clearTimeout(this.timeout);
17535     
17536         this.hoverState = 'in';
17537     
17538         if (!this.delay || !this.delay.show) {
17539             this.show();
17540             return;
17541         }
17542         var _t = this;
17543         this.timeout = setTimeout(function () {
17544             if (_t.hoverState == 'in') {
17545                 _t.show();
17546             }
17547         }, this.delay.show)
17548     },
17549     
17550     leave : function() {
17551         clearTimeout(this.timeout);
17552     
17553         this.hoverState = 'out';
17554     
17555         if (!this.delay || !this.delay.hide) {
17556             this.hide();
17557             return;
17558         }
17559         var _t = this;
17560         this.timeout = setTimeout(function () {
17561             if (_t.hoverState == 'out') {
17562                 _t.hide();
17563             }
17564         }, this.delay.hide)
17565     },
17566     
17567     show : function (on_el)
17568     {
17569         if (!on_el) {
17570             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17571         }
17572         
17573         // set content.
17574         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17575         if (this.html !== false) {
17576             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17577         }
17578         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17579         if (!this.title.length) {
17580             this.el.select('.popover-title',true).hide();
17581         }
17582         
17583         var placement = typeof this.placement == 'function' ?
17584             this.placement.call(this, this.el, on_el) :
17585             this.placement;
17586             
17587         var autoToken = /\s?auto?\s?/i;
17588         var autoPlace = autoToken.test(placement);
17589         if (autoPlace) {
17590             placement = placement.replace(autoToken, '') || 'top';
17591         }
17592         
17593         //this.el.detach()
17594         //this.el.setXY([0,0]);
17595         this.el.show();
17596         this.el.dom.style.display='block';
17597         this.el.addClass(placement);
17598         
17599         //this.el.appendTo(on_el);
17600         
17601         var p = this.getPosition();
17602         var box = this.el.getBox();
17603         
17604         if (autoPlace) {
17605             // fixme..
17606         }
17607         var align = Roo.bootstrap.Popover.alignment[placement];
17608         
17609 //        Roo.log(align);
17610         this.el.alignTo(on_el, align[0],align[1]);
17611         //var arrow = this.el.select('.arrow',true).first();
17612         //arrow.set(align[2], 
17613         
17614         this.el.addClass('in');
17615         
17616         
17617         if (this.el.hasClass('fade')) {
17618             // fade it?
17619         }
17620         
17621         this.hoverState = 'in';
17622         
17623         this.fireEvent('show', this);
17624         
17625     },
17626     hide : function()
17627     {
17628         this.el.setXY([0,0]);
17629         this.el.removeClass('in');
17630         this.el.hide();
17631         this.hoverState = null;
17632         
17633         this.fireEvent('hide', this);
17634     }
17635     
17636 });
17637
17638 Roo.bootstrap.Popover.alignment = {
17639     'left' : ['r-l', [-10,0], 'right'],
17640     'right' : ['l-r', [10,0], 'left'],
17641     'bottom' : ['t-b', [0,10], 'top'],
17642     'top' : [ 'b-t', [0,-10], 'bottom']
17643 };
17644
17645  /*
17646  * - LGPL
17647  *
17648  * Progress
17649  * 
17650  */
17651
17652 /**
17653  * @class Roo.bootstrap.Progress
17654  * @extends Roo.bootstrap.Component
17655  * Bootstrap Progress class
17656  * @cfg {Boolean} striped striped of the progress bar
17657  * @cfg {Boolean} active animated of the progress bar
17658  * 
17659  * 
17660  * @constructor
17661  * Create a new Progress
17662  * @param {Object} config The config object
17663  */
17664
17665 Roo.bootstrap.Progress = function(config){
17666     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17667 };
17668
17669 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17670     
17671     striped : false,
17672     active: false,
17673     
17674     getAutoCreate : function(){
17675         var cfg = {
17676             tag: 'div',
17677             cls: 'progress'
17678         };
17679         
17680         
17681         if(this.striped){
17682             cfg.cls += ' progress-striped';
17683         }
17684       
17685         if(this.active){
17686             cfg.cls += ' active';
17687         }
17688         
17689         
17690         return cfg;
17691     }
17692    
17693 });
17694
17695  
17696
17697  /*
17698  * - LGPL
17699  *
17700  * ProgressBar
17701  * 
17702  */
17703
17704 /**
17705  * @class Roo.bootstrap.ProgressBar
17706  * @extends Roo.bootstrap.Component
17707  * Bootstrap ProgressBar class
17708  * @cfg {Number} aria_valuenow aria-value now
17709  * @cfg {Number} aria_valuemin aria-value min
17710  * @cfg {Number} aria_valuemax aria-value max
17711  * @cfg {String} label label for the progress bar
17712  * @cfg {String} panel (success | info | warning | danger )
17713  * @cfg {String} role role of the progress bar
17714  * @cfg {String} sr_only text
17715  * 
17716  * 
17717  * @constructor
17718  * Create a new ProgressBar
17719  * @param {Object} config The config object
17720  */
17721
17722 Roo.bootstrap.ProgressBar = function(config){
17723     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17724 };
17725
17726 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17727     
17728     aria_valuenow : 0,
17729     aria_valuemin : 0,
17730     aria_valuemax : 100,
17731     label : false,
17732     panel : false,
17733     role : false,
17734     sr_only: false,
17735     
17736     getAutoCreate : function()
17737     {
17738         
17739         var cfg = {
17740             tag: 'div',
17741             cls: 'progress-bar',
17742             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17743         };
17744         
17745         if(this.sr_only){
17746             cfg.cn = {
17747                 tag: 'span',
17748                 cls: 'sr-only',
17749                 html: this.sr_only
17750             }
17751         }
17752         
17753         if(this.role){
17754             cfg.role = this.role;
17755         }
17756         
17757         if(this.aria_valuenow){
17758             cfg['aria-valuenow'] = this.aria_valuenow;
17759         }
17760         
17761         if(this.aria_valuemin){
17762             cfg['aria-valuemin'] = this.aria_valuemin;
17763         }
17764         
17765         if(this.aria_valuemax){
17766             cfg['aria-valuemax'] = this.aria_valuemax;
17767         }
17768         
17769         if(this.label && !this.sr_only){
17770             cfg.html = this.label;
17771         }
17772         
17773         if(this.panel){
17774             cfg.cls += ' progress-bar-' + this.panel;
17775         }
17776         
17777         return cfg;
17778     },
17779     
17780     update : function(aria_valuenow)
17781     {
17782         this.aria_valuenow = aria_valuenow;
17783         
17784         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17785     }
17786    
17787 });
17788
17789  
17790
17791  /*
17792  * - LGPL
17793  *
17794  * column
17795  * 
17796  */
17797
17798 /**
17799  * @class Roo.bootstrap.TabGroup
17800  * @extends Roo.bootstrap.Column
17801  * Bootstrap Column class
17802  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17803  * @cfg {Boolean} carousel true to make the group behave like a carousel
17804  * @cfg {Boolean} bullets show bullets for the panels
17805  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17806  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17807  * @cfg {Boolean} showarrow (true|false) show arrow default true
17808  * 
17809  * @constructor
17810  * Create a new TabGroup
17811  * @param {Object} config The config object
17812  */
17813
17814 Roo.bootstrap.TabGroup = function(config){
17815     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17816     if (!this.navId) {
17817         this.navId = Roo.id();
17818     }
17819     this.tabs = [];
17820     Roo.bootstrap.TabGroup.register(this);
17821     
17822 };
17823
17824 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17825     
17826     carousel : false,
17827     transition : false,
17828     bullets : 0,
17829     timer : 0,
17830     autoslide : false,
17831     slideFn : false,
17832     slideOnTouch : false,
17833     showarrow : true,
17834     
17835     getAutoCreate : function()
17836     {
17837         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17838         
17839         cfg.cls += ' tab-content';
17840         
17841         if (this.carousel) {
17842             cfg.cls += ' carousel slide';
17843             
17844             cfg.cn = [{
17845                cls : 'carousel-inner',
17846                cn : []
17847             }];
17848         
17849             if(this.bullets  && !Roo.isTouch){
17850                 
17851                 var bullets = {
17852                     cls : 'carousel-bullets',
17853                     cn : []
17854                 };
17855                
17856                 if(this.bullets_cls){
17857                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17858                 }
17859                 
17860                 bullets.cn.push({
17861                     cls : 'clear'
17862                 });
17863                 
17864                 cfg.cn[0].cn.push(bullets);
17865             }
17866             
17867             if(this.showarrow){
17868                 cfg.cn[0].cn.push({
17869                     tag : 'div',
17870                     class : 'carousel-arrow',
17871                     cn : [
17872                         {
17873                             tag : 'div',
17874                             class : 'carousel-prev',
17875                             cn : [
17876                                 {
17877                                     tag : 'i',
17878                                     class : 'fa fa-chevron-left'
17879                                 }
17880                             ]
17881                         },
17882                         {
17883                             tag : 'div',
17884                             class : 'carousel-next',
17885                             cn : [
17886                                 {
17887                                     tag : 'i',
17888                                     class : 'fa fa-chevron-right'
17889                                 }
17890                             ]
17891                         }
17892                     ]
17893                 });
17894             }
17895             
17896         }
17897         
17898         return cfg;
17899     },
17900     
17901     initEvents:  function()
17902     {
17903 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17904 //            this.el.on("touchstart", this.onTouchStart, this);
17905 //        }
17906         
17907         if(this.autoslide){
17908             var _this = this;
17909             
17910             this.slideFn = window.setInterval(function() {
17911                 _this.showPanelNext();
17912             }, this.timer);
17913         }
17914         
17915         if(this.showarrow){
17916             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17917             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17918         }
17919         
17920         
17921     },
17922     
17923 //    onTouchStart : function(e, el, o)
17924 //    {
17925 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17926 //            return;
17927 //        }
17928 //        
17929 //        this.showPanelNext();
17930 //    },
17931     
17932     
17933     getChildContainer : function()
17934     {
17935         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17936     },
17937     
17938     /**
17939     * register a Navigation item
17940     * @param {Roo.bootstrap.NavItem} the navitem to add
17941     */
17942     register : function(item)
17943     {
17944         this.tabs.push( item);
17945         item.navId = this.navId; // not really needed..
17946         this.addBullet();
17947     
17948     },
17949     
17950     getActivePanel : function()
17951     {
17952         var r = false;
17953         Roo.each(this.tabs, function(t) {
17954             if (t.active) {
17955                 r = t;
17956                 return false;
17957             }
17958             return null;
17959         });
17960         return r;
17961         
17962     },
17963     getPanelByName : function(n)
17964     {
17965         var r = false;
17966         Roo.each(this.tabs, function(t) {
17967             if (t.tabId == n) {
17968                 r = t;
17969                 return false;
17970             }
17971             return null;
17972         });
17973         return r;
17974     },
17975     indexOfPanel : function(p)
17976     {
17977         var r = false;
17978         Roo.each(this.tabs, function(t,i) {
17979             if (t.tabId == p.tabId) {
17980                 r = i;
17981                 return false;
17982             }
17983             return null;
17984         });
17985         return r;
17986     },
17987     /**
17988      * show a specific panel
17989      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17990      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17991      */
17992     showPanel : function (pan)
17993     {
17994         if(this.transition || typeof(pan) == 'undefined'){
17995             Roo.log("waiting for the transitionend");
17996             return;
17997         }
17998         
17999         if (typeof(pan) == 'number') {
18000             pan = this.tabs[pan];
18001         }
18002         
18003         if (typeof(pan) == 'string') {
18004             pan = this.getPanelByName(pan);
18005         }
18006         
18007         var cur = this.getActivePanel();
18008         
18009         if(!pan || !cur){
18010             Roo.log('pan or acitve pan is undefined');
18011             return false;
18012         }
18013         
18014         if (pan.tabId == this.getActivePanel().tabId) {
18015             return true;
18016         }
18017         
18018         if (false === cur.fireEvent('beforedeactivate')) {
18019             return false;
18020         }
18021         
18022         if(this.bullets > 0 && !Roo.isTouch){
18023             this.setActiveBullet(this.indexOfPanel(pan));
18024         }
18025         
18026         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18027             
18028             this.transition = true;
18029             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18030             var lr = dir == 'next' ? 'left' : 'right';
18031             pan.el.addClass(dir); // or prev
18032             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18033             cur.el.addClass(lr); // or right
18034             pan.el.addClass(lr);
18035             
18036             var _this = this;
18037             cur.el.on('transitionend', function() {
18038                 Roo.log("trans end?");
18039                 
18040                 pan.el.removeClass([lr,dir]);
18041                 pan.setActive(true);
18042                 
18043                 cur.el.removeClass([lr]);
18044                 cur.setActive(false);
18045                 
18046                 _this.transition = false;
18047                 
18048             }, this, { single:  true } );
18049             
18050             return true;
18051         }
18052         
18053         cur.setActive(false);
18054         pan.setActive(true);
18055         
18056         return true;
18057         
18058     },
18059     showPanelNext : function()
18060     {
18061         var i = this.indexOfPanel(this.getActivePanel());
18062         
18063         if (i >= this.tabs.length - 1 && !this.autoslide) {
18064             return;
18065         }
18066         
18067         if (i >= this.tabs.length - 1 && this.autoslide) {
18068             i = -1;
18069         }
18070         
18071         this.showPanel(this.tabs[i+1]);
18072     },
18073     
18074     showPanelPrev : function()
18075     {
18076         var i = this.indexOfPanel(this.getActivePanel());
18077         
18078         if (i  < 1 && !this.autoslide) {
18079             return;
18080         }
18081         
18082         if (i < 1 && this.autoslide) {
18083             i = this.tabs.length;
18084         }
18085         
18086         this.showPanel(this.tabs[i-1]);
18087     },
18088     
18089     
18090     addBullet: function()
18091     {
18092         if(!this.bullets || Roo.isTouch){
18093             return;
18094         }
18095         var ctr = this.el.select('.carousel-bullets',true).first();
18096         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18097         var bullet = ctr.createChild({
18098             cls : 'bullet bullet-' + i
18099         },ctr.dom.lastChild);
18100         
18101         
18102         var _this = this;
18103         
18104         bullet.on('click', (function(e, el, o, ii, t){
18105
18106             e.preventDefault();
18107
18108             this.showPanel(ii);
18109
18110             if(this.autoslide && this.slideFn){
18111                 clearInterval(this.slideFn);
18112                 this.slideFn = window.setInterval(function() {
18113                     _this.showPanelNext();
18114                 }, this.timer);
18115             }
18116
18117         }).createDelegate(this, [i, bullet], true));
18118                 
18119         
18120     },
18121      
18122     setActiveBullet : function(i)
18123     {
18124         if(Roo.isTouch){
18125             return;
18126         }
18127         
18128         Roo.each(this.el.select('.bullet', true).elements, function(el){
18129             el.removeClass('selected');
18130         });
18131
18132         var bullet = this.el.select('.bullet-' + i, true).first();
18133         
18134         if(!bullet){
18135             return;
18136         }
18137         
18138         bullet.addClass('selected');
18139     }
18140     
18141     
18142   
18143 });
18144
18145  
18146
18147  
18148  
18149 Roo.apply(Roo.bootstrap.TabGroup, {
18150     
18151     groups: {},
18152      /**
18153     * register a Navigation Group
18154     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18155     */
18156     register : function(navgrp)
18157     {
18158         this.groups[navgrp.navId] = navgrp;
18159         
18160     },
18161     /**
18162     * fetch a Navigation Group based on the navigation ID
18163     * if one does not exist , it will get created.
18164     * @param {string} the navgroup to add
18165     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18166     */
18167     get: function(navId) {
18168         if (typeof(this.groups[navId]) == 'undefined') {
18169             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18170         }
18171         return this.groups[navId] ;
18172     }
18173     
18174     
18175     
18176 });
18177
18178  /*
18179  * - LGPL
18180  *
18181  * TabPanel
18182  * 
18183  */
18184
18185 /**
18186  * @class Roo.bootstrap.TabPanel
18187  * @extends Roo.bootstrap.Component
18188  * Bootstrap TabPanel class
18189  * @cfg {Boolean} active panel active
18190  * @cfg {String} html panel content
18191  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18192  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18193  * @cfg {String} href click to link..
18194  * 
18195  * 
18196  * @constructor
18197  * Create a new TabPanel
18198  * @param {Object} config The config object
18199  */
18200
18201 Roo.bootstrap.TabPanel = function(config){
18202     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18203     this.addEvents({
18204         /**
18205              * @event changed
18206              * Fires when the active status changes
18207              * @param {Roo.bootstrap.TabPanel} this
18208              * @param {Boolean} state the new state
18209             
18210          */
18211         'changed': true,
18212         /**
18213              * @event beforedeactivate
18214              * Fires before a tab is de-activated - can be used to do validation on a form.
18215              * @param {Roo.bootstrap.TabPanel} this
18216              * @return {Boolean} false if there is an error
18217             
18218          */
18219         'beforedeactivate': true
18220      });
18221     
18222     this.tabId = this.tabId || Roo.id();
18223   
18224 };
18225
18226 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18227     
18228     active: false,
18229     html: false,
18230     tabId: false,
18231     navId : false,
18232     href : '',
18233     
18234     getAutoCreate : function(){
18235         var cfg = {
18236             tag: 'div',
18237             // item is needed for carousel - not sure if it has any effect otherwise
18238             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18239             html: this.html || ''
18240         };
18241         
18242         if(this.active){
18243             cfg.cls += ' active';
18244         }
18245         
18246         if(this.tabId){
18247             cfg.tabId = this.tabId;
18248         }
18249         
18250         
18251         return cfg;
18252     },
18253     
18254     initEvents:  function()
18255     {
18256         var p = this.parent();
18257         
18258         this.navId = this.navId || p.navId;
18259         
18260         if (typeof(this.navId) != 'undefined') {
18261             // not really needed.. but just in case.. parent should be a NavGroup.
18262             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18263             
18264             tg.register(this);
18265             
18266             var i = tg.tabs.length - 1;
18267             
18268             if(this.active && tg.bullets > 0 && i < tg.bullets){
18269                 tg.setActiveBullet(i);
18270             }
18271         }
18272         
18273         this.el.on('click', this.onClick, this);
18274         
18275         if(Roo.isTouch){
18276             this.el.on("touchstart", this.onTouchStart, this);
18277             this.el.on("touchmove", this.onTouchMove, this);
18278             this.el.on("touchend", this.onTouchEnd, this);
18279         }
18280         
18281     },
18282     
18283     onRender : function(ct, position)
18284     {
18285         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18286     },
18287     
18288     setActive : function(state)
18289     {
18290         Roo.log("panel - set active " + this.tabId + "=" + state);
18291         
18292         this.active = state;
18293         if (!state) {
18294             this.el.removeClass('active');
18295             
18296         } else  if (!this.el.hasClass('active')) {
18297             this.el.addClass('active');
18298         }
18299         
18300         this.fireEvent('changed', this, state);
18301     },
18302     
18303     onClick : function(e)
18304     {
18305         e.preventDefault();
18306         
18307         if(!this.href.length){
18308             return;
18309         }
18310         
18311         window.location.href = this.href;
18312     },
18313     
18314     startX : 0,
18315     startY : 0,
18316     endX : 0,
18317     endY : 0,
18318     swiping : false,
18319     
18320     onTouchStart : function(e)
18321     {
18322         this.swiping = false;
18323         
18324         this.startX = e.browserEvent.touches[0].clientX;
18325         this.startY = e.browserEvent.touches[0].clientY;
18326     },
18327     
18328     onTouchMove : function(e)
18329     {
18330         this.swiping = true;
18331         
18332         this.endX = e.browserEvent.touches[0].clientX;
18333         this.endY = e.browserEvent.touches[0].clientY;
18334     },
18335     
18336     onTouchEnd : function(e)
18337     {
18338         if(!this.swiping){
18339             this.onClick(e);
18340             return;
18341         }
18342         
18343         var tabGroup = this.parent();
18344         
18345         if(this.endX > this.startX){ // swiping right
18346             tabGroup.showPanelPrev();
18347             return;
18348         }
18349         
18350         if(this.startX > this.endX){ // swiping left
18351             tabGroup.showPanelNext();
18352             return;
18353         }
18354     }
18355     
18356     
18357 });
18358  
18359
18360  
18361
18362  /*
18363  * - LGPL
18364  *
18365  * DateField
18366  * 
18367  */
18368
18369 /**
18370  * @class Roo.bootstrap.DateField
18371  * @extends Roo.bootstrap.Input
18372  * Bootstrap DateField class
18373  * @cfg {Number} weekStart default 0
18374  * @cfg {String} viewMode default empty, (months|years)
18375  * @cfg {String} minViewMode default empty, (months|years)
18376  * @cfg {Number} startDate default -Infinity
18377  * @cfg {Number} endDate default Infinity
18378  * @cfg {Boolean} todayHighlight default false
18379  * @cfg {Boolean} todayBtn default false
18380  * @cfg {Boolean} calendarWeeks default false
18381  * @cfg {Object} daysOfWeekDisabled default empty
18382  * @cfg {Boolean} singleMode default false (true | false)
18383  * 
18384  * @cfg {Boolean} keyboardNavigation default true
18385  * @cfg {String} language default en
18386  * 
18387  * @constructor
18388  * Create a new DateField
18389  * @param {Object} config The config object
18390  */
18391
18392 Roo.bootstrap.DateField = function(config){
18393     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18394      this.addEvents({
18395             /**
18396              * @event show
18397              * Fires when this field show.
18398              * @param {Roo.bootstrap.DateField} this
18399              * @param {Mixed} date The date value
18400              */
18401             show : true,
18402             /**
18403              * @event show
18404              * Fires when this field hide.
18405              * @param {Roo.bootstrap.DateField} this
18406              * @param {Mixed} date The date value
18407              */
18408             hide : true,
18409             /**
18410              * @event select
18411              * Fires when select a date.
18412              * @param {Roo.bootstrap.DateField} this
18413              * @param {Mixed} date The date value
18414              */
18415             select : true,
18416             /**
18417              * @event beforeselect
18418              * Fires when before select a date.
18419              * @param {Roo.bootstrap.DateField} this
18420              * @param {Mixed} date The date value
18421              */
18422             beforeselect : true
18423         });
18424 };
18425
18426 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18427     
18428     /**
18429      * @cfg {String} format
18430      * The default date format string which can be overriden for localization support.  The format must be
18431      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18432      */
18433     format : "m/d/y",
18434     /**
18435      * @cfg {String} altFormats
18436      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18437      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18438      */
18439     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18440     
18441     weekStart : 0,
18442     
18443     viewMode : '',
18444     
18445     minViewMode : '',
18446     
18447     todayHighlight : false,
18448     
18449     todayBtn: false,
18450     
18451     language: 'en',
18452     
18453     keyboardNavigation: true,
18454     
18455     calendarWeeks: false,
18456     
18457     startDate: -Infinity,
18458     
18459     endDate: Infinity,
18460     
18461     daysOfWeekDisabled: [],
18462     
18463     _events: [],
18464     
18465     singleMode : false,
18466     
18467     UTCDate: function()
18468     {
18469         return new Date(Date.UTC.apply(Date, arguments));
18470     },
18471     
18472     UTCToday: function()
18473     {
18474         var today = new Date();
18475         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18476     },
18477     
18478     getDate: function() {
18479             var d = this.getUTCDate();
18480             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18481     },
18482     
18483     getUTCDate: function() {
18484             return this.date;
18485     },
18486     
18487     setDate: function(d) {
18488             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18489     },
18490     
18491     setUTCDate: function(d) {
18492             this.date = d;
18493             this.setValue(this.formatDate(this.date));
18494     },
18495         
18496     onRender: function(ct, position)
18497     {
18498         
18499         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18500         
18501         this.language = this.language || 'en';
18502         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18503         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18504         
18505         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18506         this.format = this.format || 'm/d/y';
18507         this.isInline = false;
18508         this.isInput = true;
18509         this.component = this.el.select('.add-on', true).first() || false;
18510         this.component = (this.component && this.component.length === 0) ? false : this.component;
18511         this.hasInput = this.component && this.inputEl().length;
18512         
18513         if (typeof(this.minViewMode === 'string')) {
18514             switch (this.minViewMode) {
18515                 case 'months':
18516                     this.minViewMode = 1;
18517                     break;
18518                 case 'years':
18519                     this.minViewMode = 2;
18520                     break;
18521                 default:
18522                     this.minViewMode = 0;
18523                     break;
18524             }
18525         }
18526         
18527         if (typeof(this.viewMode === 'string')) {
18528             switch (this.viewMode) {
18529                 case 'months':
18530                     this.viewMode = 1;
18531                     break;
18532                 case 'years':
18533                     this.viewMode = 2;
18534                     break;
18535                 default:
18536                     this.viewMode = 0;
18537                     break;
18538             }
18539         }
18540                 
18541         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18542         
18543 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18544         
18545         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18546         
18547         this.picker().on('mousedown', this.onMousedown, this);
18548         this.picker().on('click', this.onClick, this);
18549         
18550         this.picker().addClass('datepicker-dropdown');
18551         
18552         this.startViewMode = this.viewMode;
18553         
18554         if(this.singleMode){
18555             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18556                 v.setVisibilityMode(Roo.Element.DISPLAY);
18557                 v.hide();
18558             });
18559             
18560             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18561                 v.setStyle('width', '189px');
18562             });
18563         }
18564         
18565         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18566             if(!this.calendarWeeks){
18567                 v.remove();
18568                 return;
18569             }
18570             
18571             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18572             v.attr('colspan', function(i, val){
18573                 return parseInt(val) + 1;
18574             });
18575         });
18576                         
18577         
18578         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18579         
18580         this.setStartDate(this.startDate);
18581         this.setEndDate(this.endDate);
18582         
18583         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18584         
18585         this.fillDow();
18586         this.fillMonths();
18587         this.update();
18588         this.showMode();
18589         
18590         if(this.isInline) {
18591             this.showPopup();
18592         }
18593     },
18594     
18595     picker : function()
18596     {
18597         return this.pickerEl;
18598 //        return this.el.select('.datepicker', true).first();
18599     },
18600     
18601     fillDow: function()
18602     {
18603         var dowCnt = this.weekStart;
18604         
18605         var dow = {
18606             tag: 'tr',
18607             cn: [
18608                 
18609             ]
18610         };
18611         
18612         if(this.calendarWeeks){
18613             dow.cn.push({
18614                 tag: 'th',
18615                 cls: 'cw',
18616                 html: '&nbsp;'
18617             })
18618         }
18619         
18620         while (dowCnt < this.weekStart + 7) {
18621             dow.cn.push({
18622                 tag: 'th',
18623                 cls: 'dow',
18624                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18625             });
18626         }
18627         
18628         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18629     },
18630     
18631     fillMonths: function()
18632     {    
18633         var i = 0;
18634         var months = this.picker().select('>.datepicker-months td', true).first();
18635         
18636         months.dom.innerHTML = '';
18637         
18638         while (i < 12) {
18639             var month = {
18640                 tag: 'span',
18641                 cls: 'month',
18642                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18643             };
18644             
18645             months.createChild(month);
18646         }
18647         
18648     },
18649     
18650     update: function()
18651     {
18652         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;
18653         
18654         if (this.date < this.startDate) {
18655             this.viewDate = new Date(this.startDate);
18656         } else if (this.date > this.endDate) {
18657             this.viewDate = new Date(this.endDate);
18658         } else {
18659             this.viewDate = new Date(this.date);
18660         }
18661         
18662         this.fill();
18663     },
18664     
18665     fill: function() 
18666     {
18667         var d = new Date(this.viewDate),
18668                 year = d.getUTCFullYear(),
18669                 month = d.getUTCMonth(),
18670                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18671                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18672                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18673                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18674                 currentDate = this.date && this.date.valueOf(),
18675                 today = this.UTCToday();
18676         
18677         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18678         
18679 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18680         
18681 //        this.picker.select('>tfoot th.today').
18682 //                                              .text(dates[this.language].today)
18683 //                                              .toggle(this.todayBtn !== false);
18684     
18685         this.updateNavArrows();
18686         this.fillMonths();
18687                                                 
18688         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18689         
18690         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18691          
18692         prevMonth.setUTCDate(day);
18693         
18694         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18695         
18696         var nextMonth = new Date(prevMonth);
18697         
18698         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18699         
18700         nextMonth = nextMonth.valueOf();
18701         
18702         var fillMonths = false;
18703         
18704         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18705         
18706         while(prevMonth.valueOf() <= nextMonth) {
18707             var clsName = '';
18708             
18709             if (prevMonth.getUTCDay() === this.weekStart) {
18710                 if(fillMonths){
18711                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18712                 }
18713                     
18714                 fillMonths = {
18715                     tag: 'tr',
18716                     cn: []
18717                 };
18718                 
18719                 if(this.calendarWeeks){
18720                     // ISO 8601: First week contains first thursday.
18721                     // ISO also states week starts on Monday, but we can be more abstract here.
18722                     var
18723                     // Start of current week: based on weekstart/current date
18724                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18725                     // Thursday of this week
18726                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18727                     // First Thursday of year, year from thursday
18728                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18729                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18730                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18731                     
18732                     fillMonths.cn.push({
18733                         tag: 'td',
18734                         cls: 'cw',
18735                         html: calWeek
18736                     });
18737                 }
18738             }
18739             
18740             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18741                 clsName += ' old';
18742             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18743                 clsName += ' new';
18744             }
18745             if (this.todayHighlight &&
18746                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18747                 prevMonth.getUTCMonth() == today.getMonth() &&
18748                 prevMonth.getUTCDate() == today.getDate()) {
18749                 clsName += ' today';
18750             }
18751             
18752             if (currentDate && prevMonth.valueOf() === currentDate) {
18753                 clsName += ' active';
18754             }
18755             
18756             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18757                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18758                     clsName += ' disabled';
18759             }
18760             
18761             fillMonths.cn.push({
18762                 tag: 'td',
18763                 cls: 'day ' + clsName,
18764                 html: prevMonth.getDate()
18765             });
18766             
18767             prevMonth.setDate(prevMonth.getDate()+1);
18768         }
18769           
18770         var currentYear = this.date && this.date.getUTCFullYear();
18771         var currentMonth = this.date && this.date.getUTCMonth();
18772         
18773         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18774         
18775         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18776             v.removeClass('active');
18777             
18778             if(currentYear === year && k === currentMonth){
18779                 v.addClass('active');
18780             }
18781             
18782             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18783                 v.addClass('disabled');
18784             }
18785             
18786         });
18787         
18788         
18789         year = parseInt(year/10, 10) * 10;
18790         
18791         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18792         
18793         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18794         
18795         year -= 1;
18796         for (var i = -1; i < 11; i++) {
18797             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18798                 tag: 'span',
18799                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18800                 html: year
18801             });
18802             
18803             year += 1;
18804         }
18805     },
18806     
18807     showMode: function(dir) 
18808     {
18809         if (dir) {
18810             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18811         }
18812         
18813         Roo.each(this.picker().select('>div',true).elements, function(v){
18814             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18815             v.hide();
18816         });
18817         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18818     },
18819     
18820     place: function()
18821     {
18822         if(this.isInline) {
18823             return;
18824         }
18825         
18826         this.picker().removeClass(['bottom', 'top']);
18827         
18828         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18829             /*
18830              * place to the top of element!
18831              *
18832              */
18833             
18834             this.picker().addClass('top');
18835             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18836             
18837             return;
18838         }
18839         
18840         this.picker().addClass('bottom');
18841         
18842         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18843     },
18844     
18845     parseDate : function(value)
18846     {
18847         if(!value || value instanceof Date){
18848             return value;
18849         }
18850         var v = Date.parseDate(value, this.format);
18851         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18852             v = Date.parseDate(value, 'Y-m-d');
18853         }
18854         if(!v && this.altFormats){
18855             if(!this.altFormatsArray){
18856                 this.altFormatsArray = this.altFormats.split("|");
18857             }
18858             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18859                 v = Date.parseDate(value, this.altFormatsArray[i]);
18860             }
18861         }
18862         return v;
18863     },
18864     
18865     formatDate : function(date, fmt)
18866     {   
18867         return (!date || !(date instanceof Date)) ?
18868         date : date.dateFormat(fmt || this.format);
18869     },
18870     
18871     onFocus : function()
18872     {
18873         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18874         this.showPopup();
18875     },
18876     
18877     onBlur : function()
18878     {
18879         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18880         
18881         var d = this.inputEl().getValue();
18882         
18883         this.setValue(d);
18884                 
18885         this.hidePopup();
18886     },
18887     
18888     showPopup : function()
18889     {
18890         this.picker().show();
18891         this.update();
18892         this.place();
18893         
18894         this.fireEvent('showpopup', this, this.date);
18895     },
18896     
18897     hidePopup : function()
18898     {
18899         if(this.isInline) {
18900             return;
18901         }
18902         this.picker().hide();
18903         this.viewMode = this.startViewMode;
18904         this.showMode();
18905         
18906         this.fireEvent('hidepopup', this, this.date);
18907         
18908     },
18909     
18910     onMousedown: function(e)
18911     {
18912         e.stopPropagation();
18913         e.preventDefault();
18914     },
18915     
18916     keyup: function(e)
18917     {
18918         Roo.bootstrap.DateField.superclass.keyup.call(this);
18919         this.update();
18920     },
18921
18922     setValue: function(v)
18923     {
18924         if(this.fireEvent('beforeselect', this, v) !== false){
18925             var d = new Date(this.parseDate(v) ).clearTime();
18926         
18927             if(isNaN(d.getTime())){
18928                 this.date = this.viewDate = '';
18929                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18930                 return;
18931             }
18932
18933             v = this.formatDate(d);
18934
18935             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18936
18937             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18938
18939             this.update();
18940
18941             this.fireEvent('select', this, this.date);
18942         }
18943     },
18944     
18945     getValue: function()
18946     {
18947         return this.formatDate(this.date);
18948     },
18949     
18950     fireKey: function(e)
18951     {
18952         if (!this.picker().isVisible()){
18953             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18954                 this.showPopup();
18955             }
18956             return;
18957         }
18958         
18959         var dateChanged = false,
18960         dir, day, month,
18961         newDate, newViewDate;
18962         
18963         switch(e.keyCode){
18964             case 27: // escape
18965                 this.hidePopup();
18966                 e.preventDefault();
18967                 break;
18968             case 37: // left
18969             case 39: // right
18970                 if (!this.keyboardNavigation) {
18971                     break;
18972                 }
18973                 dir = e.keyCode == 37 ? -1 : 1;
18974                 
18975                 if (e.ctrlKey){
18976                     newDate = this.moveYear(this.date, dir);
18977                     newViewDate = this.moveYear(this.viewDate, dir);
18978                 } else if (e.shiftKey){
18979                     newDate = this.moveMonth(this.date, dir);
18980                     newViewDate = this.moveMonth(this.viewDate, dir);
18981                 } else {
18982                     newDate = new Date(this.date);
18983                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18984                     newViewDate = new Date(this.viewDate);
18985                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18986                 }
18987                 if (this.dateWithinRange(newDate)){
18988                     this.date = newDate;
18989                     this.viewDate = newViewDate;
18990                     this.setValue(this.formatDate(this.date));
18991 //                    this.update();
18992                     e.preventDefault();
18993                     dateChanged = true;
18994                 }
18995                 break;
18996             case 38: // up
18997             case 40: // down
18998                 if (!this.keyboardNavigation) {
18999                     break;
19000                 }
19001                 dir = e.keyCode == 38 ? -1 : 1;
19002                 if (e.ctrlKey){
19003                     newDate = this.moveYear(this.date, dir);
19004                     newViewDate = this.moveYear(this.viewDate, dir);
19005                 } else if (e.shiftKey){
19006                     newDate = this.moveMonth(this.date, dir);
19007                     newViewDate = this.moveMonth(this.viewDate, dir);
19008                 } else {
19009                     newDate = new Date(this.date);
19010                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19011                     newViewDate = new Date(this.viewDate);
19012                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19013                 }
19014                 if (this.dateWithinRange(newDate)){
19015                     this.date = newDate;
19016                     this.viewDate = newViewDate;
19017                     this.setValue(this.formatDate(this.date));
19018 //                    this.update();
19019                     e.preventDefault();
19020                     dateChanged = true;
19021                 }
19022                 break;
19023             case 13: // enter
19024                 this.setValue(this.formatDate(this.date));
19025                 this.hidePopup();
19026                 e.preventDefault();
19027                 break;
19028             case 9: // tab
19029                 this.setValue(this.formatDate(this.date));
19030                 this.hidePopup();
19031                 break;
19032             case 16: // shift
19033             case 17: // ctrl
19034             case 18: // alt
19035                 break;
19036             default :
19037                 this.hide();
19038                 
19039         }
19040     },
19041     
19042     
19043     onClick: function(e) 
19044     {
19045         e.stopPropagation();
19046         e.preventDefault();
19047         
19048         var target = e.getTarget();
19049         
19050         if(target.nodeName.toLowerCase() === 'i'){
19051             target = Roo.get(target).dom.parentNode;
19052         }
19053         
19054         var nodeName = target.nodeName;
19055         var className = target.className;
19056         var html = target.innerHTML;
19057         //Roo.log(nodeName);
19058         
19059         switch(nodeName.toLowerCase()) {
19060             case 'th':
19061                 switch(className) {
19062                     case 'switch':
19063                         this.showMode(1);
19064                         break;
19065                     case 'prev':
19066                     case 'next':
19067                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19068                         switch(this.viewMode){
19069                                 case 0:
19070                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19071                                         break;
19072                                 case 1:
19073                                 case 2:
19074                                         this.viewDate = this.moveYear(this.viewDate, dir);
19075                                         break;
19076                         }
19077                         this.fill();
19078                         break;
19079                     case 'today':
19080                         var date = new Date();
19081                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19082 //                        this.fill()
19083                         this.setValue(this.formatDate(this.date));
19084                         
19085                         this.hidePopup();
19086                         break;
19087                 }
19088                 break;
19089             case 'span':
19090                 if (className.indexOf('disabled') < 0) {
19091                     this.viewDate.setUTCDate(1);
19092                     if (className.indexOf('month') > -1) {
19093                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19094                     } else {
19095                         var year = parseInt(html, 10) || 0;
19096                         this.viewDate.setUTCFullYear(year);
19097                         
19098                     }
19099                     
19100                     if(this.singleMode){
19101                         this.setValue(this.formatDate(this.viewDate));
19102                         this.hidePopup();
19103                         return;
19104                     }
19105                     
19106                     this.showMode(-1);
19107                     this.fill();
19108                 }
19109                 break;
19110                 
19111             case 'td':
19112                 //Roo.log(className);
19113                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19114                     var day = parseInt(html, 10) || 1;
19115                     var year = this.viewDate.getUTCFullYear(),
19116                         month = this.viewDate.getUTCMonth();
19117
19118                     if (className.indexOf('old') > -1) {
19119                         if(month === 0 ){
19120                             month = 11;
19121                             year -= 1;
19122                         }else{
19123                             month -= 1;
19124                         }
19125                     } else if (className.indexOf('new') > -1) {
19126                         if (month == 11) {
19127                             month = 0;
19128                             year += 1;
19129                         } else {
19130                             month += 1;
19131                         }
19132                     }
19133                     //Roo.log([year,month,day]);
19134                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19135                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19136 //                    this.fill();
19137                     //Roo.log(this.formatDate(this.date));
19138                     this.setValue(this.formatDate(this.date));
19139                     this.hidePopup();
19140                 }
19141                 break;
19142         }
19143     },
19144     
19145     setStartDate: function(startDate)
19146     {
19147         this.startDate = startDate || -Infinity;
19148         if (this.startDate !== -Infinity) {
19149             this.startDate = this.parseDate(this.startDate);
19150         }
19151         this.update();
19152         this.updateNavArrows();
19153     },
19154
19155     setEndDate: function(endDate)
19156     {
19157         this.endDate = endDate || Infinity;
19158         if (this.endDate !== Infinity) {
19159             this.endDate = this.parseDate(this.endDate);
19160         }
19161         this.update();
19162         this.updateNavArrows();
19163     },
19164     
19165     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19166     {
19167         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19168         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19169             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19170         }
19171         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19172             return parseInt(d, 10);
19173         });
19174         this.update();
19175         this.updateNavArrows();
19176     },
19177     
19178     updateNavArrows: function() 
19179     {
19180         if(this.singleMode){
19181             return;
19182         }
19183         
19184         var d = new Date(this.viewDate),
19185         year = d.getUTCFullYear(),
19186         month = d.getUTCMonth();
19187         
19188         Roo.each(this.picker().select('.prev', true).elements, function(v){
19189             v.show();
19190             switch (this.viewMode) {
19191                 case 0:
19192
19193                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19194                         v.hide();
19195                     }
19196                     break;
19197                 case 1:
19198                 case 2:
19199                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19200                         v.hide();
19201                     }
19202                     break;
19203             }
19204         });
19205         
19206         Roo.each(this.picker().select('.next', true).elements, function(v){
19207             v.show();
19208             switch (this.viewMode) {
19209                 case 0:
19210
19211                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19212                         v.hide();
19213                     }
19214                     break;
19215                 case 1:
19216                 case 2:
19217                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19218                         v.hide();
19219                     }
19220                     break;
19221             }
19222         })
19223     },
19224     
19225     moveMonth: function(date, dir)
19226     {
19227         if (!dir) {
19228             return date;
19229         }
19230         var new_date = new Date(date.valueOf()),
19231         day = new_date.getUTCDate(),
19232         month = new_date.getUTCMonth(),
19233         mag = Math.abs(dir),
19234         new_month, test;
19235         dir = dir > 0 ? 1 : -1;
19236         if (mag == 1){
19237             test = dir == -1
19238             // If going back one month, make sure month is not current month
19239             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19240             ? function(){
19241                 return new_date.getUTCMonth() == month;
19242             }
19243             // If going forward one month, make sure month is as expected
19244             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19245             : function(){
19246                 return new_date.getUTCMonth() != new_month;
19247             };
19248             new_month = month + dir;
19249             new_date.setUTCMonth(new_month);
19250             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19251             if (new_month < 0 || new_month > 11) {
19252                 new_month = (new_month + 12) % 12;
19253             }
19254         } else {
19255             // For magnitudes >1, move one month at a time...
19256             for (var i=0; i<mag; i++) {
19257                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19258                 new_date = this.moveMonth(new_date, dir);
19259             }
19260             // ...then reset the day, keeping it in the new month
19261             new_month = new_date.getUTCMonth();
19262             new_date.setUTCDate(day);
19263             test = function(){
19264                 return new_month != new_date.getUTCMonth();
19265             };
19266         }
19267         // Common date-resetting loop -- if date is beyond end of month, make it
19268         // end of month
19269         while (test()){
19270             new_date.setUTCDate(--day);
19271             new_date.setUTCMonth(new_month);
19272         }
19273         return new_date;
19274     },
19275
19276     moveYear: function(date, dir)
19277     {
19278         return this.moveMonth(date, dir*12);
19279     },
19280
19281     dateWithinRange: function(date)
19282     {
19283         return date >= this.startDate && date <= this.endDate;
19284     },
19285
19286     
19287     remove: function() 
19288     {
19289         this.picker().remove();
19290     },
19291     
19292     validateValue : function(value)
19293     {
19294         if(this.getVisibilityEl().hasClass('hidden')){
19295             return true;
19296         }
19297         
19298         if(value.length < 1)  {
19299             if(this.allowBlank){
19300                 return true;
19301             }
19302             return false;
19303         }
19304         
19305         if(value.length < this.minLength){
19306             return false;
19307         }
19308         if(value.length > this.maxLength){
19309             return false;
19310         }
19311         if(this.vtype){
19312             var vt = Roo.form.VTypes;
19313             if(!vt[this.vtype](value, this)){
19314                 return false;
19315             }
19316         }
19317         if(typeof this.validator == "function"){
19318             var msg = this.validator(value);
19319             if(msg !== true){
19320                 return false;
19321             }
19322         }
19323         
19324         if(this.regex && !this.regex.test(value)){
19325             return false;
19326         }
19327         
19328         if(typeof(this.parseDate(value)) == 'undefined'){
19329             return false;
19330         }
19331         
19332         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19333             return false;
19334         }      
19335         
19336         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19337             return false;
19338         } 
19339         
19340         
19341         return true;
19342     },
19343     
19344     reset : function()
19345     {
19346         this.date = this.viewDate = '';
19347         
19348         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19349     }
19350    
19351 });
19352
19353 Roo.apply(Roo.bootstrap.DateField,  {
19354     
19355     head : {
19356         tag: 'thead',
19357         cn: [
19358         {
19359             tag: 'tr',
19360             cn: [
19361             {
19362                 tag: 'th',
19363                 cls: 'prev',
19364                 html: '<i class="fa fa-arrow-left"/>'
19365             },
19366             {
19367                 tag: 'th',
19368                 cls: 'switch',
19369                 colspan: '5'
19370             },
19371             {
19372                 tag: 'th',
19373                 cls: 'next',
19374                 html: '<i class="fa fa-arrow-right"/>'
19375             }
19376
19377             ]
19378         }
19379         ]
19380     },
19381     
19382     content : {
19383         tag: 'tbody',
19384         cn: [
19385         {
19386             tag: 'tr',
19387             cn: [
19388             {
19389                 tag: 'td',
19390                 colspan: '7'
19391             }
19392             ]
19393         }
19394         ]
19395     },
19396     
19397     footer : {
19398         tag: 'tfoot',
19399         cn: [
19400         {
19401             tag: 'tr',
19402             cn: [
19403             {
19404                 tag: 'th',
19405                 colspan: '7',
19406                 cls: 'today'
19407             }
19408                     
19409             ]
19410         }
19411         ]
19412     },
19413     
19414     dates:{
19415         en: {
19416             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19417             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19418             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19419             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19420             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19421             today: "Today"
19422         }
19423     },
19424     
19425     modes: [
19426     {
19427         clsName: 'days',
19428         navFnc: 'Month',
19429         navStep: 1
19430     },
19431     {
19432         clsName: 'months',
19433         navFnc: 'FullYear',
19434         navStep: 1
19435     },
19436     {
19437         clsName: 'years',
19438         navFnc: 'FullYear',
19439         navStep: 10
19440     }]
19441 });
19442
19443 Roo.apply(Roo.bootstrap.DateField,  {
19444   
19445     template : {
19446         tag: 'div',
19447         cls: 'datepicker dropdown-menu roo-dynamic',
19448         cn: [
19449         {
19450             tag: 'div',
19451             cls: 'datepicker-days',
19452             cn: [
19453             {
19454                 tag: 'table',
19455                 cls: 'table-condensed',
19456                 cn:[
19457                 Roo.bootstrap.DateField.head,
19458                 {
19459                     tag: 'tbody'
19460                 },
19461                 Roo.bootstrap.DateField.footer
19462                 ]
19463             }
19464             ]
19465         },
19466         {
19467             tag: 'div',
19468             cls: 'datepicker-months',
19469             cn: [
19470             {
19471                 tag: 'table',
19472                 cls: 'table-condensed',
19473                 cn:[
19474                 Roo.bootstrap.DateField.head,
19475                 Roo.bootstrap.DateField.content,
19476                 Roo.bootstrap.DateField.footer
19477                 ]
19478             }
19479             ]
19480         },
19481         {
19482             tag: 'div',
19483             cls: 'datepicker-years',
19484             cn: [
19485             {
19486                 tag: 'table',
19487                 cls: 'table-condensed',
19488                 cn:[
19489                 Roo.bootstrap.DateField.head,
19490                 Roo.bootstrap.DateField.content,
19491                 Roo.bootstrap.DateField.footer
19492                 ]
19493             }
19494             ]
19495         }
19496         ]
19497     }
19498 });
19499
19500  
19501
19502  /*
19503  * - LGPL
19504  *
19505  * TimeField
19506  * 
19507  */
19508
19509 /**
19510  * @class Roo.bootstrap.TimeField
19511  * @extends Roo.bootstrap.Input
19512  * Bootstrap DateField class
19513  * 
19514  * 
19515  * @constructor
19516  * Create a new TimeField
19517  * @param {Object} config The config object
19518  */
19519
19520 Roo.bootstrap.TimeField = function(config){
19521     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19522     this.addEvents({
19523             /**
19524              * @event show
19525              * Fires when this field show.
19526              * @param {Roo.bootstrap.DateField} thisthis
19527              * @param {Mixed} date The date value
19528              */
19529             show : true,
19530             /**
19531              * @event show
19532              * Fires when this field hide.
19533              * @param {Roo.bootstrap.DateField} this
19534              * @param {Mixed} date The date value
19535              */
19536             hide : true,
19537             /**
19538              * @event select
19539              * Fires when select a date.
19540              * @param {Roo.bootstrap.DateField} this
19541              * @param {Mixed} date The date value
19542              */
19543             select : true
19544         });
19545 };
19546
19547 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19548     
19549     /**
19550      * @cfg {String} format
19551      * The default time format string which can be overriden for localization support.  The format must be
19552      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19553      */
19554     format : "H:i",
19555        
19556     onRender: function(ct, position)
19557     {
19558         
19559         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19560                 
19561         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19562         
19563         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19564         
19565         this.pop = this.picker().select('>.datepicker-time',true).first();
19566         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19567         
19568         this.picker().on('mousedown', this.onMousedown, this);
19569         this.picker().on('click', this.onClick, this);
19570         
19571         this.picker().addClass('datepicker-dropdown');
19572     
19573         this.fillTime();
19574         this.update();
19575             
19576         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19577         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19578         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19579         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19580         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19581         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19582
19583     },
19584     
19585     fireKey: function(e){
19586         if (!this.picker().isVisible()){
19587             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19588                 this.show();
19589             }
19590             return;
19591         }
19592
19593         e.preventDefault();
19594         
19595         switch(e.keyCode){
19596             case 27: // escape
19597                 this.hide();
19598                 break;
19599             case 37: // left
19600             case 39: // right
19601                 this.onTogglePeriod();
19602                 break;
19603             case 38: // up
19604                 this.onIncrementMinutes();
19605                 break;
19606             case 40: // down
19607                 this.onDecrementMinutes();
19608                 break;
19609             case 13: // enter
19610             case 9: // tab
19611                 this.setTime();
19612                 break;
19613         }
19614     },
19615     
19616     onClick: function(e) {
19617         e.stopPropagation();
19618         e.preventDefault();
19619     },
19620     
19621     picker : function()
19622     {
19623         return this.el.select('.datepicker', true).first();
19624     },
19625     
19626     fillTime: function()
19627     {    
19628         var time = this.pop.select('tbody', true).first();
19629         
19630         time.dom.innerHTML = '';
19631         
19632         time.createChild({
19633             tag: 'tr',
19634             cn: [
19635                 {
19636                     tag: 'td',
19637                     cn: [
19638                         {
19639                             tag: 'a',
19640                             href: '#',
19641                             cls: 'btn',
19642                             cn: [
19643                                 {
19644                                     tag: 'span',
19645                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19646                                 }
19647                             ]
19648                         } 
19649                     ]
19650                 },
19651                 {
19652                     tag: 'td',
19653                     cls: 'separator'
19654                 },
19655                 {
19656                     tag: 'td',
19657                     cn: [
19658                         {
19659                             tag: 'a',
19660                             href: '#',
19661                             cls: 'btn',
19662                             cn: [
19663                                 {
19664                                     tag: 'span',
19665                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19666                                 }
19667                             ]
19668                         }
19669                     ]
19670                 },
19671                 {
19672                     tag: 'td',
19673                     cls: 'separator'
19674                 }
19675             ]
19676         });
19677         
19678         time.createChild({
19679             tag: 'tr',
19680             cn: [
19681                 {
19682                     tag: 'td',
19683                     cn: [
19684                         {
19685                             tag: 'span',
19686                             cls: 'timepicker-hour',
19687                             html: '00'
19688                         }  
19689                     ]
19690                 },
19691                 {
19692                     tag: 'td',
19693                     cls: 'separator',
19694                     html: ':'
19695                 },
19696                 {
19697                     tag: 'td',
19698                     cn: [
19699                         {
19700                             tag: 'span',
19701                             cls: 'timepicker-minute',
19702                             html: '00'
19703                         }  
19704                     ]
19705                 },
19706                 {
19707                     tag: 'td',
19708                     cls: 'separator'
19709                 },
19710                 {
19711                     tag: 'td',
19712                     cn: [
19713                         {
19714                             tag: 'button',
19715                             type: 'button',
19716                             cls: 'btn btn-primary period',
19717                             html: 'AM'
19718                             
19719                         }
19720                     ]
19721                 }
19722             ]
19723         });
19724         
19725         time.createChild({
19726             tag: 'tr',
19727             cn: [
19728                 {
19729                     tag: 'td',
19730                     cn: [
19731                         {
19732                             tag: 'a',
19733                             href: '#',
19734                             cls: 'btn',
19735                             cn: [
19736                                 {
19737                                     tag: 'span',
19738                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19739                                 }
19740                             ]
19741                         }
19742                     ]
19743                 },
19744                 {
19745                     tag: 'td',
19746                     cls: 'separator'
19747                 },
19748                 {
19749                     tag: 'td',
19750                     cn: [
19751                         {
19752                             tag: 'a',
19753                             href: '#',
19754                             cls: 'btn',
19755                             cn: [
19756                                 {
19757                                     tag: 'span',
19758                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19759                                 }
19760                             ]
19761                         }
19762                     ]
19763                 },
19764                 {
19765                     tag: 'td',
19766                     cls: 'separator'
19767                 }
19768             ]
19769         });
19770         
19771     },
19772     
19773     update: function()
19774     {
19775         
19776         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19777         
19778         this.fill();
19779     },
19780     
19781     fill: function() 
19782     {
19783         var hours = this.time.getHours();
19784         var minutes = this.time.getMinutes();
19785         var period = 'AM';
19786         
19787         if(hours > 11){
19788             period = 'PM';
19789         }
19790         
19791         if(hours == 0){
19792             hours = 12;
19793         }
19794         
19795         
19796         if(hours > 12){
19797             hours = hours - 12;
19798         }
19799         
19800         if(hours < 10){
19801             hours = '0' + hours;
19802         }
19803         
19804         if(minutes < 10){
19805             minutes = '0' + minutes;
19806         }
19807         
19808         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19809         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19810         this.pop.select('button', true).first().dom.innerHTML = period;
19811         
19812     },
19813     
19814     place: function()
19815     {   
19816         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19817         
19818         var cls = ['bottom'];
19819         
19820         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19821             cls.pop();
19822             cls.push('top');
19823         }
19824         
19825         cls.push('right');
19826         
19827         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19828             cls.pop();
19829             cls.push('left');
19830         }
19831         
19832         this.picker().addClass(cls.join('-'));
19833         
19834         var _this = this;
19835         
19836         Roo.each(cls, function(c){
19837             if(c == 'bottom'){
19838                 _this.picker().setTop(_this.inputEl().getHeight());
19839                 return;
19840             }
19841             if(c == 'top'){
19842                 _this.picker().setTop(0 - _this.picker().getHeight());
19843                 return;
19844             }
19845             
19846             if(c == 'left'){
19847                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19848                 return;
19849             }
19850             if(c == 'right'){
19851                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19852                 return;
19853             }
19854         });
19855         
19856     },
19857   
19858     onFocus : function()
19859     {
19860         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19861         this.show();
19862     },
19863     
19864     onBlur : function()
19865     {
19866         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19867         this.hide();
19868     },
19869     
19870     show : function()
19871     {
19872         this.picker().show();
19873         this.pop.show();
19874         this.update();
19875         this.place();
19876         
19877         this.fireEvent('show', this, this.date);
19878     },
19879     
19880     hide : function()
19881     {
19882         this.picker().hide();
19883         this.pop.hide();
19884         
19885         this.fireEvent('hide', this, this.date);
19886     },
19887     
19888     setTime : function()
19889     {
19890         this.hide();
19891         this.setValue(this.time.format(this.format));
19892         
19893         this.fireEvent('select', this, this.date);
19894         
19895         
19896     },
19897     
19898     onMousedown: function(e){
19899         e.stopPropagation();
19900         e.preventDefault();
19901     },
19902     
19903     onIncrementHours: function()
19904     {
19905         Roo.log('onIncrementHours');
19906         this.time = this.time.add(Date.HOUR, 1);
19907         this.update();
19908         
19909     },
19910     
19911     onDecrementHours: function()
19912     {
19913         Roo.log('onDecrementHours');
19914         this.time = this.time.add(Date.HOUR, -1);
19915         this.update();
19916     },
19917     
19918     onIncrementMinutes: function()
19919     {
19920         Roo.log('onIncrementMinutes');
19921         this.time = this.time.add(Date.MINUTE, 1);
19922         this.update();
19923     },
19924     
19925     onDecrementMinutes: function()
19926     {
19927         Roo.log('onDecrementMinutes');
19928         this.time = this.time.add(Date.MINUTE, -1);
19929         this.update();
19930     },
19931     
19932     onTogglePeriod: function()
19933     {
19934         Roo.log('onTogglePeriod');
19935         this.time = this.time.add(Date.HOUR, 12);
19936         this.update();
19937     }
19938     
19939    
19940 });
19941
19942 Roo.apply(Roo.bootstrap.TimeField,  {
19943     
19944     content : {
19945         tag: 'tbody',
19946         cn: [
19947             {
19948                 tag: 'tr',
19949                 cn: [
19950                 {
19951                     tag: 'td',
19952                     colspan: '7'
19953                 }
19954                 ]
19955             }
19956         ]
19957     },
19958     
19959     footer : {
19960         tag: 'tfoot',
19961         cn: [
19962             {
19963                 tag: 'tr',
19964                 cn: [
19965                 {
19966                     tag: 'th',
19967                     colspan: '7',
19968                     cls: '',
19969                     cn: [
19970                         {
19971                             tag: 'button',
19972                             cls: 'btn btn-info ok',
19973                             html: 'OK'
19974                         }
19975                     ]
19976                 }
19977
19978                 ]
19979             }
19980         ]
19981     }
19982 });
19983
19984 Roo.apply(Roo.bootstrap.TimeField,  {
19985   
19986     template : {
19987         tag: 'div',
19988         cls: 'datepicker dropdown-menu',
19989         cn: [
19990             {
19991                 tag: 'div',
19992                 cls: 'datepicker-time',
19993                 cn: [
19994                 {
19995                     tag: 'table',
19996                     cls: 'table-condensed',
19997                     cn:[
19998                     Roo.bootstrap.TimeField.content,
19999                     Roo.bootstrap.TimeField.footer
20000                     ]
20001                 }
20002                 ]
20003             }
20004         ]
20005     }
20006 });
20007
20008  
20009
20010  /*
20011  * - LGPL
20012  *
20013  * MonthField
20014  * 
20015  */
20016
20017 /**
20018  * @class Roo.bootstrap.MonthField
20019  * @extends Roo.bootstrap.Input
20020  * Bootstrap MonthField class
20021  * 
20022  * @cfg {String} language default en
20023  * 
20024  * @constructor
20025  * Create a new MonthField
20026  * @param {Object} config The config object
20027  */
20028
20029 Roo.bootstrap.MonthField = function(config){
20030     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20031     
20032     this.addEvents({
20033         /**
20034          * @event show
20035          * Fires when this field show.
20036          * @param {Roo.bootstrap.MonthField} this
20037          * @param {Mixed} date The date value
20038          */
20039         show : true,
20040         /**
20041          * @event show
20042          * Fires when this field hide.
20043          * @param {Roo.bootstrap.MonthField} this
20044          * @param {Mixed} date The date value
20045          */
20046         hide : true,
20047         /**
20048          * @event select
20049          * Fires when select a date.
20050          * @param {Roo.bootstrap.MonthField} this
20051          * @param {String} oldvalue The old value
20052          * @param {String} newvalue The new value
20053          */
20054         select : true
20055     });
20056 };
20057
20058 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20059     
20060     onRender: function(ct, position)
20061     {
20062         
20063         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20064         
20065         this.language = this.language || 'en';
20066         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20067         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20068         
20069         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20070         this.isInline = false;
20071         this.isInput = true;
20072         this.component = this.el.select('.add-on', true).first() || false;
20073         this.component = (this.component && this.component.length === 0) ? false : this.component;
20074         this.hasInput = this.component && this.inputEL().length;
20075         
20076         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20077         
20078         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20079         
20080         this.picker().on('mousedown', this.onMousedown, this);
20081         this.picker().on('click', this.onClick, this);
20082         
20083         this.picker().addClass('datepicker-dropdown');
20084         
20085         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20086             v.setStyle('width', '189px');
20087         });
20088         
20089         this.fillMonths();
20090         
20091         this.update();
20092         
20093         if(this.isInline) {
20094             this.show();
20095         }
20096         
20097     },
20098     
20099     setValue: function(v, suppressEvent)
20100     {   
20101         var o = this.getValue();
20102         
20103         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20104         
20105         this.update();
20106
20107         if(suppressEvent !== true){
20108             this.fireEvent('select', this, o, v);
20109         }
20110         
20111     },
20112     
20113     getValue: function()
20114     {
20115         return this.value;
20116     },
20117     
20118     onClick: function(e) 
20119     {
20120         e.stopPropagation();
20121         e.preventDefault();
20122         
20123         var target = e.getTarget();
20124         
20125         if(target.nodeName.toLowerCase() === 'i'){
20126             target = Roo.get(target).dom.parentNode;
20127         }
20128         
20129         var nodeName = target.nodeName;
20130         var className = target.className;
20131         var html = target.innerHTML;
20132         
20133         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20134             return;
20135         }
20136         
20137         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20138         
20139         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20140         
20141         this.hide();
20142                         
20143     },
20144     
20145     picker : function()
20146     {
20147         return this.pickerEl;
20148     },
20149     
20150     fillMonths: function()
20151     {    
20152         var i = 0;
20153         var months = this.picker().select('>.datepicker-months td', true).first();
20154         
20155         months.dom.innerHTML = '';
20156         
20157         while (i < 12) {
20158             var month = {
20159                 tag: 'span',
20160                 cls: 'month',
20161                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20162             };
20163             
20164             months.createChild(month);
20165         }
20166         
20167     },
20168     
20169     update: function()
20170     {
20171         var _this = this;
20172         
20173         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20174             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20175         }
20176         
20177         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20178             e.removeClass('active');
20179             
20180             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20181                 e.addClass('active');
20182             }
20183         })
20184     },
20185     
20186     place: function()
20187     {
20188         if(this.isInline) {
20189             return;
20190         }
20191         
20192         this.picker().removeClass(['bottom', 'top']);
20193         
20194         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20195             /*
20196              * place to the top of element!
20197              *
20198              */
20199             
20200             this.picker().addClass('top');
20201             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20202             
20203             return;
20204         }
20205         
20206         this.picker().addClass('bottom');
20207         
20208         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20209     },
20210     
20211     onFocus : function()
20212     {
20213         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20214         this.show();
20215     },
20216     
20217     onBlur : function()
20218     {
20219         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20220         
20221         var d = this.inputEl().getValue();
20222         
20223         this.setValue(d);
20224                 
20225         this.hide();
20226     },
20227     
20228     show : function()
20229     {
20230         this.picker().show();
20231         this.picker().select('>.datepicker-months', true).first().show();
20232         this.update();
20233         this.place();
20234         
20235         this.fireEvent('show', this, this.date);
20236     },
20237     
20238     hide : function()
20239     {
20240         if(this.isInline) {
20241             return;
20242         }
20243         this.picker().hide();
20244         this.fireEvent('hide', this, this.date);
20245         
20246     },
20247     
20248     onMousedown: function(e)
20249     {
20250         e.stopPropagation();
20251         e.preventDefault();
20252     },
20253     
20254     keyup: function(e)
20255     {
20256         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20257         this.update();
20258     },
20259
20260     fireKey: function(e)
20261     {
20262         if (!this.picker().isVisible()){
20263             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20264                 this.show();
20265             }
20266             return;
20267         }
20268         
20269         var dir;
20270         
20271         switch(e.keyCode){
20272             case 27: // escape
20273                 this.hide();
20274                 e.preventDefault();
20275                 break;
20276             case 37: // left
20277             case 39: // right
20278                 dir = e.keyCode == 37 ? -1 : 1;
20279                 
20280                 this.vIndex = this.vIndex + dir;
20281                 
20282                 if(this.vIndex < 0){
20283                     this.vIndex = 0;
20284                 }
20285                 
20286                 if(this.vIndex > 11){
20287                     this.vIndex = 11;
20288                 }
20289                 
20290                 if(isNaN(this.vIndex)){
20291                     this.vIndex = 0;
20292                 }
20293                 
20294                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20295                 
20296                 break;
20297             case 38: // up
20298             case 40: // down
20299                 
20300                 dir = e.keyCode == 38 ? -1 : 1;
20301                 
20302                 this.vIndex = this.vIndex + dir * 4;
20303                 
20304                 if(this.vIndex < 0){
20305                     this.vIndex = 0;
20306                 }
20307                 
20308                 if(this.vIndex > 11){
20309                     this.vIndex = 11;
20310                 }
20311                 
20312                 if(isNaN(this.vIndex)){
20313                     this.vIndex = 0;
20314                 }
20315                 
20316                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20317                 break;
20318                 
20319             case 13: // enter
20320                 
20321                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20322                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20323                 }
20324                 
20325                 this.hide();
20326                 e.preventDefault();
20327                 break;
20328             case 9: // tab
20329                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20330                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20331                 }
20332                 this.hide();
20333                 break;
20334             case 16: // shift
20335             case 17: // ctrl
20336             case 18: // alt
20337                 break;
20338             default :
20339                 this.hide();
20340                 
20341         }
20342     },
20343     
20344     remove: function() 
20345     {
20346         this.picker().remove();
20347     }
20348    
20349 });
20350
20351 Roo.apply(Roo.bootstrap.MonthField,  {
20352     
20353     content : {
20354         tag: 'tbody',
20355         cn: [
20356         {
20357             tag: 'tr',
20358             cn: [
20359             {
20360                 tag: 'td',
20361                 colspan: '7'
20362             }
20363             ]
20364         }
20365         ]
20366     },
20367     
20368     dates:{
20369         en: {
20370             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20371             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20372         }
20373     }
20374 });
20375
20376 Roo.apply(Roo.bootstrap.MonthField,  {
20377   
20378     template : {
20379         tag: 'div',
20380         cls: 'datepicker dropdown-menu roo-dynamic',
20381         cn: [
20382             {
20383                 tag: 'div',
20384                 cls: 'datepicker-months',
20385                 cn: [
20386                 {
20387                     tag: 'table',
20388                     cls: 'table-condensed',
20389                     cn:[
20390                         Roo.bootstrap.DateField.content
20391                     ]
20392                 }
20393                 ]
20394             }
20395         ]
20396     }
20397 });
20398
20399  
20400
20401  
20402  /*
20403  * - LGPL
20404  *
20405  * CheckBox
20406  * 
20407  */
20408
20409 /**
20410  * @class Roo.bootstrap.CheckBox
20411  * @extends Roo.bootstrap.Input
20412  * Bootstrap CheckBox class
20413  * 
20414  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20415  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20416  * @cfg {String} boxLabel The text that appears beside the checkbox
20417  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20418  * @cfg {Boolean} checked initnal the element
20419  * @cfg {Boolean} inline inline the element (default false)
20420  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20421  * @cfg {String} tooltip label tooltip
20422  * 
20423  * @constructor
20424  * Create a new CheckBox
20425  * @param {Object} config The config object
20426  */
20427
20428 Roo.bootstrap.CheckBox = function(config){
20429     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20430    
20431     this.addEvents({
20432         /**
20433         * @event check
20434         * Fires when the element is checked or unchecked.
20435         * @param {Roo.bootstrap.CheckBox} this This input
20436         * @param {Boolean} checked The new checked value
20437         */
20438        check : true,
20439        /**
20440         * @event click
20441         * Fires when the element is click.
20442         * @param {Roo.bootstrap.CheckBox} this This input
20443         */
20444        click : true
20445     });
20446     
20447 };
20448
20449 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20450   
20451     inputType: 'checkbox',
20452     inputValue: 1,
20453     valueOff: 0,
20454     boxLabel: false,
20455     checked: false,
20456     weight : false,
20457     inline: false,
20458     tooltip : '',
20459     
20460     getAutoCreate : function()
20461     {
20462         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20463         
20464         var id = Roo.id();
20465         
20466         var cfg = {};
20467         
20468         cfg.cls = 'form-group ' + this.inputType; //input-group
20469         
20470         if(this.inline){
20471             cfg.cls += ' ' + this.inputType + '-inline';
20472         }
20473         
20474         var input =  {
20475             tag: 'input',
20476             id : id,
20477             type : this.inputType,
20478             value : this.inputValue,
20479             cls : 'roo-' + this.inputType, //'form-box',
20480             placeholder : this.placeholder || ''
20481             
20482         };
20483         
20484         if(this.inputType != 'radio'){
20485             var hidden =  {
20486                 tag: 'input',
20487                 type : 'hidden',
20488                 cls : 'roo-hidden-value',
20489                 value : this.checked ? this.inputValue : this.valueOff
20490             };
20491         }
20492         
20493             
20494         if (this.weight) { // Validity check?
20495             cfg.cls += " " + this.inputType + "-" + this.weight;
20496         }
20497         
20498         if (this.disabled) {
20499             input.disabled=true;
20500         }
20501         
20502         if(this.checked){
20503             input.checked = this.checked;
20504         }
20505         
20506         if (this.name) {
20507             
20508             input.name = this.name;
20509             
20510             if(this.inputType != 'radio'){
20511                 hidden.name = this.name;
20512                 input.name = '_hidden_' + this.name;
20513             }
20514         }
20515         
20516         if (this.size) {
20517             input.cls += ' input-' + this.size;
20518         }
20519         
20520         var settings=this;
20521         
20522         ['xs','sm','md','lg'].map(function(size){
20523             if (settings[size]) {
20524                 cfg.cls += ' col-' + size + '-' + settings[size];
20525             }
20526         });
20527         
20528         var inputblock = input;
20529          
20530         if (this.before || this.after) {
20531             
20532             inputblock = {
20533                 cls : 'input-group',
20534                 cn :  [] 
20535             };
20536             
20537             if (this.before) {
20538                 inputblock.cn.push({
20539                     tag :'span',
20540                     cls : 'input-group-addon',
20541                     html : this.before
20542                 });
20543             }
20544             
20545             inputblock.cn.push(input);
20546             
20547             if(this.inputType != 'radio'){
20548                 inputblock.cn.push(hidden);
20549             }
20550             
20551             if (this.after) {
20552                 inputblock.cn.push({
20553                     tag :'span',
20554                     cls : 'input-group-addon',
20555                     html : this.after
20556                 });
20557             }
20558             
20559         }
20560         
20561         if (align ==='left' && this.fieldLabel.length) {
20562 //                Roo.log("left and has label");
20563             cfg.cn = [
20564                 {
20565                     tag: 'label',
20566                     'for' :  id,
20567                     cls : 'control-label',
20568                     html : this.fieldLabel
20569                 },
20570                 {
20571                     cls : "", 
20572                     cn: [
20573                         inputblock
20574                     ]
20575                 }
20576             ];
20577             
20578             if(this.labelWidth > 12){
20579                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20580             }
20581             
20582             if(this.labelWidth < 13 && this.labelmd == 0){
20583                 this.labelmd = this.labelWidth;
20584             }
20585             
20586             if(this.labellg > 0){
20587                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20588                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20589             }
20590             
20591             if(this.labelmd > 0){
20592                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20593                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20594             }
20595             
20596             if(this.labelsm > 0){
20597                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20598                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20599             }
20600             
20601             if(this.labelxs > 0){
20602                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20603                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20604             }
20605             
20606         } else if ( this.fieldLabel.length) {
20607 //                Roo.log(" label");
20608                 cfg.cn = [
20609                    
20610                     {
20611                         tag: this.boxLabel ? 'span' : 'label',
20612                         'for': id,
20613                         cls: 'control-label box-input-label',
20614                         //cls : 'input-group-addon',
20615                         html : this.fieldLabel
20616                     },
20617                     
20618                     inputblock
20619                     
20620                 ];
20621
20622         } else {
20623             
20624 //                Roo.log(" no label && no align");
20625                 cfg.cn = [  inputblock ] ;
20626                 
20627                 
20628         }
20629         
20630         if(this.boxLabel){
20631              var boxLabelCfg = {
20632                 tag: 'label',
20633                 //'for': id, // box label is handled by onclick - so no for...
20634                 cls: 'box-label',
20635                 html: this.boxLabel
20636             };
20637             
20638             if(this.tooltip){
20639                 boxLabelCfg.tooltip = this.tooltip;
20640             }
20641              
20642             cfg.cn.push(boxLabelCfg);
20643         }
20644         
20645         if(this.inputType != 'radio'){
20646             cfg.cn.push(hidden);
20647         }
20648         
20649         return cfg;
20650         
20651     },
20652     
20653     /**
20654      * return the real input element.
20655      */
20656     inputEl: function ()
20657     {
20658         return this.el.select('input.roo-' + this.inputType,true).first();
20659     },
20660     hiddenEl: function ()
20661     {
20662         return this.el.select('input.roo-hidden-value',true).first();
20663     },
20664     
20665     labelEl: function()
20666     {
20667         return this.el.select('label.control-label',true).first();
20668     },
20669     /* depricated... */
20670     
20671     label: function()
20672     {
20673         return this.labelEl();
20674     },
20675     
20676     boxLabelEl: function()
20677     {
20678         return this.el.select('label.box-label',true).first();
20679     },
20680     
20681     initEvents : function()
20682     {
20683 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20684         
20685         this.inputEl().on('click', this.onClick,  this);
20686         
20687         if (this.boxLabel) { 
20688             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20689         }
20690         
20691         this.startValue = this.getValue();
20692         
20693         if(this.groupId){
20694             Roo.bootstrap.CheckBox.register(this);
20695         }
20696     },
20697     
20698     onClick : function(e)
20699     {   
20700         if(this.fireEvent('click', this, e) !== false){
20701             this.setChecked(!this.checked);
20702         }
20703         
20704     },
20705     
20706     setChecked : function(state,suppressEvent)
20707     {
20708         this.startValue = this.getValue();
20709
20710         if(this.inputType == 'radio'){
20711             
20712             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20713                 e.dom.checked = false;
20714             });
20715             
20716             this.inputEl().dom.checked = true;
20717             
20718             this.inputEl().dom.value = this.inputValue;
20719             
20720             if(suppressEvent !== true){
20721                 this.fireEvent('check', this, true);
20722             }
20723             
20724             this.validate();
20725             
20726             return;
20727         }
20728         
20729         this.checked = state;
20730         
20731         this.inputEl().dom.checked = state;
20732         
20733         
20734         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20735         
20736         if(suppressEvent !== true){
20737             this.fireEvent('check', this, state);
20738         }
20739         
20740         this.validate();
20741     },
20742     
20743     getValue : function()
20744     {
20745         if(this.inputType == 'radio'){
20746             return this.getGroupValue();
20747         }
20748         
20749         return this.hiddenEl().dom.value;
20750         
20751     },
20752     
20753     getGroupValue : function()
20754     {
20755         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20756             return '';
20757         }
20758         
20759         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20760     },
20761     
20762     setValue : function(v,suppressEvent)
20763     {
20764         if(this.inputType == 'radio'){
20765             this.setGroupValue(v, suppressEvent);
20766             return;
20767         }
20768         
20769         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20770         
20771         this.validate();
20772     },
20773     
20774     setGroupValue : function(v, suppressEvent)
20775     {
20776         this.startValue = this.getValue();
20777         
20778         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20779             e.dom.checked = false;
20780             
20781             if(e.dom.value == v){
20782                 e.dom.checked = true;
20783             }
20784         });
20785         
20786         if(suppressEvent !== true){
20787             this.fireEvent('check', this, true);
20788         }
20789
20790         this.validate();
20791         
20792         return;
20793     },
20794     
20795     validate : function()
20796     {
20797         if(this.getVisibilityEl().hasClass('hidden')){
20798             return true;
20799         }
20800         
20801         if(
20802                 this.disabled || 
20803                 (this.inputType == 'radio' && this.validateRadio()) ||
20804                 (this.inputType == 'checkbox' && this.validateCheckbox())
20805         ){
20806             this.markValid();
20807             return true;
20808         }
20809         
20810         this.markInvalid();
20811         return false;
20812     },
20813     
20814     validateRadio : function()
20815     {
20816         if(this.getVisibilityEl().hasClass('hidden')){
20817             return true;
20818         }
20819         
20820         if(this.allowBlank){
20821             return true;
20822         }
20823         
20824         var valid = false;
20825         
20826         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20827             if(!e.dom.checked){
20828                 return;
20829             }
20830             
20831             valid = true;
20832             
20833             return false;
20834         });
20835         
20836         return valid;
20837     },
20838     
20839     validateCheckbox : function()
20840     {
20841         if(!this.groupId){
20842             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20843             //return (this.getValue() == this.inputValue) ? true : false;
20844         }
20845         
20846         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20847         
20848         if(!group){
20849             return false;
20850         }
20851         
20852         var r = false;
20853         
20854         for(var i in group){
20855             if(group[i].el.isVisible(true)){
20856                 r = false;
20857                 break;
20858             }
20859             
20860             r = true;
20861         }
20862         
20863         for(var i in group){
20864             if(r){
20865                 break;
20866             }
20867             
20868             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20869         }
20870         
20871         return r;
20872     },
20873     
20874     /**
20875      * Mark this field as valid
20876      */
20877     markValid : function()
20878     {
20879         var _this = this;
20880         
20881         this.fireEvent('valid', this);
20882         
20883         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20884         
20885         if(this.groupId){
20886             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20887         }
20888         
20889         if(label){
20890             label.markValid();
20891         }
20892
20893         if(this.inputType == 'radio'){
20894             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20895                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20896                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20897             });
20898             
20899             return;
20900         }
20901
20902         if(!this.groupId){
20903             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20904             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20905             return;
20906         }
20907         
20908         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20909         
20910         if(!group){
20911             return;
20912         }
20913         
20914         for(var i in group){
20915             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20916             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20917         }
20918     },
20919     
20920      /**
20921      * Mark this field as invalid
20922      * @param {String} msg The validation message
20923      */
20924     markInvalid : function(msg)
20925     {
20926         if(this.allowBlank){
20927             return;
20928         }
20929         
20930         var _this = this;
20931         
20932         this.fireEvent('invalid', this, msg);
20933         
20934         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20935         
20936         if(this.groupId){
20937             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20938         }
20939         
20940         if(label){
20941             label.markInvalid();
20942         }
20943             
20944         if(this.inputType == 'radio'){
20945             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20946                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20947                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20948             });
20949             
20950             return;
20951         }
20952         
20953         if(!this.groupId){
20954             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20955             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20956             return;
20957         }
20958         
20959         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20960         
20961         if(!group){
20962             return;
20963         }
20964         
20965         for(var i in group){
20966             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20967             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20968         }
20969         
20970     },
20971     
20972     clearInvalid : function()
20973     {
20974         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20975         
20976         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20977         
20978         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20979         
20980         if (label && label.iconEl) {
20981             label.iconEl.removeClass(label.validClass);
20982             label.iconEl.removeClass(label.invalidClass);
20983         }
20984     },
20985     
20986     disable : function()
20987     {
20988         if(this.inputType != 'radio'){
20989             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20990             return;
20991         }
20992         
20993         var _this = this;
20994         
20995         if(this.rendered){
20996             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20997                 _this.getActionEl().addClass(this.disabledClass);
20998                 e.dom.disabled = true;
20999             });
21000         }
21001         
21002         this.disabled = true;
21003         this.fireEvent("disable", this);
21004         return this;
21005     },
21006
21007     enable : function()
21008     {
21009         if(this.inputType != 'radio'){
21010             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21011             return;
21012         }
21013         
21014         var _this = this;
21015         
21016         if(this.rendered){
21017             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21018                 _this.getActionEl().removeClass(this.disabledClass);
21019                 e.dom.disabled = false;
21020             });
21021         }
21022         
21023         this.disabled = false;
21024         this.fireEvent("enable", this);
21025         return this;
21026     },
21027     
21028     setBoxLabel : function(v)
21029     {
21030         this.boxLabel = v;
21031         
21032         if(this.rendered){
21033             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21034         }
21035     }
21036
21037 });
21038
21039 Roo.apply(Roo.bootstrap.CheckBox, {
21040     
21041     groups: {},
21042     
21043      /**
21044     * register a CheckBox Group
21045     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21046     */
21047     register : function(checkbox)
21048     {
21049         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21050             this.groups[checkbox.groupId] = {};
21051         }
21052         
21053         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21054             return;
21055         }
21056         
21057         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21058         
21059     },
21060     /**
21061     * fetch a CheckBox Group based on the group ID
21062     * @param {string} the group ID
21063     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21064     */
21065     get: function(groupId) {
21066         if (typeof(this.groups[groupId]) == 'undefined') {
21067             return false;
21068         }
21069         
21070         return this.groups[groupId] ;
21071     }
21072     
21073     
21074 });
21075 /*
21076  * - LGPL
21077  *
21078  * RadioItem
21079  * 
21080  */
21081
21082 /**
21083  * @class Roo.bootstrap.Radio
21084  * @extends Roo.bootstrap.Component
21085  * Bootstrap Radio class
21086  * @cfg {String} boxLabel - the label associated
21087  * @cfg {String} value - the value of radio
21088  * 
21089  * @constructor
21090  * Create a new Radio
21091  * @param {Object} config The config object
21092  */
21093 Roo.bootstrap.Radio = function(config){
21094     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21095     
21096 };
21097
21098 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21099     
21100     boxLabel : '',
21101     
21102     value : '',
21103     
21104     getAutoCreate : function()
21105     {
21106         var cfg = {
21107             tag : 'div',
21108             cls : 'form-group radio',
21109             cn : [
21110                 {
21111                     tag : 'label',
21112                     cls : 'box-label',
21113                     html : this.boxLabel
21114                 }
21115             ]
21116         };
21117         
21118         return cfg;
21119     },
21120     
21121     initEvents : function() 
21122     {
21123         this.parent().register(this);
21124         
21125         this.el.on('click', this.onClick, this);
21126         
21127     },
21128     
21129     onClick : function(e)
21130     {
21131         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21132             this.setChecked(true);
21133         }
21134     },
21135     
21136     setChecked : function(state, suppressEvent)
21137     {
21138         this.parent().setValue(this.value, suppressEvent);
21139         
21140     },
21141     
21142     setBoxLabel : function(v)
21143     {
21144         this.boxLabel = v;
21145         
21146         if(this.rendered){
21147             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21148         }
21149     }
21150     
21151 });
21152  
21153
21154  /*
21155  * - LGPL
21156  *
21157  * Input
21158  * 
21159  */
21160
21161 /**
21162  * @class Roo.bootstrap.SecurePass
21163  * @extends Roo.bootstrap.Input
21164  * Bootstrap SecurePass class
21165  *
21166  * 
21167  * @constructor
21168  * Create a new SecurePass
21169  * @param {Object} config The config object
21170  */
21171  
21172 Roo.bootstrap.SecurePass = function (config) {
21173     // these go here, so the translation tool can replace them..
21174     this.errors = {
21175         PwdEmpty: "Please type a password, and then retype it to confirm.",
21176         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21177         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21178         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21179         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21180         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21181         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21182         TooWeak: "Your password is Too Weak."
21183     },
21184     this.meterLabel = "Password strength:";
21185     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21186     this.meterClass = [
21187         "roo-password-meter-tooweak", 
21188         "roo-password-meter-weak", 
21189         "roo-password-meter-medium", 
21190         "roo-password-meter-strong", 
21191         "roo-password-meter-grey"
21192     ];
21193     
21194     this.errors = {};
21195     
21196     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21197 }
21198
21199 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21200     /**
21201      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21202      * {
21203      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21204      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21205      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21206      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21207      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21208      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21209      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21210      * })
21211      */
21212     // private
21213     
21214     meterWidth: 300,
21215     errorMsg :'',    
21216     errors: false,
21217     imageRoot: '/',
21218     /**
21219      * @cfg {String/Object} Label for the strength meter (defaults to
21220      * 'Password strength:')
21221      */
21222     // private
21223     meterLabel: '',
21224     /**
21225      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21226      * ['Weak', 'Medium', 'Strong'])
21227      */
21228     // private    
21229     pwdStrengths: false,    
21230     // private
21231     strength: 0,
21232     // private
21233     _lastPwd: null,
21234     // private
21235     kCapitalLetter: 0,
21236     kSmallLetter: 1,
21237     kDigit: 2,
21238     kPunctuation: 3,
21239     
21240     insecure: false,
21241     // private
21242     initEvents: function ()
21243     {
21244         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21245
21246         if (this.el.is('input[type=password]') && Roo.isSafari) {
21247             this.el.on('keydown', this.SafariOnKeyDown, this);
21248         }
21249
21250         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21251     },
21252     // private
21253     onRender: function (ct, position)
21254     {
21255         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21256         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21257         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21258
21259         this.trigger.createChild({
21260                    cn: [
21261                     {
21262                     //id: 'PwdMeter',
21263                     tag: 'div',
21264                     cls: 'roo-password-meter-grey col-xs-12',
21265                     style: {
21266                         //width: 0,
21267                         //width: this.meterWidth + 'px'                                                
21268                         }
21269                     },
21270                     {                            
21271                          cls: 'roo-password-meter-text'                          
21272                     }
21273                 ]            
21274         });
21275
21276          
21277         if (this.hideTrigger) {
21278             this.trigger.setDisplayed(false);
21279         }
21280         this.setSize(this.width || '', this.height || '');
21281     },
21282     // private
21283     onDestroy: function ()
21284     {
21285         if (this.trigger) {
21286             this.trigger.removeAllListeners();
21287             this.trigger.remove();
21288         }
21289         if (this.wrap) {
21290             this.wrap.remove();
21291         }
21292         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21293     },
21294     // private
21295     checkStrength: function ()
21296     {
21297         var pwd = this.inputEl().getValue();
21298         if (pwd == this._lastPwd) {
21299             return;
21300         }
21301
21302         var strength;
21303         if (this.ClientSideStrongPassword(pwd)) {
21304             strength = 3;
21305         } else if (this.ClientSideMediumPassword(pwd)) {
21306             strength = 2;
21307         } else if (this.ClientSideWeakPassword(pwd)) {
21308             strength = 1;
21309         } else {
21310             strength = 0;
21311         }
21312         
21313         Roo.log('strength1: ' + strength);
21314         
21315         //var pm = this.trigger.child('div/div/div').dom;
21316         var pm = this.trigger.child('div/div');
21317         pm.removeClass(this.meterClass);
21318         pm.addClass(this.meterClass[strength]);
21319                 
21320         
21321         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21322                 
21323         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21324         
21325         this._lastPwd = pwd;
21326     },
21327     reset: function ()
21328     {
21329         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21330         
21331         this._lastPwd = '';
21332         
21333         var pm = this.trigger.child('div/div');
21334         pm.removeClass(this.meterClass);
21335         pm.addClass('roo-password-meter-grey');        
21336         
21337         
21338         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21339         
21340         pt.innerHTML = '';
21341         this.inputEl().dom.type='password';
21342     },
21343     // private
21344     validateValue: function (value)
21345     {
21346         
21347         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21348             return false;
21349         }
21350         if (value.length == 0) {
21351             if (this.allowBlank) {
21352                 this.clearInvalid();
21353                 return true;
21354             }
21355
21356             this.markInvalid(this.errors.PwdEmpty);
21357             this.errorMsg = this.errors.PwdEmpty;
21358             return false;
21359         }
21360         
21361         if(this.insecure){
21362             return true;
21363         }
21364         
21365         if ('[\x21-\x7e]*'.match(value)) {
21366             this.markInvalid(this.errors.PwdBadChar);
21367             this.errorMsg = this.errors.PwdBadChar;
21368             return false;
21369         }
21370         if (value.length < 6) {
21371             this.markInvalid(this.errors.PwdShort);
21372             this.errorMsg = this.errors.PwdShort;
21373             return false;
21374         }
21375         if (value.length > 16) {
21376             this.markInvalid(this.errors.PwdLong);
21377             this.errorMsg = this.errors.PwdLong;
21378             return false;
21379         }
21380         var strength;
21381         if (this.ClientSideStrongPassword(value)) {
21382             strength = 3;
21383         } else if (this.ClientSideMediumPassword(value)) {
21384             strength = 2;
21385         } else if (this.ClientSideWeakPassword(value)) {
21386             strength = 1;
21387         } else {
21388             strength = 0;
21389         }
21390
21391         
21392         if (strength < 2) {
21393             //this.markInvalid(this.errors.TooWeak);
21394             this.errorMsg = this.errors.TooWeak;
21395             //return false;
21396         }
21397         
21398         
21399         console.log('strength2: ' + strength);
21400         
21401         //var pm = this.trigger.child('div/div/div').dom;
21402         
21403         var pm = this.trigger.child('div/div');
21404         pm.removeClass(this.meterClass);
21405         pm.addClass(this.meterClass[strength]);
21406                 
21407         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21408                 
21409         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21410         
21411         this.errorMsg = ''; 
21412         return true;
21413     },
21414     // private
21415     CharacterSetChecks: function (type)
21416     {
21417         this.type = type;
21418         this.fResult = false;
21419     },
21420     // private
21421     isctype: function (character, type)
21422     {
21423         switch (type) {  
21424             case this.kCapitalLetter:
21425                 if (character >= 'A' && character <= 'Z') {
21426                     return true;
21427                 }
21428                 break;
21429             
21430             case this.kSmallLetter:
21431                 if (character >= 'a' && character <= 'z') {
21432                     return true;
21433                 }
21434                 break;
21435             
21436             case this.kDigit:
21437                 if (character >= '0' && character <= '9') {
21438                     return true;
21439                 }
21440                 break;
21441             
21442             case this.kPunctuation:
21443                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21444                     return true;
21445                 }
21446                 break;
21447             
21448             default:
21449                 return false;
21450         }
21451
21452     },
21453     // private
21454     IsLongEnough: function (pwd, size)
21455     {
21456         return !(pwd == null || isNaN(size) || pwd.length < size);
21457     },
21458     // private
21459     SpansEnoughCharacterSets: function (word, nb)
21460     {
21461         if (!this.IsLongEnough(word, nb))
21462         {
21463             return false;
21464         }
21465
21466         var characterSetChecks = new Array(
21467             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21468             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21469         );
21470         
21471         for (var index = 0; index < word.length; ++index) {
21472             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21473                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21474                     characterSetChecks[nCharSet].fResult = true;
21475                     break;
21476                 }
21477             }
21478         }
21479
21480         var nCharSets = 0;
21481         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21482             if (characterSetChecks[nCharSet].fResult) {
21483                 ++nCharSets;
21484             }
21485         }
21486
21487         if (nCharSets < nb) {
21488             return false;
21489         }
21490         return true;
21491     },
21492     // private
21493     ClientSideStrongPassword: function (pwd)
21494     {
21495         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21496     },
21497     // private
21498     ClientSideMediumPassword: function (pwd)
21499     {
21500         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21501     },
21502     // private
21503     ClientSideWeakPassword: function (pwd)
21504     {
21505         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21506     }
21507           
21508 })//<script type="text/javascript">
21509
21510 /*
21511  * Based  Ext JS Library 1.1.1
21512  * Copyright(c) 2006-2007, Ext JS, LLC.
21513  * LGPL
21514  *
21515  */
21516  
21517 /**
21518  * @class Roo.HtmlEditorCore
21519  * @extends Roo.Component
21520  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21521  *
21522  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21523  */
21524
21525 Roo.HtmlEditorCore = function(config){
21526     
21527     
21528     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21529     
21530     
21531     this.addEvents({
21532         /**
21533          * @event initialize
21534          * Fires when the editor is fully initialized (including the iframe)
21535          * @param {Roo.HtmlEditorCore} this
21536          */
21537         initialize: true,
21538         /**
21539          * @event activate
21540          * Fires when the editor is first receives the focus. Any insertion must wait
21541          * until after this event.
21542          * @param {Roo.HtmlEditorCore} this
21543          */
21544         activate: true,
21545          /**
21546          * @event beforesync
21547          * Fires before the textarea is updated with content from the editor iframe. Return false
21548          * to cancel the sync.
21549          * @param {Roo.HtmlEditorCore} this
21550          * @param {String} html
21551          */
21552         beforesync: true,
21553          /**
21554          * @event beforepush
21555          * Fires before the iframe editor is updated with content from the textarea. Return false
21556          * to cancel the push.
21557          * @param {Roo.HtmlEditorCore} this
21558          * @param {String} html
21559          */
21560         beforepush: true,
21561          /**
21562          * @event sync
21563          * Fires when the textarea is updated with content from the editor iframe.
21564          * @param {Roo.HtmlEditorCore} this
21565          * @param {String} html
21566          */
21567         sync: true,
21568          /**
21569          * @event push
21570          * Fires when the iframe editor is updated with content from the textarea.
21571          * @param {Roo.HtmlEditorCore} this
21572          * @param {String} html
21573          */
21574         push: true,
21575         
21576         /**
21577          * @event editorevent
21578          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21579          * @param {Roo.HtmlEditorCore} this
21580          */
21581         editorevent: true
21582         
21583     });
21584     
21585     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21586     
21587     // defaults : white / black...
21588     this.applyBlacklists();
21589     
21590     
21591     
21592 };
21593
21594
21595 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21596
21597
21598      /**
21599      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21600      */
21601     
21602     owner : false,
21603     
21604      /**
21605      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21606      *                        Roo.resizable.
21607      */
21608     resizable : false,
21609      /**
21610      * @cfg {Number} height (in pixels)
21611      */   
21612     height: 300,
21613    /**
21614      * @cfg {Number} width (in pixels)
21615      */   
21616     width: 500,
21617     
21618     /**
21619      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21620      * 
21621      */
21622     stylesheets: false,
21623     
21624     // id of frame..
21625     frameId: false,
21626     
21627     // private properties
21628     validationEvent : false,
21629     deferHeight: true,
21630     initialized : false,
21631     activated : false,
21632     sourceEditMode : false,
21633     onFocus : Roo.emptyFn,
21634     iframePad:3,
21635     hideMode:'offsets',
21636     
21637     clearUp: true,
21638     
21639     // blacklist + whitelisted elements..
21640     black: false,
21641     white: false,
21642      
21643     bodyCls : '',
21644
21645     /**
21646      * Protected method that will not generally be called directly. It
21647      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21648      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21649      */
21650     getDocMarkup : function(){
21651         // body styles..
21652         var st = '';
21653         
21654         // inherit styels from page...?? 
21655         if (this.stylesheets === false) {
21656             
21657             Roo.get(document.head).select('style').each(function(node) {
21658                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21659             });
21660             
21661             Roo.get(document.head).select('link').each(function(node) { 
21662                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21663             });
21664             
21665         } else if (!this.stylesheets.length) {
21666                 // simple..
21667                 st = '<style type="text/css">' +
21668                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21669                    '</style>';
21670         } else { 
21671             st = '<style type="text/css">' +
21672                     this.stylesheets +
21673                 '</style>';
21674         }
21675         
21676         st +=  '<style type="text/css">' +
21677             'IMG { cursor: pointer } ' +
21678         '</style>';
21679
21680         var cls = 'roo-htmleditor-body';
21681         
21682         if(this.bodyCls.length){
21683             cls += ' ' + this.bodyCls;
21684         }
21685         
21686         return '<html><head>' + st  +
21687             //<style type="text/css">' +
21688             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21689             //'</style>' +
21690             ' </head><body class="' +  cls + '"></body></html>';
21691     },
21692
21693     // private
21694     onRender : function(ct, position)
21695     {
21696         var _t = this;
21697         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21698         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21699         
21700         
21701         this.el.dom.style.border = '0 none';
21702         this.el.dom.setAttribute('tabIndex', -1);
21703         this.el.addClass('x-hidden hide');
21704         
21705         
21706         
21707         if(Roo.isIE){ // fix IE 1px bogus margin
21708             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21709         }
21710        
21711         
21712         this.frameId = Roo.id();
21713         
21714          
21715         
21716         var iframe = this.owner.wrap.createChild({
21717             tag: 'iframe',
21718             cls: 'form-control', // bootstrap..
21719             id: this.frameId,
21720             name: this.frameId,
21721             frameBorder : 'no',
21722             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21723         }, this.el
21724         );
21725         
21726         
21727         this.iframe = iframe.dom;
21728
21729          this.assignDocWin();
21730         
21731         this.doc.designMode = 'on';
21732        
21733         this.doc.open();
21734         this.doc.write(this.getDocMarkup());
21735         this.doc.close();
21736
21737         
21738         var task = { // must defer to wait for browser to be ready
21739             run : function(){
21740                 //console.log("run task?" + this.doc.readyState);
21741                 this.assignDocWin();
21742                 if(this.doc.body || this.doc.readyState == 'complete'){
21743                     try {
21744                         this.doc.designMode="on";
21745                     } catch (e) {
21746                         return;
21747                     }
21748                     Roo.TaskMgr.stop(task);
21749                     this.initEditor.defer(10, this);
21750                 }
21751             },
21752             interval : 10,
21753             duration: 10000,
21754             scope: this
21755         };
21756         Roo.TaskMgr.start(task);
21757
21758     },
21759
21760     // private
21761     onResize : function(w, h)
21762     {
21763          Roo.log('resize: ' +w + ',' + h );
21764         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21765         if(!this.iframe){
21766             return;
21767         }
21768         if(typeof w == 'number'){
21769             
21770             this.iframe.style.width = w + 'px';
21771         }
21772         if(typeof h == 'number'){
21773             
21774             this.iframe.style.height = h + 'px';
21775             if(this.doc){
21776                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21777             }
21778         }
21779         
21780     },
21781
21782     /**
21783      * Toggles the editor between standard and source edit mode.
21784      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21785      */
21786     toggleSourceEdit : function(sourceEditMode){
21787         
21788         this.sourceEditMode = sourceEditMode === true;
21789         
21790         if(this.sourceEditMode){
21791  
21792             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21793             
21794         }else{
21795             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21796             //this.iframe.className = '';
21797             this.deferFocus();
21798         }
21799         //this.setSize(this.owner.wrap.getSize());
21800         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21801     },
21802
21803     
21804   
21805
21806     /**
21807      * Protected method that will not generally be called directly. If you need/want
21808      * custom HTML cleanup, this is the method you should override.
21809      * @param {String} html The HTML to be cleaned
21810      * return {String} The cleaned HTML
21811      */
21812     cleanHtml : function(html){
21813         html = String(html);
21814         if(html.length > 5){
21815             if(Roo.isSafari){ // strip safari nonsense
21816                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21817             }
21818         }
21819         if(html == '&nbsp;'){
21820             html = '';
21821         }
21822         return html;
21823     },
21824
21825     /**
21826      * HTML Editor -> Textarea
21827      * Protected method that will not generally be called directly. Syncs the contents
21828      * of the editor iframe with the textarea.
21829      */
21830     syncValue : function(){
21831         if(this.initialized){
21832             var bd = (this.doc.body || this.doc.documentElement);
21833             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21834             var html = bd.innerHTML;
21835             if(Roo.isSafari){
21836                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21837                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21838                 if(m && m[1]){
21839                     html = '<div style="'+m[0]+'">' + html + '</div>';
21840                 }
21841             }
21842             html = this.cleanHtml(html);
21843             // fix up the special chars.. normaly like back quotes in word...
21844             // however we do not want to do this with chinese..
21845             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21846                 var cc = b.charCodeAt();
21847                 if (
21848                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21849                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21850                     (cc >= 0xf900 && cc < 0xfb00 )
21851                 ) {
21852                         return b;
21853                 }
21854                 return "&#"+cc+";" 
21855             });
21856             if(this.owner.fireEvent('beforesync', this, html) !== false){
21857                 this.el.dom.value = html;
21858                 this.owner.fireEvent('sync', this, html);
21859             }
21860         }
21861     },
21862
21863     /**
21864      * Protected method that will not generally be called directly. Pushes the value of the textarea
21865      * into the iframe editor.
21866      */
21867     pushValue : function(){
21868         if(this.initialized){
21869             var v = this.el.dom.value.trim();
21870             
21871 //            if(v.length < 1){
21872 //                v = '&#160;';
21873 //            }
21874             
21875             if(this.owner.fireEvent('beforepush', this, v) !== false){
21876                 var d = (this.doc.body || this.doc.documentElement);
21877                 d.innerHTML = v;
21878                 this.cleanUpPaste();
21879                 this.el.dom.value = d.innerHTML;
21880                 this.owner.fireEvent('push', this, v);
21881             }
21882         }
21883     },
21884
21885     // private
21886     deferFocus : function(){
21887         this.focus.defer(10, this);
21888     },
21889
21890     // doc'ed in Field
21891     focus : function(){
21892         if(this.win && !this.sourceEditMode){
21893             this.win.focus();
21894         }else{
21895             this.el.focus();
21896         }
21897     },
21898     
21899     assignDocWin: function()
21900     {
21901         var iframe = this.iframe;
21902         
21903          if(Roo.isIE){
21904             this.doc = iframe.contentWindow.document;
21905             this.win = iframe.contentWindow;
21906         } else {
21907 //            if (!Roo.get(this.frameId)) {
21908 //                return;
21909 //            }
21910 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21911 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21912             
21913             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21914                 return;
21915             }
21916             
21917             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21918             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21919         }
21920     },
21921     
21922     // private
21923     initEditor : function(){
21924         //console.log("INIT EDITOR");
21925         this.assignDocWin();
21926         
21927         
21928         
21929         this.doc.designMode="on";
21930         this.doc.open();
21931         this.doc.write(this.getDocMarkup());
21932         this.doc.close();
21933         
21934         var dbody = (this.doc.body || this.doc.documentElement);
21935         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21936         // this copies styles from the containing element into thsi one..
21937         // not sure why we need all of this..
21938         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21939         
21940         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21941         //ss['background-attachment'] = 'fixed'; // w3c
21942         dbody.bgProperties = 'fixed'; // ie
21943         //Roo.DomHelper.applyStyles(dbody, ss);
21944         Roo.EventManager.on(this.doc, {
21945             //'mousedown': this.onEditorEvent,
21946             'mouseup': this.onEditorEvent,
21947             'dblclick': this.onEditorEvent,
21948             'click': this.onEditorEvent,
21949             'keyup': this.onEditorEvent,
21950             buffer:100,
21951             scope: this
21952         });
21953         if(Roo.isGecko){
21954             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21955         }
21956         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21957             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21958         }
21959         this.initialized = true;
21960
21961         this.owner.fireEvent('initialize', this);
21962         this.pushValue();
21963     },
21964
21965     // private
21966     onDestroy : function(){
21967         
21968         
21969         
21970         if(this.rendered){
21971             
21972             //for (var i =0; i < this.toolbars.length;i++) {
21973             //    // fixme - ask toolbars for heights?
21974             //    this.toolbars[i].onDestroy();
21975            // }
21976             
21977             //this.wrap.dom.innerHTML = '';
21978             //this.wrap.remove();
21979         }
21980     },
21981
21982     // private
21983     onFirstFocus : function(){
21984         
21985         this.assignDocWin();
21986         
21987         
21988         this.activated = true;
21989          
21990     
21991         if(Roo.isGecko){ // prevent silly gecko errors
21992             this.win.focus();
21993             var s = this.win.getSelection();
21994             if(!s.focusNode || s.focusNode.nodeType != 3){
21995                 var r = s.getRangeAt(0);
21996                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21997                 r.collapse(true);
21998                 this.deferFocus();
21999             }
22000             try{
22001                 this.execCmd('useCSS', true);
22002                 this.execCmd('styleWithCSS', false);
22003             }catch(e){}
22004         }
22005         this.owner.fireEvent('activate', this);
22006     },
22007
22008     // private
22009     adjustFont: function(btn){
22010         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22011         //if(Roo.isSafari){ // safari
22012         //    adjust *= 2;
22013        // }
22014         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22015         if(Roo.isSafari){ // safari
22016             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22017             v =  (v < 10) ? 10 : v;
22018             v =  (v > 48) ? 48 : v;
22019             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22020             
22021         }
22022         
22023         
22024         v = Math.max(1, v+adjust);
22025         
22026         this.execCmd('FontSize', v  );
22027     },
22028
22029     onEditorEvent : function(e)
22030     {
22031         this.owner.fireEvent('editorevent', this, e);
22032       //  this.updateToolbar();
22033         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22034     },
22035
22036     insertTag : function(tg)
22037     {
22038         // could be a bit smarter... -> wrap the current selected tRoo..
22039         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22040             
22041             range = this.createRange(this.getSelection());
22042             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22043             wrappingNode.appendChild(range.extractContents());
22044             range.insertNode(wrappingNode);
22045
22046             return;
22047             
22048             
22049             
22050         }
22051         this.execCmd("formatblock",   tg);
22052         
22053     },
22054     
22055     insertText : function(txt)
22056     {
22057         
22058         
22059         var range = this.createRange();
22060         range.deleteContents();
22061                //alert(Sender.getAttribute('label'));
22062                
22063         range.insertNode(this.doc.createTextNode(txt));
22064     } ,
22065     
22066      
22067
22068     /**
22069      * Executes a Midas editor command on the editor document and performs necessary focus and
22070      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22071      * @param {String} cmd The Midas command
22072      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22073      */
22074     relayCmd : function(cmd, value){
22075         this.win.focus();
22076         this.execCmd(cmd, value);
22077         this.owner.fireEvent('editorevent', this);
22078         //this.updateToolbar();
22079         this.owner.deferFocus();
22080     },
22081
22082     /**
22083      * Executes a Midas editor command directly on the editor document.
22084      * For visual commands, you should use {@link #relayCmd} instead.
22085      * <b>This should only be called after the editor is initialized.</b>
22086      * @param {String} cmd The Midas command
22087      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22088      */
22089     execCmd : function(cmd, value){
22090         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22091         this.syncValue();
22092     },
22093  
22094  
22095    
22096     /**
22097      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22098      * to insert tRoo.
22099      * @param {String} text | dom node.. 
22100      */
22101     insertAtCursor : function(text)
22102     {
22103         
22104         if(!this.activated){
22105             return;
22106         }
22107         /*
22108         if(Roo.isIE){
22109             this.win.focus();
22110             var r = this.doc.selection.createRange();
22111             if(r){
22112                 r.collapse(true);
22113                 r.pasteHTML(text);
22114                 this.syncValue();
22115                 this.deferFocus();
22116             
22117             }
22118             return;
22119         }
22120         */
22121         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22122             this.win.focus();
22123             
22124             
22125             // from jquery ui (MIT licenced)
22126             var range, node;
22127             var win = this.win;
22128             
22129             if (win.getSelection && win.getSelection().getRangeAt) {
22130                 range = win.getSelection().getRangeAt(0);
22131                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22132                 range.insertNode(node);
22133             } else if (win.document.selection && win.document.selection.createRange) {
22134                 // no firefox support
22135                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22136                 win.document.selection.createRange().pasteHTML(txt);
22137             } else {
22138                 // no firefox support
22139                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22140                 this.execCmd('InsertHTML', txt);
22141             } 
22142             
22143             this.syncValue();
22144             
22145             this.deferFocus();
22146         }
22147     },
22148  // private
22149     mozKeyPress : function(e){
22150         if(e.ctrlKey){
22151             var c = e.getCharCode(), cmd;
22152           
22153             if(c > 0){
22154                 c = String.fromCharCode(c).toLowerCase();
22155                 switch(c){
22156                     case 'b':
22157                         cmd = 'bold';
22158                         break;
22159                     case 'i':
22160                         cmd = 'italic';
22161                         break;
22162                     
22163                     case 'u':
22164                         cmd = 'underline';
22165                         break;
22166                     
22167                     case 'v':
22168                         this.cleanUpPaste.defer(100, this);
22169                         return;
22170                         
22171                 }
22172                 if(cmd){
22173                     this.win.focus();
22174                     this.execCmd(cmd);
22175                     this.deferFocus();
22176                     e.preventDefault();
22177                 }
22178                 
22179             }
22180         }
22181     },
22182
22183     // private
22184     fixKeys : function(){ // load time branching for fastest keydown performance
22185         if(Roo.isIE){
22186             return function(e){
22187                 var k = e.getKey(), r;
22188                 if(k == e.TAB){
22189                     e.stopEvent();
22190                     r = this.doc.selection.createRange();
22191                     if(r){
22192                         r.collapse(true);
22193                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22194                         this.deferFocus();
22195                     }
22196                     return;
22197                 }
22198                 
22199                 if(k == e.ENTER){
22200                     r = this.doc.selection.createRange();
22201                     if(r){
22202                         var target = r.parentElement();
22203                         if(!target || target.tagName.toLowerCase() != 'li'){
22204                             e.stopEvent();
22205                             r.pasteHTML('<br />');
22206                             r.collapse(false);
22207                             r.select();
22208                         }
22209                     }
22210                 }
22211                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22212                     this.cleanUpPaste.defer(100, this);
22213                     return;
22214                 }
22215                 
22216                 
22217             };
22218         }else if(Roo.isOpera){
22219             return function(e){
22220                 var k = e.getKey();
22221                 if(k == e.TAB){
22222                     e.stopEvent();
22223                     this.win.focus();
22224                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22225                     this.deferFocus();
22226                 }
22227                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22228                     this.cleanUpPaste.defer(100, this);
22229                     return;
22230                 }
22231                 
22232             };
22233         }else if(Roo.isSafari){
22234             return function(e){
22235                 var k = e.getKey();
22236                 
22237                 if(k == e.TAB){
22238                     e.stopEvent();
22239                     this.execCmd('InsertText','\t');
22240                     this.deferFocus();
22241                     return;
22242                 }
22243                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22244                     this.cleanUpPaste.defer(100, this);
22245                     return;
22246                 }
22247                 
22248              };
22249         }
22250     }(),
22251     
22252     getAllAncestors: function()
22253     {
22254         var p = this.getSelectedNode();
22255         var a = [];
22256         if (!p) {
22257             a.push(p); // push blank onto stack..
22258             p = this.getParentElement();
22259         }
22260         
22261         
22262         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22263             a.push(p);
22264             p = p.parentNode;
22265         }
22266         a.push(this.doc.body);
22267         return a;
22268     },
22269     lastSel : false,
22270     lastSelNode : false,
22271     
22272     
22273     getSelection : function() 
22274     {
22275         this.assignDocWin();
22276         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22277     },
22278     
22279     getSelectedNode: function() 
22280     {
22281         // this may only work on Gecko!!!
22282         
22283         // should we cache this!!!!
22284         
22285         
22286         
22287          
22288         var range = this.createRange(this.getSelection()).cloneRange();
22289         
22290         if (Roo.isIE) {
22291             var parent = range.parentElement();
22292             while (true) {
22293                 var testRange = range.duplicate();
22294                 testRange.moveToElementText(parent);
22295                 if (testRange.inRange(range)) {
22296                     break;
22297                 }
22298                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22299                     break;
22300                 }
22301                 parent = parent.parentElement;
22302             }
22303             return parent;
22304         }
22305         
22306         // is ancestor a text element.
22307         var ac =  range.commonAncestorContainer;
22308         if (ac.nodeType == 3) {
22309             ac = ac.parentNode;
22310         }
22311         
22312         var ar = ac.childNodes;
22313          
22314         var nodes = [];
22315         var other_nodes = [];
22316         var has_other_nodes = false;
22317         for (var i=0;i<ar.length;i++) {
22318             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22319                 continue;
22320             }
22321             // fullly contained node.
22322             
22323             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22324                 nodes.push(ar[i]);
22325                 continue;
22326             }
22327             
22328             // probably selected..
22329             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22330                 other_nodes.push(ar[i]);
22331                 continue;
22332             }
22333             // outer..
22334             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22335                 continue;
22336             }
22337             
22338             
22339             has_other_nodes = true;
22340         }
22341         if (!nodes.length && other_nodes.length) {
22342             nodes= other_nodes;
22343         }
22344         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22345             return false;
22346         }
22347         
22348         return nodes[0];
22349     },
22350     createRange: function(sel)
22351     {
22352         // this has strange effects when using with 
22353         // top toolbar - not sure if it's a great idea.
22354         //this.editor.contentWindow.focus();
22355         if (typeof sel != "undefined") {
22356             try {
22357                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22358             } catch(e) {
22359                 return this.doc.createRange();
22360             }
22361         } else {
22362             return this.doc.createRange();
22363         }
22364     },
22365     getParentElement: function()
22366     {
22367         
22368         this.assignDocWin();
22369         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22370         
22371         var range = this.createRange(sel);
22372          
22373         try {
22374             var p = range.commonAncestorContainer;
22375             while (p.nodeType == 3) { // text node
22376                 p = p.parentNode;
22377             }
22378             return p;
22379         } catch (e) {
22380             return null;
22381         }
22382     
22383     },
22384     /***
22385      *
22386      * Range intersection.. the hard stuff...
22387      *  '-1' = before
22388      *  '0' = hits..
22389      *  '1' = after.
22390      *         [ -- selected range --- ]
22391      *   [fail]                        [fail]
22392      *
22393      *    basically..
22394      *      if end is before start or  hits it. fail.
22395      *      if start is after end or hits it fail.
22396      *
22397      *   if either hits (but other is outside. - then it's not 
22398      *   
22399      *    
22400      **/
22401     
22402     
22403     // @see http://www.thismuchiknow.co.uk/?p=64.
22404     rangeIntersectsNode : function(range, node)
22405     {
22406         var nodeRange = node.ownerDocument.createRange();
22407         try {
22408             nodeRange.selectNode(node);
22409         } catch (e) {
22410             nodeRange.selectNodeContents(node);
22411         }
22412     
22413         var rangeStartRange = range.cloneRange();
22414         rangeStartRange.collapse(true);
22415     
22416         var rangeEndRange = range.cloneRange();
22417         rangeEndRange.collapse(false);
22418     
22419         var nodeStartRange = nodeRange.cloneRange();
22420         nodeStartRange.collapse(true);
22421     
22422         var nodeEndRange = nodeRange.cloneRange();
22423         nodeEndRange.collapse(false);
22424     
22425         return rangeStartRange.compareBoundaryPoints(
22426                  Range.START_TO_START, nodeEndRange) == -1 &&
22427                rangeEndRange.compareBoundaryPoints(
22428                  Range.START_TO_START, nodeStartRange) == 1;
22429         
22430          
22431     },
22432     rangeCompareNode : function(range, node)
22433     {
22434         var nodeRange = node.ownerDocument.createRange();
22435         try {
22436             nodeRange.selectNode(node);
22437         } catch (e) {
22438             nodeRange.selectNodeContents(node);
22439         }
22440         
22441         
22442         range.collapse(true);
22443     
22444         nodeRange.collapse(true);
22445      
22446         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22447         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22448          
22449         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22450         
22451         var nodeIsBefore   =  ss == 1;
22452         var nodeIsAfter    = ee == -1;
22453         
22454         if (nodeIsBefore && nodeIsAfter) {
22455             return 0; // outer
22456         }
22457         if (!nodeIsBefore && nodeIsAfter) {
22458             return 1; //right trailed.
22459         }
22460         
22461         if (nodeIsBefore && !nodeIsAfter) {
22462             return 2;  // left trailed.
22463         }
22464         // fully contined.
22465         return 3;
22466     },
22467
22468     // private? - in a new class?
22469     cleanUpPaste :  function()
22470     {
22471         // cleans up the whole document..
22472         Roo.log('cleanuppaste');
22473         
22474         this.cleanUpChildren(this.doc.body);
22475         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22476         if (clean != this.doc.body.innerHTML) {
22477             this.doc.body.innerHTML = clean;
22478         }
22479         
22480     },
22481     
22482     cleanWordChars : function(input) {// change the chars to hex code
22483         var he = Roo.HtmlEditorCore;
22484         
22485         var output = input;
22486         Roo.each(he.swapCodes, function(sw) { 
22487             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22488             
22489             output = output.replace(swapper, sw[1]);
22490         });
22491         
22492         return output;
22493     },
22494     
22495     
22496     cleanUpChildren : function (n)
22497     {
22498         if (!n.childNodes.length) {
22499             return;
22500         }
22501         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22502            this.cleanUpChild(n.childNodes[i]);
22503         }
22504     },
22505     
22506     
22507         
22508     
22509     cleanUpChild : function (node)
22510     {
22511         var ed = this;
22512         //console.log(node);
22513         if (node.nodeName == "#text") {
22514             // clean up silly Windows -- stuff?
22515             return; 
22516         }
22517         if (node.nodeName == "#comment") {
22518             node.parentNode.removeChild(node);
22519             // clean up silly Windows -- stuff?
22520             return; 
22521         }
22522         var lcname = node.tagName.toLowerCase();
22523         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22524         // whitelist of tags..
22525         
22526         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22527             // remove node.
22528             node.parentNode.removeChild(node);
22529             return;
22530             
22531         }
22532         
22533         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22534         
22535         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22536         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22537         
22538         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22539         //    remove_keep_children = true;
22540         //}
22541         
22542         if (remove_keep_children) {
22543             this.cleanUpChildren(node);
22544             // inserts everything just before this node...
22545             while (node.childNodes.length) {
22546                 var cn = node.childNodes[0];
22547                 node.removeChild(cn);
22548                 node.parentNode.insertBefore(cn, node);
22549             }
22550             node.parentNode.removeChild(node);
22551             return;
22552         }
22553         
22554         if (!node.attributes || !node.attributes.length) {
22555             this.cleanUpChildren(node);
22556             return;
22557         }
22558         
22559         function cleanAttr(n,v)
22560         {
22561             
22562             if (v.match(/^\./) || v.match(/^\//)) {
22563                 return;
22564             }
22565             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22566                 return;
22567             }
22568             if (v.match(/^#/)) {
22569                 return;
22570             }
22571 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22572             node.removeAttribute(n);
22573             
22574         }
22575         
22576         var cwhite = this.cwhite;
22577         var cblack = this.cblack;
22578             
22579         function cleanStyle(n,v)
22580         {
22581             if (v.match(/expression/)) { //XSS?? should we even bother..
22582                 node.removeAttribute(n);
22583                 return;
22584             }
22585             
22586             var parts = v.split(/;/);
22587             var clean = [];
22588             
22589             Roo.each(parts, function(p) {
22590                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22591                 if (!p.length) {
22592                     return true;
22593                 }
22594                 var l = p.split(':').shift().replace(/\s+/g,'');
22595                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22596                 
22597                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22598 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22599                     //node.removeAttribute(n);
22600                     return true;
22601                 }
22602                 //Roo.log()
22603                 // only allow 'c whitelisted system attributes'
22604                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22605 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22606                     //node.removeAttribute(n);
22607                     return true;
22608                 }
22609                 
22610                 
22611                  
22612                 
22613                 clean.push(p);
22614                 return true;
22615             });
22616             if (clean.length) { 
22617                 node.setAttribute(n, clean.join(';'));
22618             } else {
22619                 node.removeAttribute(n);
22620             }
22621             
22622         }
22623         
22624         
22625         for (var i = node.attributes.length-1; i > -1 ; i--) {
22626             var a = node.attributes[i];
22627             //console.log(a);
22628             
22629             if (a.name.toLowerCase().substr(0,2)=='on')  {
22630                 node.removeAttribute(a.name);
22631                 continue;
22632             }
22633             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22634                 node.removeAttribute(a.name);
22635                 continue;
22636             }
22637             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22638                 cleanAttr(a.name,a.value); // fixme..
22639                 continue;
22640             }
22641             if (a.name == 'style') {
22642                 cleanStyle(a.name,a.value);
22643                 continue;
22644             }
22645             /// clean up MS crap..
22646             // tecnically this should be a list of valid class'es..
22647             
22648             
22649             if (a.name == 'class') {
22650                 if (a.value.match(/^Mso/)) {
22651                     node.className = '';
22652                 }
22653                 
22654                 if (a.value.match(/^body$/)) {
22655                     node.className = '';
22656                 }
22657                 continue;
22658             }
22659             
22660             // style cleanup!?
22661             // class cleanup?
22662             
22663         }
22664         
22665         
22666         this.cleanUpChildren(node);
22667         
22668         
22669     },
22670     
22671     /**
22672      * Clean up MS wordisms...
22673      */
22674     cleanWord : function(node)
22675     {
22676         
22677         
22678         if (!node) {
22679             this.cleanWord(this.doc.body);
22680             return;
22681         }
22682         if (node.nodeName == "#text") {
22683             // clean up silly Windows -- stuff?
22684             return; 
22685         }
22686         if (node.nodeName == "#comment") {
22687             node.parentNode.removeChild(node);
22688             // clean up silly Windows -- stuff?
22689             return; 
22690         }
22691         
22692         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22693             node.parentNode.removeChild(node);
22694             return;
22695         }
22696         
22697         // remove - but keep children..
22698         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22699             while (node.childNodes.length) {
22700                 var cn = node.childNodes[0];
22701                 node.removeChild(cn);
22702                 node.parentNode.insertBefore(cn, node);
22703             }
22704             node.parentNode.removeChild(node);
22705             this.iterateChildren(node, this.cleanWord);
22706             return;
22707         }
22708         // clean styles
22709         if (node.className.length) {
22710             
22711             var cn = node.className.split(/\W+/);
22712             var cna = [];
22713             Roo.each(cn, function(cls) {
22714                 if (cls.match(/Mso[a-zA-Z]+/)) {
22715                     return;
22716                 }
22717                 cna.push(cls);
22718             });
22719             node.className = cna.length ? cna.join(' ') : '';
22720             if (!cna.length) {
22721                 node.removeAttribute("class");
22722             }
22723         }
22724         
22725         if (node.hasAttribute("lang")) {
22726             node.removeAttribute("lang");
22727         }
22728         
22729         if (node.hasAttribute("style")) {
22730             
22731             var styles = node.getAttribute("style").split(";");
22732             var nstyle = [];
22733             Roo.each(styles, function(s) {
22734                 if (!s.match(/:/)) {
22735                     return;
22736                 }
22737                 var kv = s.split(":");
22738                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22739                     return;
22740                 }
22741                 // what ever is left... we allow.
22742                 nstyle.push(s);
22743             });
22744             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22745             if (!nstyle.length) {
22746                 node.removeAttribute('style');
22747             }
22748         }
22749         this.iterateChildren(node, this.cleanWord);
22750         
22751         
22752         
22753     },
22754     /**
22755      * iterateChildren of a Node, calling fn each time, using this as the scole..
22756      * @param {DomNode} node node to iterate children of.
22757      * @param {Function} fn method of this class to call on each item.
22758      */
22759     iterateChildren : function(node, fn)
22760     {
22761         if (!node.childNodes.length) {
22762                 return;
22763         }
22764         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22765            fn.call(this, node.childNodes[i])
22766         }
22767     },
22768     
22769     
22770     /**
22771      * cleanTableWidths.
22772      *
22773      * Quite often pasting from word etc.. results in tables with column and widths.
22774      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22775      *
22776      */
22777     cleanTableWidths : function(node)
22778     {
22779          
22780          
22781         if (!node) {
22782             this.cleanTableWidths(this.doc.body);
22783             return;
22784         }
22785         
22786         // ignore list...
22787         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22788             return; 
22789         }
22790         Roo.log(node.tagName);
22791         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22792             this.iterateChildren(node, this.cleanTableWidths);
22793             return;
22794         }
22795         if (node.hasAttribute('width')) {
22796             node.removeAttribute('width');
22797         }
22798         
22799          
22800         if (node.hasAttribute("style")) {
22801             // pretty basic...
22802             
22803             var styles = node.getAttribute("style").split(";");
22804             var nstyle = [];
22805             Roo.each(styles, function(s) {
22806                 if (!s.match(/:/)) {
22807                     return;
22808                 }
22809                 var kv = s.split(":");
22810                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22811                     return;
22812                 }
22813                 // what ever is left... we allow.
22814                 nstyle.push(s);
22815             });
22816             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22817             if (!nstyle.length) {
22818                 node.removeAttribute('style');
22819             }
22820         }
22821         
22822         this.iterateChildren(node, this.cleanTableWidths);
22823         
22824         
22825     },
22826     
22827     
22828     
22829     
22830     domToHTML : function(currentElement, depth, nopadtext) {
22831         
22832         depth = depth || 0;
22833         nopadtext = nopadtext || false;
22834     
22835         if (!currentElement) {
22836             return this.domToHTML(this.doc.body);
22837         }
22838         
22839         //Roo.log(currentElement);
22840         var j;
22841         var allText = false;
22842         var nodeName = currentElement.nodeName;
22843         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22844         
22845         if  (nodeName == '#text') {
22846             
22847             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22848         }
22849         
22850         
22851         var ret = '';
22852         if (nodeName != 'BODY') {
22853              
22854             var i = 0;
22855             // Prints the node tagName, such as <A>, <IMG>, etc
22856             if (tagName) {
22857                 var attr = [];
22858                 for(i = 0; i < currentElement.attributes.length;i++) {
22859                     // quoting?
22860                     var aname = currentElement.attributes.item(i).name;
22861                     if (!currentElement.attributes.item(i).value.length) {
22862                         continue;
22863                     }
22864                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22865                 }
22866                 
22867                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22868             } 
22869             else {
22870                 
22871                 // eack
22872             }
22873         } else {
22874             tagName = false;
22875         }
22876         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22877             return ret;
22878         }
22879         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22880             nopadtext = true;
22881         }
22882         
22883         
22884         // Traverse the tree
22885         i = 0;
22886         var currentElementChild = currentElement.childNodes.item(i);
22887         var allText = true;
22888         var innerHTML  = '';
22889         lastnode = '';
22890         while (currentElementChild) {
22891             // Formatting code (indent the tree so it looks nice on the screen)
22892             var nopad = nopadtext;
22893             if (lastnode == 'SPAN') {
22894                 nopad  = true;
22895             }
22896             // text
22897             if  (currentElementChild.nodeName == '#text') {
22898                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22899                 toadd = nopadtext ? toadd : toadd.trim();
22900                 if (!nopad && toadd.length > 80) {
22901                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22902                 }
22903                 innerHTML  += toadd;
22904                 
22905                 i++;
22906                 currentElementChild = currentElement.childNodes.item(i);
22907                 lastNode = '';
22908                 continue;
22909             }
22910             allText = false;
22911             
22912             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22913                 
22914             // Recursively traverse the tree structure of the child node
22915             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22916             lastnode = currentElementChild.nodeName;
22917             i++;
22918             currentElementChild=currentElement.childNodes.item(i);
22919         }
22920         
22921         ret += innerHTML;
22922         
22923         if (!allText) {
22924                 // The remaining code is mostly for formatting the tree
22925             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22926         }
22927         
22928         
22929         if (tagName) {
22930             ret+= "</"+tagName+">";
22931         }
22932         return ret;
22933         
22934     },
22935         
22936     applyBlacklists : function()
22937     {
22938         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22939         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22940         
22941         this.white = [];
22942         this.black = [];
22943         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22944             if (b.indexOf(tag) > -1) {
22945                 return;
22946             }
22947             this.white.push(tag);
22948             
22949         }, this);
22950         
22951         Roo.each(w, function(tag) {
22952             if (b.indexOf(tag) > -1) {
22953                 return;
22954             }
22955             if (this.white.indexOf(tag) > -1) {
22956                 return;
22957             }
22958             this.white.push(tag);
22959             
22960         }, this);
22961         
22962         
22963         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22964             if (w.indexOf(tag) > -1) {
22965                 return;
22966             }
22967             this.black.push(tag);
22968             
22969         }, this);
22970         
22971         Roo.each(b, function(tag) {
22972             if (w.indexOf(tag) > -1) {
22973                 return;
22974             }
22975             if (this.black.indexOf(tag) > -1) {
22976                 return;
22977             }
22978             this.black.push(tag);
22979             
22980         }, this);
22981         
22982         
22983         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22984         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22985         
22986         this.cwhite = [];
22987         this.cblack = [];
22988         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22989             if (b.indexOf(tag) > -1) {
22990                 return;
22991             }
22992             this.cwhite.push(tag);
22993             
22994         }, this);
22995         
22996         Roo.each(w, function(tag) {
22997             if (b.indexOf(tag) > -1) {
22998                 return;
22999             }
23000             if (this.cwhite.indexOf(tag) > -1) {
23001                 return;
23002             }
23003             this.cwhite.push(tag);
23004             
23005         }, this);
23006         
23007         
23008         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23009             if (w.indexOf(tag) > -1) {
23010                 return;
23011             }
23012             this.cblack.push(tag);
23013             
23014         }, this);
23015         
23016         Roo.each(b, function(tag) {
23017             if (w.indexOf(tag) > -1) {
23018                 return;
23019             }
23020             if (this.cblack.indexOf(tag) > -1) {
23021                 return;
23022             }
23023             this.cblack.push(tag);
23024             
23025         }, this);
23026     },
23027     
23028     setStylesheets : function(stylesheets)
23029     {
23030         if(typeof(stylesheets) == 'string'){
23031             Roo.get(this.iframe.contentDocument.head).createChild({
23032                 tag : 'link',
23033                 rel : 'stylesheet',
23034                 type : 'text/css',
23035                 href : stylesheets
23036             });
23037             
23038             return;
23039         }
23040         var _this = this;
23041      
23042         Roo.each(stylesheets, function(s) {
23043             if(!s.length){
23044                 return;
23045             }
23046             
23047             Roo.get(_this.iframe.contentDocument.head).createChild({
23048                 tag : 'link',
23049                 rel : 'stylesheet',
23050                 type : 'text/css',
23051                 href : s
23052             });
23053         });
23054
23055         
23056     },
23057     
23058     removeStylesheets : function()
23059     {
23060         var _this = this;
23061         
23062         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23063             s.remove();
23064         });
23065     },
23066     
23067     setStyle : function(style)
23068     {
23069         Roo.get(this.iframe.contentDocument.head).createChild({
23070             tag : 'style',
23071             type : 'text/css',
23072             html : style
23073         });
23074
23075         return;
23076     }
23077     
23078     // hide stuff that is not compatible
23079     /**
23080      * @event blur
23081      * @hide
23082      */
23083     /**
23084      * @event change
23085      * @hide
23086      */
23087     /**
23088      * @event focus
23089      * @hide
23090      */
23091     /**
23092      * @event specialkey
23093      * @hide
23094      */
23095     /**
23096      * @cfg {String} fieldClass @hide
23097      */
23098     /**
23099      * @cfg {String} focusClass @hide
23100      */
23101     /**
23102      * @cfg {String} autoCreate @hide
23103      */
23104     /**
23105      * @cfg {String} inputType @hide
23106      */
23107     /**
23108      * @cfg {String} invalidClass @hide
23109      */
23110     /**
23111      * @cfg {String} invalidText @hide
23112      */
23113     /**
23114      * @cfg {String} msgFx @hide
23115      */
23116     /**
23117      * @cfg {String} validateOnBlur @hide
23118      */
23119 });
23120
23121 Roo.HtmlEditorCore.white = [
23122         'area', 'br', 'img', 'input', 'hr', 'wbr',
23123         
23124        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23125        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23126        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23127        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23128        'table',   'ul',         'xmp', 
23129        
23130        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23131       'thead',   'tr', 
23132      
23133       'dir', 'menu', 'ol', 'ul', 'dl',
23134        
23135       'embed',  'object'
23136 ];
23137
23138
23139 Roo.HtmlEditorCore.black = [
23140     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23141         'applet', // 
23142         'base',   'basefont', 'bgsound', 'blink',  'body', 
23143         'frame',  'frameset', 'head',    'html',   'ilayer', 
23144         'iframe', 'layer',  'link',     'meta',    'object',   
23145         'script', 'style' ,'title',  'xml' // clean later..
23146 ];
23147 Roo.HtmlEditorCore.clean = [
23148     'script', 'style', 'title', 'xml'
23149 ];
23150 Roo.HtmlEditorCore.remove = [
23151     'font'
23152 ];
23153 // attributes..
23154
23155 Roo.HtmlEditorCore.ablack = [
23156     'on'
23157 ];
23158     
23159 Roo.HtmlEditorCore.aclean = [ 
23160     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23161 ];
23162
23163 // protocols..
23164 Roo.HtmlEditorCore.pwhite= [
23165         'http',  'https',  'mailto'
23166 ];
23167
23168 // white listed style attributes.
23169 Roo.HtmlEditorCore.cwhite= [
23170       //  'text-align', /// default is to allow most things..
23171       
23172          
23173 //        'font-size'//??
23174 ];
23175
23176 // black listed style attributes.
23177 Roo.HtmlEditorCore.cblack= [
23178       //  'font-size' -- this can be set by the project 
23179 ];
23180
23181
23182 Roo.HtmlEditorCore.swapCodes   =[ 
23183     [    8211, "--" ], 
23184     [    8212, "--" ], 
23185     [    8216,  "'" ],  
23186     [    8217, "'" ],  
23187     [    8220, '"' ],  
23188     [    8221, '"' ],  
23189     [    8226, "*" ],  
23190     [    8230, "..." ]
23191 ]; 
23192
23193     /*
23194  * - LGPL
23195  *
23196  * HtmlEditor
23197  * 
23198  */
23199
23200 /**
23201  * @class Roo.bootstrap.HtmlEditor
23202  * @extends Roo.bootstrap.TextArea
23203  * Bootstrap HtmlEditor class
23204
23205  * @constructor
23206  * Create a new HtmlEditor
23207  * @param {Object} config The config object
23208  */
23209
23210 Roo.bootstrap.HtmlEditor = function(config){
23211     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23212     if (!this.toolbars) {
23213         this.toolbars = [];
23214     }
23215     
23216     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23217     this.addEvents({
23218             /**
23219              * @event initialize
23220              * Fires when the editor is fully initialized (including the iframe)
23221              * @param {HtmlEditor} this
23222              */
23223             initialize: true,
23224             /**
23225              * @event activate
23226              * Fires when the editor is first receives the focus. Any insertion must wait
23227              * until after this event.
23228              * @param {HtmlEditor} this
23229              */
23230             activate: true,
23231              /**
23232              * @event beforesync
23233              * Fires before the textarea is updated with content from the editor iframe. Return false
23234              * to cancel the sync.
23235              * @param {HtmlEditor} this
23236              * @param {String} html
23237              */
23238             beforesync: true,
23239              /**
23240              * @event beforepush
23241              * Fires before the iframe editor is updated with content from the textarea. Return false
23242              * to cancel the push.
23243              * @param {HtmlEditor} this
23244              * @param {String} html
23245              */
23246             beforepush: true,
23247              /**
23248              * @event sync
23249              * Fires when the textarea is updated with content from the editor iframe.
23250              * @param {HtmlEditor} this
23251              * @param {String} html
23252              */
23253             sync: true,
23254              /**
23255              * @event push
23256              * Fires when the iframe editor is updated with content from the textarea.
23257              * @param {HtmlEditor} this
23258              * @param {String} html
23259              */
23260             push: true,
23261              /**
23262              * @event editmodechange
23263              * Fires when the editor switches edit modes
23264              * @param {HtmlEditor} this
23265              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23266              */
23267             editmodechange: true,
23268             /**
23269              * @event editorevent
23270              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23271              * @param {HtmlEditor} this
23272              */
23273             editorevent: true,
23274             /**
23275              * @event firstfocus
23276              * Fires when on first focus - needed by toolbars..
23277              * @param {HtmlEditor} this
23278              */
23279             firstfocus: true,
23280             /**
23281              * @event autosave
23282              * Auto save the htmlEditor value as a file into Events
23283              * @param {HtmlEditor} this
23284              */
23285             autosave: true,
23286             /**
23287              * @event savedpreview
23288              * preview the saved version of htmlEditor
23289              * @param {HtmlEditor} this
23290              */
23291             savedpreview: true
23292         });
23293 };
23294
23295
23296 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23297     
23298     
23299       /**
23300      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23301      */
23302     toolbars : false,
23303     
23304      /**
23305     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23306     */
23307     btns : [],
23308    
23309      /**
23310      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23311      *                        Roo.resizable.
23312      */
23313     resizable : false,
23314      /**
23315      * @cfg {Number} height (in pixels)
23316      */   
23317     height: 300,
23318    /**
23319      * @cfg {Number} width (in pixels)
23320      */   
23321     width: false,
23322     
23323     /**
23324      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23325      * 
23326      */
23327     stylesheets: false,
23328     
23329     // id of frame..
23330     frameId: false,
23331     
23332     // private properties
23333     validationEvent : false,
23334     deferHeight: true,
23335     initialized : false,
23336     activated : false,
23337     
23338     onFocus : Roo.emptyFn,
23339     iframePad:3,
23340     hideMode:'offsets',
23341     
23342     tbContainer : false,
23343     
23344     bodyCls : '',
23345     
23346     toolbarContainer :function() {
23347         return this.wrap.select('.x-html-editor-tb',true).first();
23348     },
23349
23350     /**
23351      * Protected method that will not generally be called directly. It
23352      * is called when the editor creates its toolbar. Override this method if you need to
23353      * add custom toolbar buttons.
23354      * @param {HtmlEditor} editor
23355      */
23356     createToolbar : function(){
23357         Roo.log('renewing');
23358         Roo.log("create toolbars");
23359         
23360         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23361         this.toolbars[0].render(this.toolbarContainer());
23362         
23363         return;
23364         
23365 //        if (!editor.toolbars || !editor.toolbars.length) {
23366 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23367 //        }
23368 //        
23369 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23370 //            editor.toolbars[i] = Roo.factory(
23371 //                    typeof(editor.toolbars[i]) == 'string' ?
23372 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23373 //                Roo.bootstrap.HtmlEditor);
23374 //            editor.toolbars[i].init(editor);
23375 //        }
23376     },
23377
23378      
23379     // private
23380     onRender : function(ct, position)
23381     {
23382        // Roo.log("Call onRender: " + this.xtype);
23383         var _t = this;
23384         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23385       
23386         this.wrap = this.inputEl().wrap({
23387             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23388         });
23389         
23390         this.editorcore.onRender(ct, position);
23391          
23392         if (this.resizable) {
23393             this.resizeEl = new Roo.Resizable(this.wrap, {
23394                 pinned : true,
23395                 wrap: true,
23396                 dynamic : true,
23397                 minHeight : this.height,
23398                 height: this.height,
23399                 handles : this.resizable,
23400                 width: this.width,
23401                 listeners : {
23402                     resize : function(r, w, h) {
23403                         _t.onResize(w,h); // -something
23404                     }
23405                 }
23406             });
23407             
23408         }
23409         this.createToolbar(this);
23410        
23411         
23412         if(!this.width && this.resizable){
23413             this.setSize(this.wrap.getSize());
23414         }
23415         if (this.resizeEl) {
23416             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23417             // should trigger onReize..
23418         }
23419         
23420     },
23421
23422     // private
23423     onResize : function(w, h)
23424     {
23425         Roo.log('resize: ' +w + ',' + h );
23426         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23427         var ew = false;
23428         var eh = false;
23429         
23430         if(this.inputEl() ){
23431             if(typeof w == 'number'){
23432                 var aw = w - this.wrap.getFrameWidth('lr');
23433                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23434                 ew = aw;
23435             }
23436             if(typeof h == 'number'){
23437                  var tbh = -11;  // fixme it needs to tool bar size!
23438                 for (var i =0; i < this.toolbars.length;i++) {
23439                     // fixme - ask toolbars for heights?
23440                     tbh += this.toolbars[i].el.getHeight();
23441                     //if (this.toolbars[i].footer) {
23442                     //    tbh += this.toolbars[i].footer.el.getHeight();
23443                     //}
23444                 }
23445               
23446                 
23447                 
23448                 
23449                 
23450                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23451                 ah -= 5; // knock a few pixes off for look..
23452                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23453                 var eh = ah;
23454             }
23455         }
23456         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23457         this.editorcore.onResize(ew,eh);
23458         
23459     },
23460
23461     /**
23462      * Toggles the editor between standard and source edit mode.
23463      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23464      */
23465     toggleSourceEdit : function(sourceEditMode)
23466     {
23467         this.editorcore.toggleSourceEdit(sourceEditMode);
23468         
23469         if(this.editorcore.sourceEditMode){
23470             Roo.log('editor - showing textarea');
23471             
23472 //            Roo.log('in');
23473 //            Roo.log(this.syncValue());
23474             this.syncValue();
23475             this.inputEl().removeClass(['hide', 'x-hidden']);
23476             this.inputEl().dom.removeAttribute('tabIndex');
23477             this.inputEl().focus();
23478         }else{
23479             Roo.log('editor - hiding textarea');
23480 //            Roo.log('out')
23481 //            Roo.log(this.pushValue()); 
23482             this.pushValue();
23483             
23484             this.inputEl().addClass(['hide', 'x-hidden']);
23485             this.inputEl().dom.setAttribute('tabIndex', -1);
23486             //this.deferFocus();
23487         }
23488          
23489         if(this.resizable){
23490             this.setSize(this.wrap.getSize());
23491         }
23492         
23493         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23494     },
23495  
23496     // private (for BoxComponent)
23497     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23498
23499     // private (for BoxComponent)
23500     getResizeEl : function(){
23501         return this.wrap;
23502     },
23503
23504     // private (for BoxComponent)
23505     getPositionEl : function(){
23506         return this.wrap;
23507     },
23508
23509     // private
23510     initEvents : function(){
23511         this.originalValue = this.getValue();
23512     },
23513
23514 //    /**
23515 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23516 //     * @method
23517 //     */
23518 //    markInvalid : Roo.emptyFn,
23519 //    /**
23520 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23521 //     * @method
23522 //     */
23523 //    clearInvalid : Roo.emptyFn,
23524
23525     setValue : function(v){
23526         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23527         this.editorcore.pushValue();
23528     },
23529
23530      
23531     // private
23532     deferFocus : function(){
23533         this.focus.defer(10, this);
23534     },
23535
23536     // doc'ed in Field
23537     focus : function(){
23538         this.editorcore.focus();
23539         
23540     },
23541       
23542
23543     // private
23544     onDestroy : function(){
23545         
23546         
23547         
23548         if(this.rendered){
23549             
23550             for (var i =0; i < this.toolbars.length;i++) {
23551                 // fixme - ask toolbars for heights?
23552                 this.toolbars[i].onDestroy();
23553             }
23554             
23555             this.wrap.dom.innerHTML = '';
23556             this.wrap.remove();
23557         }
23558     },
23559
23560     // private
23561     onFirstFocus : function(){
23562         //Roo.log("onFirstFocus");
23563         this.editorcore.onFirstFocus();
23564          for (var i =0; i < this.toolbars.length;i++) {
23565             this.toolbars[i].onFirstFocus();
23566         }
23567         
23568     },
23569     
23570     // private
23571     syncValue : function()
23572     {   
23573         this.editorcore.syncValue();
23574     },
23575     
23576     pushValue : function()
23577     {   
23578         this.editorcore.pushValue();
23579     }
23580      
23581     
23582     // hide stuff that is not compatible
23583     /**
23584      * @event blur
23585      * @hide
23586      */
23587     /**
23588      * @event change
23589      * @hide
23590      */
23591     /**
23592      * @event focus
23593      * @hide
23594      */
23595     /**
23596      * @event specialkey
23597      * @hide
23598      */
23599     /**
23600      * @cfg {String} fieldClass @hide
23601      */
23602     /**
23603      * @cfg {String} focusClass @hide
23604      */
23605     /**
23606      * @cfg {String} autoCreate @hide
23607      */
23608     /**
23609      * @cfg {String} inputType @hide
23610      */
23611     /**
23612      * @cfg {String} invalidClass @hide
23613      */
23614     /**
23615      * @cfg {String} invalidText @hide
23616      */
23617     /**
23618      * @cfg {String} msgFx @hide
23619      */
23620     /**
23621      * @cfg {String} validateOnBlur @hide
23622      */
23623 });
23624  
23625     
23626    
23627    
23628    
23629       
23630 Roo.namespace('Roo.bootstrap.htmleditor');
23631 /**
23632  * @class Roo.bootstrap.HtmlEditorToolbar1
23633  * Basic Toolbar
23634  * 
23635  * Usage:
23636  *
23637  new Roo.bootstrap.HtmlEditor({
23638     ....
23639     toolbars : [
23640         new Roo.bootstrap.HtmlEditorToolbar1({
23641             disable : { fonts: 1 , format: 1, ..., ... , ...],
23642             btns : [ .... ]
23643         })
23644     }
23645      
23646  * 
23647  * @cfg {Object} disable List of elements to disable..
23648  * @cfg {Array} btns List of additional buttons.
23649  * 
23650  * 
23651  * NEEDS Extra CSS? 
23652  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23653  */
23654  
23655 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23656 {
23657     
23658     Roo.apply(this, config);
23659     
23660     // default disabled, based on 'good practice'..
23661     this.disable = this.disable || {};
23662     Roo.applyIf(this.disable, {
23663         fontSize : true,
23664         colors : true,
23665         specialElements : true
23666     });
23667     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23668     
23669     this.editor = config.editor;
23670     this.editorcore = config.editor.editorcore;
23671     
23672     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23673     
23674     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23675     // dont call parent... till later.
23676 }
23677 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23678      
23679     bar : true,
23680     
23681     editor : false,
23682     editorcore : false,
23683     
23684     
23685     formats : [
23686         "p" ,  
23687         "h1","h2","h3","h4","h5","h6", 
23688         "pre", "code", 
23689         "abbr", "acronym", "address", "cite", "samp", "var",
23690         'div','span'
23691     ],
23692     
23693     onRender : function(ct, position)
23694     {
23695        // Roo.log("Call onRender: " + this.xtype);
23696         
23697        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23698        Roo.log(this.el);
23699        this.el.dom.style.marginBottom = '0';
23700        var _this = this;
23701        var editorcore = this.editorcore;
23702        var editor= this.editor;
23703        
23704        var children = [];
23705        var btn = function(id,cmd , toggle, handler, html){
23706        
23707             var  event = toggle ? 'toggle' : 'click';
23708        
23709             var a = {
23710                 size : 'sm',
23711                 xtype: 'Button',
23712                 xns: Roo.bootstrap,
23713                 glyphicon : id,
23714                 cmd : id || cmd,
23715                 enableToggle:toggle !== false,
23716                 html : html || '',
23717                 pressed : toggle ? false : null,
23718                 listeners : {}
23719             };
23720             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23721                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23722             };
23723             children.push(a);
23724             return a;
23725        }
23726        
23727     //    var cb_box = function...
23728         
23729         var style = {
23730                 xtype: 'Button',
23731                 size : 'sm',
23732                 xns: Roo.bootstrap,
23733                 glyphicon : 'font',
23734                 //html : 'submit'
23735                 menu : {
23736                     xtype: 'Menu',
23737                     xns: Roo.bootstrap,
23738                     items:  []
23739                 }
23740         };
23741         Roo.each(this.formats, function(f) {
23742             style.menu.items.push({
23743                 xtype :'MenuItem',
23744                 xns: Roo.bootstrap,
23745                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23746                 tagname : f,
23747                 listeners : {
23748                     click : function()
23749                     {
23750                         editorcore.insertTag(this.tagname);
23751                         editor.focus();
23752                     }
23753                 }
23754                 
23755             });
23756         });
23757         children.push(style);   
23758         
23759         btn('bold',false,true);
23760         btn('italic',false,true);
23761         btn('align-left', 'justifyleft',true);
23762         btn('align-center', 'justifycenter',true);
23763         btn('align-right' , 'justifyright',true);
23764         btn('link', false, false, function(btn) {
23765             //Roo.log("create link?");
23766             var url = prompt(this.createLinkText, this.defaultLinkValue);
23767             if(url && url != 'http:/'+'/'){
23768                 this.editorcore.relayCmd('createlink', url);
23769             }
23770         }),
23771         btn('list','insertunorderedlist',true);
23772         btn('pencil', false,true, function(btn){
23773                 Roo.log(this);
23774                 this.toggleSourceEdit(btn.pressed);
23775         });
23776         
23777         if (this.editor.btns.length > 0) {
23778             for (var i = 0; i<this.editor.btns.length; i++) {
23779                 children.push(this.editor.btns[i]);
23780             }
23781         }
23782         
23783         /*
23784         var cog = {
23785                 xtype: 'Button',
23786                 size : 'sm',
23787                 xns: Roo.bootstrap,
23788                 glyphicon : 'cog',
23789                 //html : 'submit'
23790                 menu : {
23791                     xtype: 'Menu',
23792                     xns: Roo.bootstrap,
23793                     items:  []
23794                 }
23795         };
23796         
23797         cog.menu.items.push({
23798             xtype :'MenuItem',
23799             xns: Roo.bootstrap,
23800             html : Clean styles,
23801             tagname : f,
23802             listeners : {
23803                 click : function()
23804                 {
23805                     editorcore.insertTag(this.tagname);
23806                     editor.focus();
23807                 }
23808             }
23809             
23810         });
23811        */
23812         
23813          
23814        this.xtype = 'NavSimplebar';
23815         
23816         for(var i=0;i< children.length;i++) {
23817             
23818             this.buttons.add(this.addxtypeChild(children[i]));
23819             
23820         }
23821         
23822         editor.on('editorevent', this.updateToolbar, this);
23823     },
23824     onBtnClick : function(id)
23825     {
23826        this.editorcore.relayCmd(id);
23827        this.editorcore.focus();
23828     },
23829     
23830     /**
23831      * Protected method that will not generally be called directly. It triggers
23832      * a toolbar update by reading the markup state of the current selection in the editor.
23833      */
23834     updateToolbar: function(){
23835
23836         if(!this.editorcore.activated){
23837             this.editor.onFirstFocus(); // is this neeed?
23838             return;
23839         }
23840
23841         var btns = this.buttons; 
23842         var doc = this.editorcore.doc;
23843         btns.get('bold').setActive(doc.queryCommandState('bold'));
23844         btns.get('italic').setActive(doc.queryCommandState('italic'));
23845         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23846         
23847         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23848         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23849         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23850         
23851         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23852         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23853          /*
23854         
23855         var ans = this.editorcore.getAllAncestors();
23856         if (this.formatCombo) {
23857             
23858             
23859             var store = this.formatCombo.store;
23860             this.formatCombo.setValue("");
23861             for (var i =0; i < ans.length;i++) {
23862                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23863                     // select it..
23864                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23865                     break;
23866                 }
23867             }
23868         }
23869         
23870         
23871         
23872         // hides menus... - so this cant be on a menu...
23873         Roo.bootstrap.MenuMgr.hideAll();
23874         */
23875         Roo.bootstrap.MenuMgr.hideAll();
23876         //this.editorsyncValue();
23877     },
23878     onFirstFocus: function() {
23879         this.buttons.each(function(item){
23880            item.enable();
23881         });
23882     },
23883     toggleSourceEdit : function(sourceEditMode){
23884         
23885           
23886         if(sourceEditMode){
23887             Roo.log("disabling buttons");
23888            this.buttons.each( function(item){
23889                 if(item.cmd != 'pencil'){
23890                     item.disable();
23891                 }
23892             });
23893           
23894         }else{
23895             Roo.log("enabling buttons");
23896             if(this.editorcore.initialized){
23897                 this.buttons.each( function(item){
23898                     item.enable();
23899                 });
23900             }
23901             
23902         }
23903         Roo.log("calling toggole on editor");
23904         // tell the editor that it's been pressed..
23905         this.editor.toggleSourceEdit(sourceEditMode);
23906        
23907     }
23908 });
23909
23910
23911
23912
23913
23914 /**
23915  * @class Roo.bootstrap.Table.AbstractSelectionModel
23916  * @extends Roo.util.Observable
23917  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23918  * implemented by descendant classes.  This class should not be directly instantiated.
23919  * @constructor
23920  */
23921 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23922     this.locked = false;
23923     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23924 };
23925
23926
23927 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23928     /** @ignore Called by the grid automatically. Do not call directly. */
23929     init : function(grid){
23930         this.grid = grid;
23931         this.initEvents();
23932     },
23933
23934     /**
23935      * Locks the selections.
23936      */
23937     lock : function(){
23938         this.locked = true;
23939     },
23940
23941     /**
23942      * Unlocks the selections.
23943      */
23944     unlock : function(){
23945         this.locked = false;
23946     },
23947
23948     /**
23949      * Returns true if the selections are locked.
23950      * @return {Boolean}
23951      */
23952     isLocked : function(){
23953         return this.locked;
23954     }
23955 });
23956 /**
23957  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23958  * @class Roo.bootstrap.Table.RowSelectionModel
23959  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23960  * It supports multiple selections and keyboard selection/navigation. 
23961  * @constructor
23962  * @param {Object} config
23963  */
23964
23965 Roo.bootstrap.Table.RowSelectionModel = function(config){
23966     Roo.apply(this, config);
23967     this.selections = new Roo.util.MixedCollection(false, function(o){
23968         return o.id;
23969     });
23970
23971     this.last = false;
23972     this.lastActive = false;
23973
23974     this.addEvents({
23975         /**
23976              * @event selectionchange
23977              * Fires when the selection changes
23978              * @param {SelectionModel} this
23979              */
23980             "selectionchange" : true,
23981         /**
23982              * @event afterselectionchange
23983              * Fires after the selection changes (eg. by key press or clicking)
23984              * @param {SelectionModel} this
23985              */
23986             "afterselectionchange" : true,
23987         /**
23988              * @event beforerowselect
23989              * Fires when a row is selected being selected, return false to cancel.
23990              * @param {SelectionModel} this
23991              * @param {Number} rowIndex The selected index
23992              * @param {Boolean} keepExisting False if other selections will be cleared
23993              */
23994             "beforerowselect" : true,
23995         /**
23996              * @event rowselect
23997              * Fires when a row is selected.
23998              * @param {SelectionModel} this
23999              * @param {Number} rowIndex The selected index
24000              * @param {Roo.data.Record} r The record
24001              */
24002             "rowselect" : true,
24003         /**
24004              * @event rowdeselect
24005              * Fires when a row is deselected.
24006              * @param {SelectionModel} this
24007              * @param {Number} rowIndex The selected index
24008              */
24009         "rowdeselect" : true
24010     });
24011     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24012     this.locked = false;
24013  };
24014
24015 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24016     /**
24017      * @cfg {Boolean} singleSelect
24018      * True to allow selection of only one row at a time (defaults to false)
24019      */
24020     singleSelect : false,
24021
24022     // private
24023     initEvents : function()
24024     {
24025
24026         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24027         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24028         //}else{ // allow click to work like normal
24029          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24030         //}
24031         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24032         this.grid.on("rowclick", this.handleMouseDown, this);
24033         
24034         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24035             "up" : function(e){
24036                 if(!e.shiftKey){
24037                     this.selectPrevious(e.shiftKey);
24038                 }else if(this.last !== false && this.lastActive !== false){
24039                     var last = this.last;
24040                     this.selectRange(this.last,  this.lastActive-1);
24041                     this.grid.getView().focusRow(this.lastActive);
24042                     if(last !== false){
24043                         this.last = last;
24044                     }
24045                 }else{
24046                     this.selectFirstRow();
24047                 }
24048                 this.fireEvent("afterselectionchange", this);
24049             },
24050             "down" : function(e){
24051                 if(!e.shiftKey){
24052                     this.selectNext(e.shiftKey);
24053                 }else if(this.last !== false && this.lastActive !== false){
24054                     var last = this.last;
24055                     this.selectRange(this.last,  this.lastActive+1);
24056                     this.grid.getView().focusRow(this.lastActive);
24057                     if(last !== false){
24058                         this.last = last;
24059                     }
24060                 }else{
24061                     this.selectFirstRow();
24062                 }
24063                 this.fireEvent("afterselectionchange", this);
24064             },
24065             scope: this
24066         });
24067         this.grid.store.on('load', function(){
24068             this.selections.clear();
24069         },this);
24070         /*
24071         var view = this.grid.view;
24072         view.on("refresh", this.onRefresh, this);
24073         view.on("rowupdated", this.onRowUpdated, this);
24074         view.on("rowremoved", this.onRemove, this);
24075         */
24076     },
24077
24078     // private
24079     onRefresh : function()
24080     {
24081         var ds = this.grid.store, i, v = this.grid.view;
24082         var s = this.selections;
24083         s.each(function(r){
24084             if((i = ds.indexOfId(r.id)) != -1){
24085                 v.onRowSelect(i);
24086             }else{
24087                 s.remove(r);
24088             }
24089         });
24090     },
24091
24092     // private
24093     onRemove : function(v, index, r){
24094         this.selections.remove(r);
24095     },
24096
24097     // private
24098     onRowUpdated : function(v, index, r){
24099         if(this.isSelected(r)){
24100             v.onRowSelect(index);
24101         }
24102     },
24103
24104     /**
24105      * Select records.
24106      * @param {Array} records The records to select
24107      * @param {Boolean} keepExisting (optional) True to keep existing selections
24108      */
24109     selectRecords : function(records, keepExisting)
24110     {
24111         if(!keepExisting){
24112             this.clearSelections();
24113         }
24114             var ds = this.grid.store;
24115         for(var i = 0, len = records.length; i < len; i++){
24116             this.selectRow(ds.indexOf(records[i]), true);
24117         }
24118     },
24119
24120     /**
24121      * Gets the number of selected rows.
24122      * @return {Number}
24123      */
24124     getCount : function(){
24125         return this.selections.length;
24126     },
24127
24128     /**
24129      * Selects the first row in the grid.
24130      */
24131     selectFirstRow : function(){
24132         this.selectRow(0);
24133     },
24134
24135     /**
24136      * Select the last row.
24137      * @param {Boolean} keepExisting (optional) True to keep existing selections
24138      */
24139     selectLastRow : function(keepExisting){
24140         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24141         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24142     },
24143
24144     /**
24145      * Selects the row immediately following the last selected row.
24146      * @param {Boolean} keepExisting (optional) True to keep existing selections
24147      */
24148     selectNext : function(keepExisting)
24149     {
24150             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24151             this.selectRow(this.last+1, keepExisting);
24152             this.grid.getView().focusRow(this.last);
24153         }
24154     },
24155
24156     /**
24157      * Selects the row that precedes the last selected row.
24158      * @param {Boolean} keepExisting (optional) True to keep existing selections
24159      */
24160     selectPrevious : function(keepExisting){
24161         if(this.last){
24162             this.selectRow(this.last-1, keepExisting);
24163             this.grid.getView().focusRow(this.last);
24164         }
24165     },
24166
24167     /**
24168      * Returns the selected records
24169      * @return {Array} Array of selected records
24170      */
24171     getSelections : function(){
24172         return [].concat(this.selections.items);
24173     },
24174
24175     /**
24176      * Returns the first selected record.
24177      * @return {Record}
24178      */
24179     getSelected : function(){
24180         return this.selections.itemAt(0);
24181     },
24182
24183
24184     /**
24185      * Clears all selections.
24186      */
24187     clearSelections : function(fast)
24188     {
24189         if(this.locked) {
24190             return;
24191         }
24192         if(fast !== true){
24193                 var ds = this.grid.store;
24194             var s = this.selections;
24195             s.each(function(r){
24196                 this.deselectRow(ds.indexOfId(r.id));
24197             }, this);
24198             s.clear();
24199         }else{
24200             this.selections.clear();
24201         }
24202         this.last = false;
24203     },
24204
24205
24206     /**
24207      * Selects all rows.
24208      */
24209     selectAll : function(){
24210         if(this.locked) {
24211             return;
24212         }
24213         this.selections.clear();
24214         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24215             this.selectRow(i, true);
24216         }
24217     },
24218
24219     /**
24220      * Returns True if there is a selection.
24221      * @return {Boolean}
24222      */
24223     hasSelection : function(){
24224         return this.selections.length > 0;
24225     },
24226
24227     /**
24228      * Returns True if the specified row is selected.
24229      * @param {Number/Record} record The record or index of the record to check
24230      * @return {Boolean}
24231      */
24232     isSelected : function(index){
24233             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24234         return (r && this.selections.key(r.id) ? true : false);
24235     },
24236
24237     /**
24238      * Returns True if the specified record id is selected.
24239      * @param {String} id The id of record to check
24240      * @return {Boolean}
24241      */
24242     isIdSelected : function(id){
24243         return (this.selections.key(id) ? true : false);
24244     },
24245
24246
24247     // private
24248     handleMouseDBClick : function(e, t){
24249         
24250     },
24251     // private
24252     handleMouseDown : function(e, t)
24253     {
24254             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24255         if(this.isLocked() || rowIndex < 0 ){
24256             return;
24257         };
24258         if(e.shiftKey && this.last !== false){
24259             var last = this.last;
24260             this.selectRange(last, rowIndex, e.ctrlKey);
24261             this.last = last; // reset the last
24262             t.focus();
24263     
24264         }else{
24265             var isSelected = this.isSelected(rowIndex);
24266             //Roo.log("select row:" + rowIndex);
24267             if(isSelected){
24268                 this.deselectRow(rowIndex);
24269             } else {
24270                         this.selectRow(rowIndex, true);
24271             }
24272     
24273             /*
24274                 if(e.button !== 0 && isSelected){
24275                 alert('rowIndex 2: ' + rowIndex);
24276                     view.focusRow(rowIndex);
24277                 }else if(e.ctrlKey && isSelected){
24278                     this.deselectRow(rowIndex);
24279                 }else if(!isSelected){
24280                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24281                     view.focusRow(rowIndex);
24282                 }
24283             */
24284         }
24285         this.fireEvent("afterselectionchange", this);
24286     },
24287     // private
24288     handleDragableRowClick :  function(grid, rowIndex, e) 
24289     {
24290         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24291             this.selectRow(rowIndex, false);
24292             grid.view.focusRow(rowIndex);
24293              this.fireEvent("afterselectionchange", this);
24294         }
24295     },
24296     
24297     /**
24298      * Selects multiple rows.
24299      * @param {Array} rows Array of the indexes of the row to select
24300      * @param {Boolean} keepExisting (optional) True to keep existing selections
24301      */
24302     selectRows : function(rows, keepExisting){
24303         if(!keepExisting){
24304             this.clearSelections();
24305         }
24306         for(var i = 0, len = rows.length; i < len; i++){
24307             this.selectRow(rows[i], true);
24308         }
24309     },
24310
24311     /**
24312      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24313      * @param {Number} startRow The index of the first row in the range
24314      * @param {Number} endRow The index of the last row in the range
24315      * @param {Boolean} keepExisting (optional) True to retain existing selections
24316      */
24317     selectRange : function(startRow, endRow, keepExisting){
24318         if(this.locked) {
24319             return;
24320         }
24321         if(!keepExisting){
24322             this.clearSelections();
24323         }
24324         if(startRow <= endRow){
24325             for(var i = startRow; i <= endRow; i++){
24326                 this.selectRow(i, true);
24327             }
24328         }else{
24329             for(var i = startRow; i >= endRow; i--){
24330                 this.selectRow(i, true);
24331             }
24332         }
24333     },
24334
24335     /**
24336      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24337      * @param {Number} startRow The index of the first row in the range
24338      * @param {Number} endRow The index of the last row in the range
24339      */
24340     deselectRange : function(startRow, endRow, preventViewNotify){
24341         if(this.locked) {
24342             return;
24343         }
24344         for(var i = startRow; i <= endRow; i++){
24345             this.deselectRow(i, preventViewNotify);
24346         }
24347     },
24348
24349     /**
24350      * Selects a row.
24351      * @param {Number} row The index of the row to select
24352      * @param {Boolean} keepExisting (optional) True to keep existing selections
24353      */
24354     selectRow : function(index, keepExisting, preventViewNotify)
24355     {
24356             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24357             return;
24358         }
24359         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24360             if(!keepExisting || this.singleSelect){
24361                 this.clearSelections();
24362             }
24363             
24364             var r = this.grid.store.getAt(index);
24365             //console.log('selectRow - record id :' + r.id);
24366             
24367             this.selections.add(r);
24368             this.last = this.lastActive = index;
24369             if(!preventViewNotify){
24370                 var proxy = new Roo.Element(
24371                                 this.grid.getRowDom(index)
24372                 );
24373                 proxy.addClass('bg-info info');
24374             }
24375             this.fireEvent("rowselect", this, index, r);
24376             this.fireEvent("selectionchange", this);
24377         }
24378     },
24379
24380     /**
24381      * Deselects a row.
24382      * @param {Number} row The index of the row to deselect
24383      */
24384     deselectRow : function(index, preventViewNotify)
24385     {
24386         if(this.locked) {
24387             return;
24388         }
24389         if(this.last == index){
24390             this.last = false;
24391         }
24392         if(this.lastActive == index){
24393             this.lastActive = false;
24394         }
24395         
24396         var r = this.grid.store.getAt(index);
24397         if (!r) {
24398             return;
24399         }
24400         
24401         this.selections.remove(r);
24402         //.console.log('deselectRow - record id :' + r.id);
24403         if(!preventViewNotify){
24404         
24405             var proxy = new Roo.Element(
24406                 this.grid.getRowDom(index)
24407             );
24408             proxy.removeClass('bg-info info');
24409         }
24410         this.fireEvent("rowdeselect", this, index);
24411         this.fireEvent("selectionchange", this);
24412     },
24413
24414     // private
24415     restoreLast : function(){
24416         if(this._last){
24417             this.last = this._last;
24418         }
24419     },
24420
24421     // private
24422     acceptsNav : function(row, col, cm){
24423         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24424     },
24425
24426     // private
24427     onEditorKey : function(field, e){
24428         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24429         if(k == e.TAB){
24430             e.stopEvent();
24431             ed.completeEdit();
24432             if(e.shiftKey){
24433                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24434             }else{
24435                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24436             }
24437         }else if(k == e.ENTER && !e.ctrlKey){
24438             e.stopEvent();
24439             ed.completeEdit();
24440             if(e.shiftKey){
24441                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24442             }else{
24443                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24444             }
24445         }else if(k == e.ESC){
24446             ed.cancelEdit();
24447         }
24448         if(newCell){
24449             g.startEditing(newCell[0], newCell[1]);
24450         }
24451     }
24452 });
24453 /*
24454  * Based on:
24455  * Ext JS Library 1.1.1
24456  * Copyright(c) 2006-2007, Ext JS, LLC.
24457  *
24458  * Originally Released Under LGPL - original licence link has changed is not relivant.
24459  *
24460  * Fork - LGPL
24461  * <script type="text/javascript">
24462  */
24463  
24464 /**
24465  * @class Roo.bootstrap.PagingToolbar
24466  * @extends Roo.bootstrap.NavSimplebar
24467  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24468  * @constructor
24469  * Create a new PagingToolbar
24470  * @param {Object} config The config object
24471  * @param {Roo.data.Store} store
24472  */
24473 Roo.bootstrap.PagingToolbar = function(config)
24474 {
24475     // old args format still supported... - xtype is prefered..
24476         // created from xtype...
24477     
24478     this.ds = config.dataSource;
24479     
24480     if (config.store && !this.ds) {
24481         this.store= Roo.factory(config.store, Roo.data);
24482         this.ds = this.store;
24483         this.ds.xmodule = this.xmodule || false;
24484     }
24485     
24486     this.toolbarItems = [];
24487     if (config.items) {
24488         this.toolbarItems = config.items;
24489     }
24490     
24491     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24492     
24493     this.cursor = 0;
24494     
24495     if (this.ds) { 
24496         this.bind(this.ds);
24497     }
24498     
24499     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24500     
24501 };
24502
24503 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24504     /**
24505      * @cfg {Roo.data.Store} dataSource
24506      * The underlying data store providing the paged data
24507      */
24508     /**
24509      * @cfg {String/HTMLElement/Element} container
24510      * container The id or element that will contain the toolbar
24511      */
24512     /**
24513      * @cfg {Boolean} displayInfo
24514      * True to display the displayMsg (defaults to false)
24515      */
24516     /**
24517      * @cfg {Number} pageSize
24518      * The number of records to display per page (defaults to 20)
24519      */
24520     pageSize: 20,
24521     /**
24522      * @cfg {String} displayMsg
24523      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24524      */
24525     displayMsg : 'Displaying {0} - {1} of {2}',
24526     /**
24527      * @cfg {String} emptyMsg
24528      * The message to display when no records are found (defaults to "No data to display")
24529      */
24530     emptyMsg : 'No data to display',
24531     /**
24532      * Customizable piece of the default paging text (defaults to "Page")
24533      * @type String
24534      */
24535     beforePageText : "Page",
24536     /**
24537      * Customizable piece of the default paging text (defaults to "of %0")
24538      * @type String
24539      */
24540     afterPageText : "of {0}",
24541     /**
24542      * Customizable piece of the default paging text (defaults to "First Page")
24543      * @type String
24544      */
24545     firstText : "First Page",
24546     /**
24547      * Customizable piece of the default paging text (defaults to "Previous Page")
24548      * @type String
24549      */
24550     prevText : "Previous Page",
24551     /**
24552      * Customizable piece of the default paging text (defaults to "Next Page")
24553      * @type String
24554      */
24555     nextText : "Next Page",
24556     /**
24557      * Customizable piece of the default paging text (defaults to "Last Page")
24558      * @type String
24559      */
24560     lastText : "Last Page",
24561     /**
24562      * Customizable piece of the default paging text (defaults to "Refresh")
24563      * @type String
24564      */
24565     refreshText : "Refresh",
24566
24567     buttons : false,
24568     // private
24569     onRender : function(ct, position) 
24570     {
24571         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24572         this.navgroup.parentId = this.id;
24573         this.navgroup.onRender(this.el, null);
24574         // add the buttons to the navgroup
24575         
24576         if(this.displayInfo){
24577             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24578             this.displayEl = this.el.select('.x-paging-info', true).first();
24579 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24580 //            this.displayEl = navel.el.select('span',true).first();
24581         }
24582         
24583         var _this = this;
24584         
24585         if(this.buttons){
24586             Roo.each(_this.buttons, function(e){ // this might need to use render????
24587                Roo.factory(e).render(_this.el);
24588             });
24589         }
24590             
24591         Roo.each(_this.toolbarItems, function(e) {
24592             _this.navgroup.addItem(e);
24593         });
24594         
24595         
24596         this.first = this.navgroup.addItem({
24597             tooltip: this.firstText,
24598             cls: "prev",
24599             icon : 'fa fa-backward',
24600             disabled: true,
24601             preventDefault: true,
24602             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24603         });
24604         
24605         this.prev =  this.navgroup.addItem({
24606             tooltip: this.prevText,
24607             cls: "prev",
24608             icon : 'fa fa-step-backward',
24609             disabled: true,
24610             preventDefault: true,
24611             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24612         });
24613     //this.addSeparator();
24614         
24615         
24616         var field = this.navgroup.addItem( {
24617             tagtype : 'span',
24618             cls : 'x-paging-position',
24619             
24620             html : this.beforePageText  +
24621                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24622                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24623          } ); //?? escaped?
24624         
24625         this.field = field.el.select('input', true).first();
24626         this.field.on("keydown", this.onPagingKeydown, this);
24627         this.field.on("focus", function(){this.dom.select();});
24628     
24629     
24630         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24631         //this.field.setHeight(18);
24632         //this.addSeparator();
24633         this.next = this.navgroup.addItem({
24634             tooltip: this.nextText,
24635             cls: "next",
24636             html : ' <i class="fa fa-step-forward">',
24637             disabled: true,
24638             preventDefault: true,
24639             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24640         });
24641         this.last = this.navgroup.addItem({
24642             tooltip: this.lastText,
24643             icon : 'fa fa-forward',
24644             cls: "next",
24645             disabled: true,
24646             preventDefault: true,
24647             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24648         });
24649     //this.addSeparator();
24650         this.loading = this.navgroup.addItem({
24651             tooltip: this.refreshText,
24652             icon: 'fa fa-refresh',
24653             preventDefault: true,
24654             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24655         });
24656         
24657     },
24658
24659     // private
24660     updateInfo : function(){
24661         if(this.displayEl){
24662             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24663             var msg = count == 0 ?
24664                 this.emptyMsg :
24665                 String.format(
24666                     this.displayMsg,
24667                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24668                 );
24669             this.displayEl.update(msg);
24670         }
24671     },
24672
24673     // private
24674     onLoad : function(ds, r, o)
24675     {
24676         this.cursor = o.params.start ? o.params.start : 0;
24677         
24678         var d = this.getPageData(),
24679             ap = d.activePage,
24680             ps = d.pages;
24681         
24682         
24683         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24684         this.field.dom.value = ap;
24685         this.first.setDisabled(ap == 1);
24686         this.prev.setDisabled(ap == 1);
24687         this.next.setDisabled(ap == ps);
24688         this.last.setDisabled(ap == ps);
24689         this.loading.enable();
24690         this.updateInfo();
24691     },
24692
24693     // private
24694     getPageData : function(){
24695         var total = this.ds.getTotalCount();
24696         return {
24697             total : total,
24698             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24699             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24700         };
24701     },
24702
24703     // private
24704     onLoadError : function(){
24705         this.loading.enable();
24706     },
24707
24708     // private
24709     onPagingKeydown : function(e){
24710         var k = e.getKey();
24711         var d = this.getPageData();
24712         if(k == e.RETURN){
24713             var v = this.field.dom.value, pageNum;
24714             if(!v || isNaN(pageNum = parseInt(v, 10))){
24715                 this.field.dom.value = d.activePage;
24716                 return;
24717             }
24718             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24719             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24720             e.stopEvent();
24721         }
24722         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))
24723         {
24724           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24725           this.field.dom.value = pageNum;
24726           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24727           e.stopEvent();
24728         }
24729         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24730         {
24731           var v = this.field.dom.value, pageNum; 
24732           var increment = (e.shiftKey) ? 10 : 1;
24733           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24734                 increment *= -1;
24735           }
24736           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24737             this.field.dom.value = d.activePage;
24738             return;
24739           }
24740           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24741           {
24742             this.field.dom.value = parseInt(v, 10) + increment;
24743             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24744             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24745           }
24746           e.stopEvent();
24747         }
24748     },
24749
24750     // private
24751     beforeLoad : function(){
24752         if(this.loading){
24753             this.loading.disable();
24754         }
24755     },
24756
24757     // private
24758     onClick : function(which){
24759         
24760         var ds = this.ds;
24761         if (!ds) {
24762             return;
24763         }
24764         
24765         switch(which){
24766             case "first":
24767                 ds.load({params:{start: 0, limit: this.pageSize}});
24768             break;
24769             case "prev":
24770                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24771             break;
24772             case "next":
24773                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24774             break;
24775             case "last":
24776                 var total = ds.getTotalCount();
24777                 var extra = total % this.pageSize;
24778                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24779                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24780             break;
24781             case "refresh":
24782                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24783             break;
24784         }
24785     },
24786
24787     /**
24788      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24789      * @param {Roo.data.Store} store The data store to unbind
24790      */
24791     unbind : function(ds){
24792         ds.un("beforeload", this.beforeLoad, this);
24793         ds.un("load", this.onLoad, this);
24794         ds.un("loadexception", this.onLoadError, this);
24795         ds.un("remove", this.updateInfo, this);
24796         ds.un("add", this.updateInfo, this);
24797         this.ds = undefined;
24798     },
24799
24800     /**
24801      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24802      * @param {Roo.data.Store} store The data store to bind
24803      */
24804     bind : function(ds){
24805         ds.on("beforeload", this.beforeLoad, this);
24806         ds.on("load", this.onLoad, this);
24807         ds.on("loadexception", this.onLoadError, this);
24808         ds.on("remove", this.updateInfo, this);
24809         ds.on("add", this.updateInfo, this);
24810         this.ds = ds;
24811     }
24812 });/*
24813  * - LGPL
24814  *
24815  * element
24816  * 
24817  */
24818
24819 /**
24820  * @class Roo.bootstrap.MessageBar
24821  * @extends Roo.bootstrap.Component
24822  * Bootstrap MessageBar class
24823  * @cfg {String} html contents of the MessageBar
24824  * @cfg {String} weight (info | success | warning | danger) default info
24825  * @cfg {String} beforeClass insert the bar before the given class
24826  * @cfg {Boolean} closable (true | false) default false
24827  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24828  * 
24829  * @constructor
24830  * Create a new Element
24831  * @param {Object} config The config object
24832  */
24833
24834 Roo.bootstrap.MessageBar = function(config){
24835     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24836 };
24837
24838 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24839     
24840     html: '',
24841     weight: 'info',
24842     closable: false,
24843     fixed: false,
24844     beforeClass: 'bootstrap-sticky-wrap',
24845     
24846     getAutoCreate : function(){
24847         
24848         var cfg = {
24849             tag: 'div',
24850             cls: 'alert alert-dismissable alert-' + this.weight,
24851             cn: [
24852                 {
24853                     tag: 'span',
24854                     cls: 'message',
24855                     html: this.html || ''
24856                 }
24857             ]
24858         };
24859         
24860         if(this.fixed){
24861             cfg.cls += ' alert-messages-fixed';
24862         }
24863         
24864         if(this.closable){
24865             cfg.cn.push({
24866                 tag: 'button',
24867                 cls: 'close',
24868                 html: 'x'
24869             });
24870         }
24871         
24872         return cfg;
24873     },
24874     
24875     onRender : function(ct, position)
24876     {
24877         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24878         
24879         if(!this.el){
24880             var cfg = Roo.apply({},  this.getAutoCreate());
24881             cfg.id = Roo.id();
24882             
24883             if (this.cls) {
24884                 cfg.cls += ' ' + this.cls;
24885             }
24886             if (this.style) {
24887                 cfg.style = this.style;
24888             }
24889             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24890             
24891             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24892         }
24893         
24894         this.el.select('>button.close').on('click', this.hide, this);
24895         
24896     },
24897     
24898     show : function()
24899     {
24900         if (!this.rendered) {
24901             this.render();
24902         }
24903         
24904         this.el.show();
24905         
24906         this.fireEvent('show', this);
24907         
24908     },
24909     
24910     hide : function()
24911     {
24912         if (!this.rendered) {
24913             this.render();
24914         }
24915         
24916         this.el.hide();
24917         
24918         this.fireEvent('hide', this);
24919     },
24920     
24921     update : function()
24922     {
24923 //        var e = this.el.dom.firstChild;
24924 //        
24925 //        if(this.closable){
24926 //            e = e.nextSibling;
24927 //        }
24928 //        
24929 //        e.data = this.html || '';
24930
24931         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24932     }
24933    
24934 });
24935
24936  
24937
24938      /*
24939  * - LGPL
24940  *
24941  * Graph
24942  * 
24943  */
24944
24945
24946 /**
24947  * @class Roo.bootstrap.Graph
24948  * @extends Roo.bootstrap.Component
24949  * Bootstrap Graph class
24950 > Prameters
24951  -sm {number} sm 4
24952  -md {number} md 5
24953  @cfg {String} graphtype  bar | vbar | pie
24954  @cfg {number} g_x coodinator | centre x (pie)
24955  @cfg {number} g_y coodinator | centre y (pie)
24956  @cfg {number} g_r radius (pie)
24957  @cfg {number} g_height height of the chart (respected by all elements in the set)
24958  @cfg {number} g_width width of the chart (respected by all elements in the set)
24959  @cfg {Object} title The title of the chart
24960     
24961  -{Array}  values
24962  -opts (object) options for the chart 
24963      o {
24964      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24965      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24966      o vgutter (number)
24967      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.
24968      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24969      o to
24970      o stretch (boolean)
24971      o }
24972  -opts (object) options for the pie
24973      o{
24974      o cut
24975      o startAngle (number)
24976      o endAngle (number)
24977      } 
24978  *
24979  * @constructor
24980  * Create a new Input
24981  * @param {Object} config The config object
24982  */
24983
24984 Roo.bootstrap.Graph = function(config){
24985     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24986     
24987     this.addEvents({
24988         // img events
24989         /**
24990          * @event click
24991          * The img click event for the img.
24992          * @param {Roo.EventObject} e
24993          */
24994         "click" : true
24995     });
24996 };
24997
24998 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24999     
25000     sm: 4,
25001     md: 5,
25002     graphtype: 'bar',
25003     g_height: 250,
25004     g_width: 400,
25005     g_x: 50,
25006     g_y: 50,
25007     g_r: 30,
25008     opts:{
25009         //g_colors: this.colors,
25010         g_type: 'soft',
25011         g_gutter: '20%'
25012
25013     },
25014     title : false,
25015
25016     getAutoCreate : function(){
25017         
25018         var cfg = {
25019             tag: 'div',
25020             html : null
25021         };
25022         
25023         
25024         return  cfg;
25025     },
25026
25027     onRender : function(ct,position){
25028         
25029         
25030         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25031         
25032         if (typeof(Raphael) == 'undefined') {
25033             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25034             return;
25035         }
25036         
25037         this.raphael = Raphael(this.el.dom);
25038         
25039                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25040                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25041                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25042                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25043                 /*
25044                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25045                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25046                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25047                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25048                 
25049                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25050                 r.barchart(330, 10, 300, 220, data1);
25051                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25052                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25053                 */
25054                 
25055                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25056                 // r.barchart(30, 30, 560, 250,  xdata, {
25057                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25058                 //     axis : "0 0 1 1",
25059                 //     axisxlabels :  xdata
25060                 //     //yvalues : cols,
25061                    
25062                 // });
25063 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25064 //        
25065 //        this.load(null,xdata,{
25066 //                axis : "0 0 1 1",
25067 //                axisxlabels :  xdata
25068 //                });
25069
25070     },
25071
25072     load : function(graphtype,xdata,opts)
25073     {
25074         this.raphael.clear();
25075         if(!graphtype) {
25076             graphtype = this.graphtype;
25077         }
25078         if(!opts){
25079             opts = this.opts;
25080         }
25081         var r = this.raphael,
25082             fin = function () {
25083                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25084             },
25085             fout = function () {
25086                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25087             },
25088             pfin = function() {
25089                 this.sector.stop();
25090                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25091
25092                 if (this.label) {
25093                     this.label[0].stop();
25094                     this.label[0].attr({ r: 7.5 });
25095                     this.label[1].attr({ "font-weight": 800 });
25096                 }
25097             },
25098             pfout = function() {
25099                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25100
25101                 if (this.label) {
25102                     this.label[0].animate({ r: 5 }, 500, "bounce");
25103                     this.label[1].attr({ "font-weight": 400 });
25104                 }
25105             };
25106
25107         switch(graphtype){
25108             case 'bar':
25109                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25110                 break;
25111             case 'hbar':
25112                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25113                 break;
25114             case 'pie':
25115 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25116 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25117 //            
25118                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25119                 
25120                 break;
25121
25122         }
25123         
25124         if(this.title){
25125             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25126         }
25127         
25128     },
25129     
25130     setTitle: function(o)
25131     {
25132         this.title = o;
25133     },
25134     
25135     initEvents: function() {
25136         
25137         if(!this.href){
25138             this.el.on('click', this.onClick, this);
25139         }
25140     },
25141     
25142     onClick : function(e)
25143     {
25144         Roo.log('img onclick');
25145         this.fireEvent('click', this, e);
25146     }
25147    
25148 });
25149
25150  
25151 /*
25152  * - LGPL
25153  *
25154  * numberBox
25155  * 
25156  */
25157 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25158
25159 /**
25160  * @class Roo.bootstrap.dash.NumberBox
25161  * @extends Roo.bootstrap.Component
25162  * Bootstrap NumberBox class
25163  * @cfg {String} headline Box headline
25164  * @cfg {String} content Box content
25165  * @cfg {String} icon Box icon
25166  * @cfg {String} footer Footer text
25167  * @cfg {String} fhref Footer href
25168  * 
25169  * @constructor
25170  * Create a new NumberBox
25171  * @param {Object} config The config object
25172  */
25173
25174
25175 Roo.bootstrap.dash.NumberBox = function(config){
25176     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25177     
25178 };
25179
25180 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25181     
25182     headline : '',
25183     content : '',
25184     icon : '',
25185     footer : '',
25186     fhref : '',
25187     ficon : '',
25188     
25189     getAutoCreate : function(){
25190         
25191         var cfg = {
25192             tag : 'div',
25193             cls : 'small-box ',
25194             cn : [
25195                 {
25196                     tag : 'div',
25197                     cls : 'inner',
25198                     cn :[
25199                         {
25200                             tag : 'h3',
25201                             cls : 'roo-headline',
25202                             html : this.headline
25203                         },
25204                         {
25205                             tag : 'p',
25206                             cls : 'roo-content',
25207                             html : this.content
25208                         }
25209                     ]
25210                 }
25211             ]
25212         };
25213         
25214         if(this.icon){
25215             cfg.cn.push({
25216                 tag : 'div',
25217                 cls : 'icon',
25218                 cn :[
25219                     {
25220                         tag : 'i',
25221                         cls : 'ion ' + this.icon
25222                     }
25223                 ]
25224             });
25225         }
25226         
25227         if(this.footer){
25228             var footer = {
25229                 tag : 'a',
25230                 cls : 'small-box-footer',
25231                 href : this.fhref || '#',
25232                 html : this.footer
25233             };
25234             
25235             cfg.cn.push(footer);
25236             
25237         }
25238         
25239         return  cfg;
25240     },
25241
25242     onRender : function(ct,position){
25243         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25244
25245
25246        
25247                 
25248     },
25249
25250     setHeadline: function (value)
25251     {
25252         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25253     },
25254     
25255     setFooter: function (value, href)
25256     {
25257         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25258         
25259         if(href){
25260             this.el.select('a.small-box-footer',true).first().attr('href', href);
25261         }
25262         
25263     },
25264
25265     setContent: function (value)
25266     {
25267         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25268     },
25269
25270     initEvents: function() 
25271     {   
25272         
25273     }
25274     
25275 });
25276
25277  
25278 /*
25279  * - LGPL
25280  *
25281  * TabBox
25282  * 
25283  */
25284 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25285
25286 /**
25287  * @class Roo.bootstrap.dash.TabBox
25288  * @extends Roo.bootstrap.Component
25289  * Bootstrap TabBox class
25290  * @cfg {String} title Title of the TabBox
25291  * @cfg {String} icon Icon of the TabBox
25292  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25293  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25294  * 
25295  * @constructor
25296  * Create a new TabBox
25297  * @param {Object} config The config object
25298  */
25299
25300
25301 Roo.bootstrap.dash.TabBox = function(config){
25302     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25303     this.addEvents({
25304         // raw events
25305         /**
25306          * @event addpane
25307          * When a pane is added
25308          * @param {Roo.bootstrap.dash.TabPane} pane
25309          */
25310         "addpane" : true,
25311         /**
25312          * @event activatepane
25313          * When a pane is activated
25314          * @param {Roo.bootstrap.dash.TabPane} pane
25315          */
25316         "activatepane" : true
25317         
25318          
25319     });
25320     
25321     this.panes = [];
25322 };
25323
25324 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25325
25326     title : '',
25327     icon : false,
25328     showtabs : true,
25329     tabScrollable : false,
25330     
25331     getChildContainer : function()
25332     {
25333         return this.el.select('.tab-content', true).first();
25334     },
25335     
25336     getAutoCreate : function(){
25337         
25338         var header = {
25339             tag: 'li',
25340             cls: 'pull-left header',
25341             html: this.title,
25342             cn : []
25343         };
25344         
25345         if(this.icon){
25346             header.cn.push({
25347                 tag: 'i',
25348                 cls: 'fa ' + this.icon
25349             });
25350         }
25351         
25352         var h = {
25353             tag: 'ul',
25354             cls: 'nav nav-tabs pull-right',
25355             cn: [
25356                 header
25357             ]
25358         };
25359         
25360         if(this.tabScrollable){
25361             h = {
25362                 tag: 'div',
25363                 cls: 'tab-header',
25364                 cn: [
25365                     {
25366                         tag: 'ul',
25367                         cls: 'nav nav-tabs pull-right',
25368                         cn: [
25369                             header
25370                         ]
25371                     }
25372                 ]
25373             };
25374         }
25375         
25376         var cfg = {
25377             tag: 'div',
25378             cls: 'nav-tabs-custom',
25379             cn: [
25380                 h,
25381                 {
25382                     tag: 'div',
25383                     cls: 'tab-content no-padding',
25384                     cn: []
25385                 }
25386             ]
25387         };
25388
25389         return  cfg;
25390     },
25391     initEvents : function()
25392     {
25393         //Roo.log('add add pane handler');
25394         this.on('addpane', this.onAddPane, this);
25395     },
25396      /**
25397      * Updates the box title
25398      * @param {String} html to set the title to.
25399      */
25400     setTitle : function(value)
25401     {
25402         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25403     },
25404     onAddPane : function(pane)
25405     {
25406         this.panes.push(pane);
25407         //Roo.log('addpane');
25408         //Roo.log(pane);
25409         // tabs are rendere left to right..
25410         if(!this.showtabs){
25411             return;
25412         }
25413         
25414         var ctr = this.el.select('.nav-tabs', true).first();
25415          
25416          
25417         var existing = ctr.select('.nav-tab',true);
25418         var qty = existing.getCount();;
25419         
25420         
25421         var tab = ctr.createChild({
25422             tag : 'li',
25423             cls : 'nav-tab' + (qty ? '' : ' active'),
25424             cn : [
25425                 {
25426                     tag : 'a',
25427                     href:'#',
25428                     html : pane.title
25429                 }
25430             ]
25431         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25432         pane.tab = tab;
25433         
25434         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25435         if (!qty) {
25436             pane.el.addClass('active');
25437         }
25438         
25439                 
25440     },
25441     onTabClick : function(ev,un,ob,pane)
25442     {
25443         //Roo.log('tab - prev default');
25444         ev.preventDefault();
25445         
25446         
25447         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25448         pane.tab.addClass('active');
25449         //Roo.log(pane.title);
25450         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25451         // technically we should have a deactivate event.. but maybe add later.
25452         // and it should not de-activate the selected tab...
25453         this.fireEvent('activatepane', pane);
25454         pane.el.addClass('active');
25455         pane.fireEvent('activate');
25456         
25457         
25458     },
25459     
25460     getActivePane : function()
25461     {
25462         var r = false;
25463         Roo.each(this.panes, function(p) {
25464             if(p.el.hasClass('active')){
25465                 r = p;
25466                 return false;
25467             }
25468             
25469             return;
25470         });
25471         
25472         return r;
25473     }
25474     
25475     
25476 });
25477
25478  
25479 /*
25480  * - LGPL
25481  *
25482  * Tab pane
25483  * 
25484  */
25485 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25486 /**
25487  * @class Roo.bootstrap.TabPane
25488  * @extends Roo.bootstrap.Component
25489  * Bootstrap TabPane class
25490  * @cfg {Boolean} active (false | true) Default false
25491  * @cfg {String} title title of panel
25492
25493  * 
25494  * @constructor
25495  * Create a new TabPane
25496  * @param {Object} config The config object
25497  */
25498
25499 Roo.bootstrap.dash.TabPane = function(config){
25500     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25501     
25502     this.addEvents({
25503         // raw events
25504         /**
25505          * @event activate
25506          * When a pane is activated
25507          * @param {Roo.bootstrap.dash.TabPane} pane
25508          */
25509         "activate" : true
25510          
25511     });
25512 };
25513
25514 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25515     
25516     active : false,
25517     title : '',
25518     
25519     // the tabBox that this is attached to.
25520     tab : false,
25521      
25522     getAutoCreate : function() 
25523     {
25524         var cfg = {
25525             tag: 'div',
25526             cls: 'tab-pane'
25527         };
25528         
25529         if(this.active){
25530             cfg.cls += ' active';
25531         }
25532         
25533         return cfg;
25534     },
25535     initEvents  : function()
25536     {
25537         //Roo.log('trigger add pane handler');
25538         this.parent().fireEvent('addpane', this)
25539     },
25540     
25541      /**
25542      * Updates the tab title 
25543      * @param {String} html to set the title to.
25544      */
25545     setTitle: function(str)
25546     {
25547         if (!this.tab) {
25548             return;
25549         }
25550         this.title = str;
25551         this.tab.select('a', true).first().dom.innerHTML = str;
25552         
25553     }
25554     
25555     
25556     
25557 });
25558
25559  
25560
25561
25562  /*
25563  * - LGPL
25564  *
25565  * menu
25566  * 
25567  */
25568 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25569
25570 /**
25571  * @class Roo.bootstrap.menu.Menu
25572  * @extends Roo.bootstrap.Component
25573  * Bootstrap Menu class - container for Menu
25574  * @cfg {String} html Text of the menu
25575  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25576  * @cfg {String} icon Font awesome icon
25577  * @cfg {String} pos Menu align to (top | bottom) default bottom
25578  * 
25579  * 
25580  * @constructor
25581  * Create a new Menu
25582  * @param {Object} config The config object
25583  */
25584
25585
25586 Roo.bootstrap.menu.Menu = function(config){
25587     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25588     
25589     this.addEvents({
25590         /**
25591          * @event beforeshow
25592          * Fires before this menu is displayed
25593          * @param {Roo.bootstrap.menu.Menu} this
25594          */
25595         beforeshow : true,
25596         /**
25597          * @event beforehide
25598          * Fires before this menu is hidden
25599          * @param {Roo.bootstrap.menu.Menu} this
25600          */
25601         beforehide : true,
25602         /**
25603          * @event show
25604          * Fires after this menu is displayed
25605          * @param {Roo.bootstrap.menu.Menu} this
25606          */
25607         show : true,
25608         /**
25609          * @event hide
25610          * Fires after this menu is hidden
25611          * @param {Roo.bootstrap.menu.Menu} this
25612          */
25613         hide : true,
25614         /**
25615          * @event click
25616          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25617          * @param {Roo.bootstrap.menu.Menu} this
25618          * @param {Roo.EventObject} e
25619          */
25620         click : true
25621     });
25622     
25623 };
25624
25625 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25626     
25627     submenu : false,
25628     html : '',
25629     weight : 'default',
25630     icon : false,
25631     pos : 'bottom',
25632     
25633     
25634     getChildContainer : function() {
25635         if(this.isSubMenu){
25636             return this.el;
25637         }
25638         
25639         return this.el.select('ul.dropdown-menu', true).first();  
25640     },
25641     
25642     getAutoCreate : function()
25643     {
25644         var text = [
25645             {
25646                 tag : 'span',
25647                 cls : 'roo-menu-text',
25648                 html : this.html
25649             }
25650         ];
25651         
25652         if(this.icon){
25653             text.unshift({
25654                 tag : 'i',
25655                 cls : 'fa ' + this.icon
25656             })
25657         }
25658         
25659         
25660         var cfg = {
25661             tag : 'div',
25662             cls : 'btn-group',
25663             cn : [
25664                 {
25665                     tag : 'button',
25666                     cls : 'dropdown-button btn btn-' + this.weight,
25667                     cn : text
25668                 },
25669                 {
25670                     tag : 'button',
25671                     cls : 'dropdown-toggle btn btn-' + this.weight,
25672                     cn : [
25673                         {
25674                             tag : 'span',
25675                             cls : 'caret'
25676                         }
25677                     ]
25678                 },
25679                 {
25680                     tag : 'ul',
25681                     cls : 'dropdown-menu'
25682                 }
25683             ]
25684             
25685         };
25686         
25687         if(this.pos == 'top'){
25688             cfg.cls += ' dropup';
25689         }
25690         
25691         if(this.isSubMenu){
25692             cfg = {
25693                 tag : 'ul',
25694                 cls : 'dropdown-menu'
25695             }
25696         }
25697         
25698         return cfg;
25699     },
25700     
25701     onRender : function(ct, position)
25702     {
25703         this.isSubMenu = ct.hasClass('dropdown-submenu');
25704         
25705         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25706     },
25707     
25708     initEvents : function() 
25709     {
25710         if(this.isSubMenu){
25711             return;
25712         }
25713         
25714         this.hidden = true;
25715         
25716         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25717         this.triggerEl.on('click', this.onTriggerPress, this);
25718         
25719         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25720         this.buttonEl.on('click', this.onClick, this);
25721         
25722     },
25723     
25724     list : function()
25725     {
25726         if(this.isSubMenu){
25727             return this.el;
25728         }
25729         
25730         return this.el.select('ul.dropdown-menu', true).first();
25731     },
25732     
25733     onClick : function(e)
25734     {
25735         this.fireEvent("click", this, e);
25736     },
25737     
25738     onTriggerPress  : function(e)
25739     {   
25740         if (this.isVisible()) {
25741             this.hide();
25742         } else {
25743             this.show();
25744         }
25745     },
25746     
25747     isVisible : function(){
25748         return !this.hidden;
25749     },
25750     
25751     show : function()
25752     {
25753         this.fireEvent("beforeshow", this);
25754         
25755         this.hidden = false;
25756         this.el.addClass('open');
25757         
25758         Roo.get(document).on("mouseup", this.onMouseUp, this);
25759         
25760         this.fireEvent("show", this);
25761         
25762         
25763     },
25764     
25765     hide : function()
25766     {
25767         this.fireEvent("beforehide", this);
25768         
25769         this.hidden = true;
25770         this.el.removeClass('open');
25771         
25772         Roo.get(document).un("mouseup", this.onMouseUp);
25773         
25774         this.fireEvent("hide", this);
25775     },
25776     
25777     onMouseUp : function()
25778     {
25779         this.hide();
25780     }
25781     
25782 });
25783
25784  
25785  /*
25786  * - LGPL
25787  *
25788  * menu item
25789  * 
25790  */
25791 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25792
25793 /**
25794  * @class Roo.bootstrap.menu.Item
25795  * @extends Roo.bootstrap.Component
25796  * Bootstrap MenuItem class
25797  * @cfg {Boolean} submenu (true | false) default false
25798  * @cfg {String} html text of the item
25799  * @cfg {String} href the link
25800  * @cfg {Boolean} disable (true | false) default false
25801  * @cfg {Boolean} preventDefault (true | false) default true
25802  * @cfg {String} icon Font awesome icon
25803  * @cfg {String} pos Submenu align to (left | right) default right 
25804  * 
25805  * 
25806  * @constructor
25807  * Create a new Item
25808  * @param {Object} config The config object
25809  */
25810
25811
25812 Roo.bootstrap.menu.Item = function(config){
25813     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25814     this.addEvents({
25815         /**
25816          * @event mouseover
25817          * Fires when the mouse is hovering over this menu
25818          * @param {Roo.bootstrap.menu.Item} this
25819          * @param {Roo.EventObject} e
25820          */
25821         mouseover : true,
25822         /**
25823          * @event mouseout
25824          * Fires when the mouse exits this menu
25825          * @param {Roo.bootstrap.menu.Item} this
25826          * @param {Roo.EventObject} e
25827          */
25828         mouseout : true,
25829         // raw events
25830         /**
25831          * @event click
25832          * The raw click event for the entire grid.
25833          * @param {Roo.EventObject} e
25834          */
25835         click : true
25836     });
25837 };
25838
25839 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25840     
25841     submenu : false,
25842     href : '',
25843     html : '',
25844     preventDefault: true,
25845     disable : false,
25846     icon : false,
25847     pos : 'right',
25848     
25849     getAutoCreate : function()
25850     {
25851         var text = [
25852             {
25853                 tag : 'span',
25854                 cls : 'roo-menu-item-text',
25855                 html : this.html
25856             }
25857         ];
25858         
25859         if(this.icon){
25860             text.unshift({
25861                 tag : 'i',
25862                 cls : 'fa ' + this.icon
25863             })
25864         }
25865         
25866         var cfg = {
25867             tag : 'li',
25868             cn : [
25869                 {
25870                     tag : 'a',
25871                     href : this.href || '#',
25872                     cn : text
25873                 }
25874             ]
25875         };
25876         
25877         if(this.disable){
25878             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25879         }
25880         
25881         if(this.submenu){
25882             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25883             
25884             if(this.pos == 'left'){
25885                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25886             }
25887         }
25888         
25889         return cfg;
25890     },
25891     
25892     initEvents : function() 
25893     {
25894         this.el.on('mouseover', this.onMouseOver, this);
25895         this.el.on('mouseout', this.onMouseOut, this);
25896         
25897         this.el.select('a', true).first().on('click', this.onClick, this);
25898         
25899     },
25900     
25901     onClick : function(e)
25902     {
25903         if(this.preventDefault){
25904             e.preventDefault();
25905         }
25906         
25907         this.fireEvent("click", this, e);
25908     },
25909     
25910     onMouseOver : function(e)
25911     {
25912         if(this.submenu && this.pos == 'left'){
25913             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25914         }
25915         
25916         this.fireEvent("mouseover", this, e);
25917     },
25918     
25919     onMouseOut : function(e)
25920     {
25921         this.fireEvent("mouseout", this, e);
25922     }
25923 });
25924
25925  
25926
25927  /*
25928  * - LGPL
25929  *
25930  * menu separator
25931  * 
25932  */
25933 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25934
25935 /**
25936  * @class Roo.bootstrap.menu.Separator
25937  * @extends Roo.bootstrap.Component
25938  * Bootstrap Separator class
25939  * 
25940  * @constructor
25941  * Create a new Separator
25942  * @param {Object} config The config object
25943  */
25944
25945
25946 Roo.bootstrap.menu.Separator = function(config){
25947     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25948 };
25949
25950 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25951     
25952     getAutoCreate : function(){
25953         var cfg = {
25954             tag : 'li',
25955             cls: 'divider'
25956         };
25957         
25958         return cfg;
25959     }
25960    
25961 });
25962
25963  
25964
25965  /*
25966  * - LGPL
25967  *
25968  * Tooltip
25969  * 
25970  */
25971
25972 /**
25973  * @class Roo.bootstrap.Tooltip
25974  * Bootstrap Tooltip class
25975  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25976  * to determine which dom element triggers the tooltip.
25977  * 
25978  * It needs to add support for additional attributes like tooltip-position
25979  * 
25980  * @constructor
25981  * Create a new Toolti
25982  * @param {Object} config The config object
25983  */
25984
25985 Roo.bootstrap.Tooltip = function(config){
25986     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25987     
25988     this.alignment = Roo.bootstrap.Tooltip.alignment;
25989     
25990     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25991         this.alignment = config.alignment;
25992     }
25993     
25994 };
25995
25996 Roo.apply(Roo.bootstrap.Tooltip, {
25997     /**
25998      * @function init initialize tooltip monitoring.
25999      * @static
26000      */
26001     currentEl : false,
26002     currentTip : false,
26003     currentRegion : false,
26004     
26005     //  init : delay?
26006     
26007     init : function()
26008     {
26009         Roo.get(document).on('mouseover', this.enter ,this);
26010         Roo.get(document).on('mouseout', this.leave, this);
26011          
26012         
26013         this.currentTip = new Roo.bootstrap.Tooltip();
26014     },
26015     
26016     enter : function(ev)
26017     {
26018         var dom = ev.getTarget();
26019         
26020         //Roo.log(['enter',dom]);
26021         var el = Roo.fly(dom);
26022         if (this.currentEl) {
26023             //Roo.log(dom);
26024             //Roo.log(this.currentEl);
26025             //Roo.log(this.currentEl.contains(dom));
26026             if (this.currentEl == el) {
26027                 return;
26028             }
26029             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26030                 return;
26031             }
26032
26033         }
26034         
26035         if (this.currentTip.el) {
26036             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26037         }    
26038         //Roo.log(ev);
26039         
26040         if(!el || el.dom == document){
26041             return;
26042         }
26043         
26044         var bindEl = el;
26045         
26046         // you can not look for children, as if el is the body.. then everythign is the child..
26047         if (!el.attr('tooltip')) { //
26048             if (!el.select("[tooltip]").elements.length) {
26049                 return;
26050             }
26051             // is the mouse over this child...?
26052             bindEl = el.select("[tooltip]").first();
26053             var xy = ev.getXY();
26054             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26055                 //Roo.log("not in region.");
26056                 return;
26057             }
26058             //Roo.log("child element over..");
26059             
26060         }
26061         this.currentEl = bindEl;
26062         this.currentTip.bind(bindEl);
26063         this.currentRegion = Roo.lib.Region.getRegion(dom);
26064         this.currentTip.enter();
26065         
26066     },
26067     leave : function(ev)
26068     {
26069         var dom = ev.getTarget();
26070         //Roo.log(['leave',dom]);
26071         if (!this.currentEl) {
26072             return;
26073         }
26074         
26075         
26076         if (dom != this.currentEl.dom) {
26077             return;
26078         }
26079         var xy = ev.getXY();
26080         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26081             return;
26082         }
26083         // only activate leave if mouse cursor is outside... bounding box..
26084         
26085         
26086         
26087         
26088         if (this.currentTip) {
26089             this.currentTip.leave();
26090         }
26091         //Roo.log('clear currentEl');
26092         this.currentEl = false;
26093         
26094         
26095     },
26096     alignment : {
26097         'left' : ['r-l', [-2,0], 'right'],
26098         'right' : ['l-r', [2,0], 'left'],
26099         'bottom' : ['t-b', [0,2], 'top'],
26100         'top' : [ 'b-t', [0,-2], 'bottom']
26101     }
26102     
26103 });
26104
26105
26106 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26107     
26108     
26109     bindEl : false,
26110     
26111     delay : null, // can be { show : 300 , hide: 500}
26112     
26113     timeout : null,
26114     
26115     hoverState : null, //???
26116     
26117     placement : 'bottom', 
26118     
26119     alignment : false,
26120     
26121     getAutoCreate : function(){
26122     
26123         var cfg = {
26124            cls : 'tooltip',
26125            role : 'tooltip',
26126            cn : [
26127                 {
26128                     cls : 'tooltip-arrow'
26129                 },
26130                 {
26131                     cls : 'tooltip-inner'
26132                 }
26133            ]
26134         };
26135         
26136         return cfg;
26137     },
26138     bind : function(el)
26139     {
26140         this.bindEl = el;
26141     },
26142       
26143     
26144     enter : function () {
26145        
26146         if (this.timeout != null) {
26147             clearTimeout(this.timeout);
26148         }
26149         
26150         this.hoverState = 'in';
26151          //Roo.log("enter - show");
26152         if (!this.delay || !this.delay.show) {
26153             this.show();
26154             return;
26155         }
26156         var _t = this;
26157         this.timeout = setTimeout(function () {
26158             if (_t.hoverState == 'in') {
26159                 _t.show();
26160             }
26161         }, this.delay.show);
26162     },
26163     leave : function()
26164     {
26165         clearTimeout(this.timeout);
26166     
26167         this.hoverState = 'out';
26168          if (!this.delay || !this.delay.hide) {
26169             this.hide();
26170             return;
26171         }
26172        
26173         var _t = this;
26174         this.timeout = setTimeout(function () {
26175             //Roo.log("leave - timeout");
26176             
26177             if (_t.hoverState == 'out') {
26178                 _t.hide();
26179                 Roo.bootstrap.Tooltip.currentEl = false;
26180             }
26181         }, delay);
26182     },
26183     
26184     show : function (msg)
26185     {
26186         if (!this.el) {
26187             this.render(document.body);
26188         }
26189         // set content.
26190         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26191         
26192         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26193         
26194         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26195         
26196         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26197         
26198         var placement = typeof this.placement == 'function' ?
26199             this.placement.call(this, this.el, on_el) :
26200             this.placement;
26201             
26202         var autoToken = /\s?auto?\s?/i;
26203         var autoPlace = autoToken.test(placement);
26204         if (autoPlace) {
26205             placement = placement.replace(autoToken, '') || 'top';
26206         }
26207         
26208         //this.el.detach()
26209         //this.el.setXY([0,0]);
26210         this.el.show();
26211         //this.el.dom.style.display='block';
26212         
26213         //this.el.appendTo(on_el);
26214         
26215         var p = this.getPosition();
26216         var box = this.el.getBox();
26217         
26218         if (autoPlace) {
26219             // fixme..
26220         }
26221         
26222         var align = this.alignment[placement];
26223         
26224         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26225         
26226         if(placement == 'top' || placement == 'bottom'){
26227             if(xy[0] < 0){
26228                 placement = 'right';
26229             }
26230             
26231             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26232                 placement = 'left';
26233             }
26234             
26235             var scroll = Roo.select('body', true).first().getScroll();
26236             
26237             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26238                 placement = 'top';
26239             }
26240             
26241             align = this.alignment[placement];
26242         }
26243         
26244         this.el.alignTo(this.bindEl, align[0],align[1]);
26245         //var arrow = this.el.select('.arrow',true).first();
26246         //arrow.set(align[2], 
26247         
26248         this.el.addClass(placement);
26249         
26250         this.el.addClass('in fade');
26251         
26252         this.hoverState = null;
26253         
26254         if (this.el.hasClass('fade')) {
26255             // fade it?
26256         }
26257         
26258     },
26259     hide : function()
26260     {
26261          
26262         if (!this.el) {
26263             return;
26264         }
26265         //this.el.setXY([0,0]);
26266         this.el.removeClass('in');
26267         //this.el.hide();
26268         
26269     }
26270     
26271 });
26272  
26273
26274  /*
26275  * - LGPL
26276  *
26277  * Location Picker
26278  * 
26279  */
26280
26281 /**
26282  * @class Roo.bootstrap.LocationPicker
26283  * @extends Roo.bootstrap.Component
26284  * Bootstrap LocationPicker class
26285  * @cfg {Number} latitude Position when init default 0
26286  * @cfg {Number} longitude Position when init default 0
26287  * @cfg {Number} zoom default 15
26288  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26289  * @cfg {Boolean} mapTypeControl default false
26290  * @cfg {Boolean} disableDoubleClickZoom default false
26291  * @cfg {Boolean} scrollwheel default true
26292  * @cfg {Boolean} streetViewControl default false
26293  * @cfg {Number} radius default 0
26294  * @cfg {String} locationName
26295  * @cfg {Boolean} draggable default true
26296  * @cfg {Boolean} enableAutocomplete default false
26297  * @cfg {Boolean} enableReverseGeocode default true
26298  * @cfg {String} markerTitle
26299  * 
26300  * @constructor
26301  * Create a new LocationPicker
26302  * @param {Object} config The config object
26303  */
26304
26305
26306 Roo.bootstrap.LocationPicker = function(config){
26307     
26308     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26309     
26310     this.addEvents({
26311         /**
26312          * @event initial
26313          * Fires when the picker initialized.
26314          * @param {Roo.bootstrap.LocationPicker} this
26315          * @param {Google Location} location
26316          */
26317         initial : true,
26318         /**
26319          * @event positionchanged
26320          * Fires when the picker position changed.
26321          * @param {Roo.bootstrap.LocationPicker} this
26322          * @param {Google Location} location
26323          */
26324         positionchanged : true,
26325         /**
26326          * @event resize
26327          * Fires when the map resize.
26328          * @param {Roo.bootstrap.LocationPicker} this
26329          */
26330         resize : true,
26331         /**
26332          * @event show
26333          * Fires when the map show.
26334          * @param {Roo.bootstrap.LocationPicker} this
26335          */
26336         show : true,
26337         /**
26338          * @event hide
26339          * Fires when the map hide.
26340          * @param {Roo.bootstrap.LocationPicker} this
26341          */
26342         hide : true,
26343         /**
26344          * @event mapClick
26345          * Fires when click the map.
26346          * @param {Roo.bootstrap.LocationPicker} this
26347          * @param {Map event} e
26348          */
26349         mapClick : true,
26350         /**
26351          * @event mapRightClick
26352          * Fires when right click the map.
26353          * @param {Roo.bootstrap.LocationPicker} this
26354          * @param {Map event} e
26355          */
26356         mapRightClick : true,
26357         /**
26358          * @event markerClick
26359          * Fires when click the marker.
26360          * @param {Roo.bootstrap.LocationPicker} this
26361          * @param {Map event} e
26362          */
26363         markerClick : true,
26364         /**
26365          * @event markerRightClick
26366          * Fires when right click the marker.
26367          * @param {Roo.bootstrap.LocationPicker} this
26368          * @param {Map event} e
26369          */
26370         markerRightClick : true,
26371         /**
26372          * @event OverlayViewDraw
26373          * Fires when OverlayView Draw
26374          * @param {Roo.bootstrap.LocationPicker} this
26375          */
26376         OverlayViewDraw : true,
26377         /**
26378          * @event OverlayViewOnAdd
26379          * Fires when OverlayView Draw
26380          * @param {Roo.bootstrap.LocationPicker} this
26381          */
26382         OverlayViewOnAdd : true,
26383         /**
26384          * @event OverlayViewOnRemove
26385          * Fires when OverlayView Draw
26386          * @param {Roo.bootstrap.LocationPicker} this
26387          */
26388         OverlayViewOnRemove : true,
26389         /**
26390          * @event OverlayViewShow
26391          * Fires when OverlayView Draw
26392          * @param {Roo.bootstrap.LocationPicker} this
26393          * @param {Pixel} cpx
26394          */
26395         OverlayViewShow : true,
26396         /**
26397          * @event OverlayViewHide
26398          * Fires when OverlayView Draw
26399          * @param {Roo.bootstrap.LocationPicker} this
26400          */
26401         OverlayViewHide : true,
26402         /**
26403          * @event loadexception
26404          * Fires when load google lib failed.
26405          * @param {Roo.bootstrap.LocationPicker} this
26406          */
26407         loadexception : true
26408     });
26409         
26410 };
26411
26412 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26413     
26414     gMapContext: false,
26415     
26416     latitude: 0,
26417     longitude: 0,
26418     zoom: 15,
26419     mapTypeId: false,
26420     mapTypeControl: false,
26421     disableDoubleClickZoom: false,
26422     scrollwheel: true,
26423     streetViewControl: false,
26424     radius: 0,
26425     locationName: '',
26426     draggable: true,
26427     enableAutocomplete: false,
26428     enableReverseGeocode: true,
26429     markerTitle: '',
26430     
26431     getAutoCreate: function()
26432     {
26433
26434         var cfg = {
26435             tag: 'div',
26436             cls: 'roo-location-picker'
26437         };
26438         
26439         return cfg
26440     },
26441     
26442     initEvents: function(ct, position)
26443     {       
26444         if(!this.el.getWidth() || this.isApplied()){
26445             return;
26446         }
26447         
26448         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26449         
26450         this.initial();
26451     },
26452     
26453     initial: function()
26454     {
26455         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26456             this.fireEvent('loadexception', this);
26457             return;
26458         }
26459         
26460         if(!this.mapTypeId){
26461             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26462         }
26463         
26464         this.gMapContext = this.GMapContext();
26465         
26466         this.initOverlayView();
26467         
26468         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26469         
26470         var _this = this;
26471                 
26472         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26473             _this.setPosition(_this.gMapContext.marker.position);
26474         });
26475         
26476         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26477             _this.fireEvent('mapClick', this, event);
26478             
26479         });
26480
26481         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26482             _this.fireEvent('mapRightClick', this, event);
26483             
26484         });
26485         
26486         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26487             _this.fireEvent('markerClick', this, event);
26488             
26489         });
26490
26491         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26492             _this.fireEvent('markerRightClick', this, event);
26493             
26494         });
26495         
26496         this.setPosition(this.gMapContext.location);
26497         
26498         this.fireEvent('initial', this, this.gMapContext.location);
26499     },
26500     
26501     initOverlayView: function()
26502     {
26503         var _this = this;
26504         
26505         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26506             
26507             draw: function()
26508             {
26509                 _this.fireEvent('OverlayViewDraw', _this);
26510             },
26511             
26512             onAdd: function()
26513             {
26514                 _this.fireEvent('OverlayViewOnAdd', _this);
26515             },
26516             
26517             onRemove: function()
26518             {
26519                 _this.fireEvent('OverlayViewOnRemove', _this);
26520             },
26521             
26522             show: function(cpx)
26523             {
26524                 _this.fireEvent('OverlayViewShow', _this, cpx);
26525             },
26526             
26527             hide: function()
26528             {
26529                 _this.fireEvent('OverlayViewHide', _this);
26530             }
26531             
26532         });
26533     },
26534     
26535     fromLatLngToContainerPixel: function(event)
26536     {
26537         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26538     },
26539     
26540     isApplied: function() 
26541     {
26542         return this.getGmapContext() == false ? false : true;
26543     },
26544     
26545     getGmapContext: function() 
26546     {
26547         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26548     },
26549     
26550     GMapContext: function() 
26551     {
26552         var position = new google.maps.LatLng(this.latitude, this.longitude);
26553         
26554         var _map = new google.maps.Map(this.el.dom, {
26555             center: position,
26556             zoom: this.zoom,
26557             mapTypeId: this.mapTypeId,
26558             mapTypeControl: this.mapTypeControl,
26559             disableDoubleClickZoom: this.disableDoubleClickZoom,
26560             scrollwheel: this.scrollwheel,
26561             streetViewControl: this.streetViewControl,
26562             locationName: this.locationName,
26563             draggable: this.draggable,
26564             enableAutocomplete: this.enableAutocomplete,
26565             enableReverseGeocode: this.enableReverseGeocode
26566         });
26567         
26568         var _marker = new google.maps.Marker({
26569             position: position,
26570             map: _map,
26571             title: this.markerTitle,
26572             draggable: this.draggable
26573         });
26574         
26575         return {
26576             map: _map,
26577             marker: _marker,
26578             circle: null,
26579             location: position,
26580             radius: this.radius,
26581             locationName: this.locationName,
26582             addressComponents: {
26583                 formatted_address: null,
26584                 addressLine1: null,
26585                 addressLine2: null,
26586                 streetName: null,
26587                 streetNumber: null,
26588                 city: null,
26589                 district: null,
26590                 state: null,
26591                 stateOrProvince: null
26592             },
26593             settings: this,
26594             domContainer: this.el.dom,
26595             geodecoder: new google.maps.Geocoder()
26596         };
26597     },
26598     
26599     drawCircle: function(center, radius, options) 
26600     {
26601         if (this.gMapContext.circle != null) {
26602             this.gMapContext.circle.setMap(null);
26603         }
26604         if (radius > 0) {
26605             radius *= 1;
26606             options = Roo.apply({}, options, {
26607                 strokeColor: "#0000FF",
26608                 strokeOpacity: .35,
26609                 strokeWeight: 2,
26610                 fillColor: "#0000FF",
26611                 fillOpacity: .2
26612             });
26613             
26614             options.map = this.gMapContext.map;
26615             options.radius = radius;
26616             options.center = center;
26617             this.gMapContext.circle = new google.maps.Circle(options);
26618             return this.gMapContext.circle;
26619         }
26620         
26621         return null;
26622     },
26623     
26624     setPosition: function(location) 
26625     {
26626         this.gMapContext.location = location;
26627         this.gMapContext.marker.setPosition(location);
26628         this.gMapContext.map.panTo(location);
26629         this.drawCircle(location, this.gMapContext.radius, {});
26630         
26631         var _this = this;
26632         
26633         if (this.gMapContext.settings.enableReverseGeocode) {
26634             this.gMapContext.geodecoder.geocode({
26635                 latLng: this.gMapContext.location
26636             }, function(results, status) {
26637                 
26638                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26639                     _this.gMapContext.locationName = results[0].formatted_address;
26640                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26641                     
26642                     _this.fireEvent('positionchanged', this, location);
26643                 }
26644             });
26645             
26646             return;
26647         }
26648         
26649         this.fireEvent('positionchanged', this, location);
26650     },
26651     
26652     resize: function()
26653     {
26654         google.maps.event.trigger(this.gMapContext.map, "resize");
26655         
26656         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26657         
26658         this.fireEvent('resize', this);
26659     },
26660     
26661     setPositionByLatLng: function(latitude, longitude)
26662     {
26663         this.setPosition(new google.maps.LatLng(latitude, longitude));
26664     },
26665     
26666     getCurrentPosition: function() 
26667     {
26668         return {
26669             latitude: this.gMapContext.location.lat(),
26670             longitude: this.gMapContext.location.lng()
26671         };
26672     },
26673     
26674     getAddressName: function() 
26675     {
26676         return this.gMapContext.locationName;
26677     },
26678     
26679     getAddressComponents: function() 
26680     {
26681         return this.gMapContext.addressComponents;
26682     },
26683     
26684     address_component_from_google_geocode: function(address_components) 
26685     {
26686         var result = {};
26687         
26688         for (var i = 0; i < address_components.length; i++) {
26689             var component = address_components[i];
26690             if (component.types.indexOf("postal_code") >= 0) {
26691                 result.postalCode = component.short_name;
26692             } else if (component.types.indexOf("street_number") >= 0) {
26693                 result.streetNumber = component.short_name;
26694             } else if (component.types.indexOf("route") >= 0) {
26695                 result.streetName = component.short_name;
26696             } else if (component.types.indexOf("neighborhood") >= 0) {
26697                 result.city = component.short_name;
26698             } else if (component.types.indexOf("locality") >= 0) {
26699                 result.city = component.short_name;
26700             } else if (component.types.indexOf("sublocality") >= 0) {
26701                 result.district = component.short_name;
26702             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26703                 result.stateOrProvince = component.short_name;
26704             } else if (component.types.indexOf("country") >= 0) {
26705                 result.country = component.short_name;
26706             }
26707         }
26708         
26709         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26710         result.addressLine2 = "";
26711         return result;
26712     },
26713     
26714     setZoomLevel: function(zoom)
26715     {
26716         this.gMapContext.map.setZoom(zoom);
26717     },
26718     
26719     show: function()
26720     {
26721         if(!this.el){
26722             return;
26723         }
26724         
26725         this.el.show();
26726         
26727         this.resize();
26728         
26729         this.fireEvent('show', this);
26730     },
26731     
26732     hide: function()
26733     {
26734         if(!this.el){
26735             return;
26736         }
26737         
26738         this.el.hide();
26739         
26740         this.fireEvent('hide', this);
26741     }
26742     
26743 });
26744
26745 Roo.apply(Roo.bootstrap.LocationPicker, {
26746     
26747     OverlayView : function(map, options)
26748     {
26749         options = options || {};
26750         
26751         this.setMap(map);
26752     }
26753     
26754     
26755 });/*
26756  * - LGPL
26757  *
26758  * Alert
26759  * 
26760  */
26761
26762 /**
26763  * @class Roo.bootstrap.Alert
26764  * @extends Roo.bootstrap.Component
26765  * Bootstrap Alert class
26766  * @cfg {String} title The title of alert
26767  * @cfg {String} html The content of alert
26768  * @cfg {String} weight (  success | info | warning | danger )
26769  * @cfg {String} faicon font-awesomeicon
26770  * 
26771  * @constructor
26772  * Create a new alert
26773  * @param {Object} config The config object
26774  */
26775
26776
26777 Roo.bootstrap.Alert = function(config){
26778     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26779     
26780 };
26781
26782 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26783     
26784     title: '',
26785     html: '',
26786     weight: false,
26787     faicon: false,
26788     
26789     getAutoCreate : function()
26790     {
26791         
26792         var cfg = {
26793             tag : 'div',
26794             cls : 'alert',
26795             cn : [
26796                 {
26797                     tag : 'i',
26798                     cls : 'roo-alert-icon'
26799                     
26800                 },
26801                 {
26802                     tag : 'b',
26803                     cls : 'roo-alert-title',
26804                     html : this.title
26805                 },
26806                 {
26807                     tag : 'span',
26808                     cls : 'roo-alert-text',
26809                     html : this.html
26810                 }
26811             ]
26812         };
26813         
26814         if(this.faicon){
26815             cfg.cn[0].cls += ' fa ' + this.faicon;
26816         }
26817         
26818         if(this.weight){
26819             cfg.cls += ' alert-' + this.weight;
26820         }
26821         
26822         return cfg;
26823     },
26824     
26825     initEvents: function() 
26826     {
26827         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26828     },
26829     
26830     setTitle : function(str)
26831     {
26832         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26833     },
26834     
26835     setText : function(str)
26836     {
26837         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26838     },
26839     
26840     setWeight : function(weight)
26841     {
26842         if(this.weight){
26843             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26844         }
26845         
26846         this.weight = weight;
26847         
26848         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26849     },
26850     
26851     setIcon : function(icon)
26852     {
26853         if(this.faicon){
26854             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26855         }
26856         
26857         this.faicon = icon;
26858         
26859         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26860     },
26861     
26862     hide: function() 
26863     {
26864         this.el.hide();   
26865     },
26866     
26867     show: function() 
26868     {  
26869         this.el.show();   
26870     }
26871     
26872 });
26873
26874  
26875 /*
26876 * Licence: LGPL
26877 */
26878
26879 /**
26880  * @class Roo.bootstrap.UploadCropbox
26881  * @extends Roo.bootstrap.Component
26882  * Bootstrap UploadCropbox class
26883  * @cfg {String} emptyText show when image has been loaded
26884  * @cfg {String} rotateNotify show when image too small to rotate
26885  * @cfg {Number} errorTimeout default 3000
26886  * @cfg {Number} minWidth default 300
26887  * @cfg {Number} minHeight default 300
26888  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26889  * @cfg {Boolean} isDocument (true|false) default false
26890  * @cfg {String} url action url
26891  * @cfg {String} paramName default 'imageUpload'
26892  * @cfg {String} method default POST
26893  * @cfg {Boolean} loadMask (true|false) default true
26894  * @cfg {Boolean} loadingText default 'Loading...'
26895  * 
26896  * @constructor
26897  * Create a new UploadCropbox
26898  * @param {Object} config The config object
26899  */
26900
26901 Roo.bootstrap.UploadCropbox = function(config){
26902     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26903     
26904     this.addEvents({
26905         /**
26906          * @event beforeselectfile
26907          * Fire before select file
26908          * @param {Roo.bootstrap.UploadCropbox} this
26909          */
26910         "beforeselectfile" : true,
26911         /**
26912          * @event initial
26913          * Fire after initEvent
26914          * @param {Roo.bootstrap.UploadCropbox} this
26915          */
26916         "initial" : true,
26917         /**
26918          * @event crop
26919          * Fire after initEvent
26920          * @param {Roo.bootstrap.UploadCropbox} this
26921          * @param {String} data
26922          */
26923         "crop" : true,
26924         /**
26925          * @event prepare
26926          * Fire when preparing the file data
26927          * @param {Roo.bootstrap.UploadCropbox} this
26928          * @param {Object} file
26929          */
26930         "prepare" : true,
26931         /**
26932          * @event exception
26933          * Fire when get exception
26934          * @param {Roo.bootstrap.UploadCropbox} this
26935          * @param {XMLHttpRequest} xhr
26936          */
26937         "exception" : true,
26938         /**
26939          * @event beforeloadcanvas
26940          * Fire before load the canvas
26941          * @param {Roo.bootstrap.UploadCropbox} this
26942          * @param {String} src
26943          */
26944         "beforeloadcanvas" : true,
26945         /**
26946          * @event trash
26947          * Fire when trash image
26948          * @param {Roo.bootstrap.UploadCropbox} this
26949          */
26950         "trash" : true,
26951         /**
26952          * @event download
26953          * Fire when download the image
26954          * @param {Roo.bootstrap.UploadCropbox} this
26955          */
26956         "download" : true,
26957         /**
26958          * @event footerbuttonclick
26959          * Fire when footerbuttonclick
26960          * @param {Roo.bootstrap.UploadCropbox} this
26961          * @param {String} type
26962          */
26963         "footerbuttonclick" : true,
26964         /**
26965          * @event resize
26966          * Fire when resize
26967          * @param {Roo.bootstrap.UploadCropbox} this
26968          */
26969         "resize" : true,
26970         /**
26971          * @event rotate
26972          * Fire when rotate the image
26973          * @param {Roo.bootstrap.UploadCropbox} this
26974          * @param {String} pos
26975          */
26976         "rotate" : true,
26977         /**
26978          * @event inspect
26979          * Fire when inspect the file
26980          * @param {Roo.bootstrap.UploadCropbox} this
26981          * @param {Object} file
26982          */
26983         "inspect" : true,
26984         /**
26985          * @event upload
26986          * Fire when xhr upload the file
26987          * @param {Roo.bootstrap.UploadCropbox} this
26988          * @param {Object} data
26989          */
26990         "upload" : true,
26991         /**
26992          * @event arrange
26993          * Fire when arrange the file data
26994          * @param {Roo.bootstrap.UploadCropbox} this
26995          * @param {Object} formData
26996          */
26997         "arrange" : true
26998     });
26999     
27000     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27001 };
27002
27003 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27004     
27005     emptyText : 'Click to upload image',
27006     rotateNotify : 'Image is too small to rotate',
27007     errorTimeout : 3000,
27008     scale : 0,
27009     baseScale : 1,
27010     rotate : 0,
27011     dragable : false,
27012     pinching : false,
27013     mouseX : 0,
27014     mouseY : 0,
27015     cropData : false,
27016     minWidth : 300,
27017     minHeight : 300,
27018     file : false,
27019     exif : {},
27020     baseRotate : 1,
27021     cropType : 'image/jpeg',
27022     buttons : false,
27023     canvasLoaded : false,
27024     isDocument : false,
27025     method : 'POST',
27026     paramName : 'imageUpload',
27027     loadMask : true,
27028     loadingText : 'Loading...',
27029     maskEl : false,
27030     
27031     getAutoCreate : function()
27032     {
27033         var cfg = {
27034             tag : 'div',
27035             cls : 'roo-upload-cropbox',
27036             cn : [
27037                 {
27038                     tag : 'input',
27039                     cls : 'roo-upload-cropbox-selector',
27040                     type : 'file'
27041                 },
27042                 {
27043                     tag : 'div',
27044                     cls : 'roo-upload-cropbox-body',
27045                     style : 'cursor:pointer',
27046                     cn : [
27047                         {
27048                             tag : 'div',
27049                             cls : 'roo-upload-cropbox-preview'
27050                         },
27051                         {
27052                             tag : 'div',
27053                             cls : 'roo-upload-cropbox-thumb'
27054                         },
27055                         {
27056                             tag : 'div',
27057                             cls : 'roo-upload-cropbox-empty-notify',
27058                             html : this.emptyText
27059                         },
27060                         {
27061                             tag : 'div',
27062                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27063                             html : this.rotateNotify
27064                         }
27065                     ]
27066                 },
27067                 {
27068                     tag : 'div',
27069                     cls : 'roo-upload-cropbox-footer',
27070                     cn : {
27071                         tag : 'div',
27072                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27073                         cn : []
27074                     }
27075                 }
27076             ]
27077         };
27078         
27079         return cfg;
27080     },
27081     
27082     onRender : function(ct, position)
27083     {
27084         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27085         
27086         if (this.buttons.length) {
27087             
27088             Roo.each(this.buttons, function(bb) {
27089                 
27090                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27091                 
27092                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27093                 
27094             }, this);
27095         }
27096         
27097         if(this.loadMask){
27098             this.maskEl = this.el;
27099         }
27100     },
27101     
27102     initEvents : function()
27103     {
27104         this.urlAPI = (window.createObjectURL && window) || 
27105                                 (window.URL && URL.revokeObjectURL && URL) || 
27106                                 (window.webkitURL && webkitURL);
27107                         
27108         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27109         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27110         
27111         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27112         this.selectorEl.hide();
27113         
27114         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27115         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27116         
27117         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27118         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27119         this.thumbEl.hide();
27120         
27121         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27122         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27123         
27124         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27125         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27126         this.errorEl.hide();
27127         
27128         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27129         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27130         this.footerEl.hide();
27131         
27132         this.setThumbBoxSize();
27133         
27134         this.bind();
27135         
27136         this.resize();
27137         
27138         this.fireEvent('initial', this);
27139     },
27140
27141     bind : function()
27142     {
27143         var _this = this;
27144         
27145         window.addEventListener("resize", function() { _this.resize(); } );
27146         
27147         this.bodyEl.on('click', this.beforeSelectFile, this);
27148         
27149         if(Roo.isTouch){
27150             this.bodyEl.on('touchstart', this.onTouchStart, this);
27151             this.bodyEl.on('touchmove', this.onTouchMove, this);
27152             this.bodyEl.on('touchend', this.onTouchEnd, this);
27153         }
27154         
27155         if(!Roo.isTouch){
27156             this.bodyEl.on('mousedown', this.onMouseDown, this);
27157             this.bodyEl.on('mousemove', this.onMouseMove, this);
27158             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27159             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27160             Roo.get(document).on('mouseup', this.onMouseUp, this);
27161         }
27162         
27163         this.selectorEl.on('change', this.onFileSelected, this);
27164     },
27165     
27166     reset : function()
27167     {    
27168         this.scale = 0;
27169         this.baseScale = 1;
27170         this.rotate = 0;
27171         this.baseRotate = 1;
27172         this.dragable = false;
27173         this.pinching = false;
27174         this.mouseX = 0;
27175         this.mouseY = 0;
27176         this.cropData = false;
27177         this.notifyEl.dom.innerHTML = this.emptyText;
27178         
27179         this.selectorEl.dom.value = '';
27180         
27181     },
27182     
27183     resize : function()
27184     {
27185         if(this.fireEvent('resize', this) != false){
27186             this.setThumbBoxPosition();
27187             this.setCanvasPosition();
27188         }
27189     },
27190     
27191     onFooterButtonClick : function(e, el, o, type)
27192     {
27193         switch (type) {
27194             case 'rotate-left' :
27195                 this.onRotateLeft(e);
27196                 break;
27197             case 'rotate-right' :
27198                 this.onRotateRight(e);
27199                 break;
27200             case 'picture' :
27201                 this.beforeSelectFile(e);
27202                 break;
27203             case 'trash' :
27204                 this.trash(e);
27205                 break;
27206             case 'crop' :
27207                 this.crop(e);
27208                 break;
27209             case 'download' :
27210                 this.download(e);
27211                 break;
27212             default :
27213                 break;
27214         }
27215         
27216         this.fireEvent('footerbuttonclick', this, type);
27217     },
27218     
27219     beforeSelectFile : function(e)
27220     {
27221         e.preventDefault();
27222         
27223         if(this.fireEvent('beforeselectfile', this) != false){
27224             this.selectorEl.dom.click();
27225         }
27226     },
27227     
27228     onFileSelected : function(e)
27229     {
27230         e.preventDefault();
27231         
27232         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27233             return;
27234         }
27235         
27236         var file = this.selectorEl.dom.files[0];
27237         
27238         if(this.fireEvent('inspect', this, file) != false){
27239             this.prepare(file);
27240         }
27241         
27242     },
27243     
27244     trash : function(e)
27245     {
27246         this.fireEvent('trash', this);
27247     },
27248     
27249     download : function(e)
27250     {
27251         this.fireEvent('download', this);
27252     },
27253     
27254     loadCanvas : function(src)
27255     {   
27256         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27257             
27258             this.reset();
27259             
27260             this.imageEl = document.createElement('img');
27261             
27262             var _this = this;
27263             
27264             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27265             
27266             this.imageEl.src = src;
27267         }
27268     },
27269     
27270     onLoadCanvas : function()
27271     {   
27272         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27273         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27274         
27275         this.bodyEl.un('click', this.beforeSelectFile, this);
27276         
27277         this.notifyEl.hide();
27278         this.thumbEl.show();
27279         this.footerEl.show();
27280         
27281         this.baseRotateLevel();
27282         
27283         if(this.isDocument){
27284             this.setThumbBoxSize();
27285         }
27286         
27287         this.setThumbBoxPosition();
27288         
27289         this.baseScaleLevel();
27290         
27291         this.draw();
27292         
27293         this.resize();
27294         
27295         this.canvasLoaded = true;
27296         
27297         if(this.loadMask){
27298             this.maskEl.unmask();
27299         }
27300         
27301     },
27302     
27303     setCanvasPosition : function()
27304     {   
27305         if(!this.canvasEl){
27306             return;
27307         }
27308         
27309         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27310         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27311         
27312         this.previewEl.setLeft(pw);
27313         this.previewEl.setTop(ph);
27314         
27315     },
27316     
27317     onMouseDown : function(e)
27318     {   
27319         e.stopEvent();
27320         
27321         this.dragable = true;
27322         this.pinching = false;
27323         
27324         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27325             this.dragable = false;
27326             return;
27327         }
27328         
27329         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27330         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27331         
27332     },
27333     
27334     onMouseMove : function(e)
27335     {   
27336         e.stopEvent();
27337         
27338         if(!this.canvasLoaded){
27339             return;
27340         }
27341         
27342         if (!this.dragable){
27343             return;
27344         }
27345         
27346         var minX = Math.ceil(this.thumbEl.getLeft(true));
27347         var minY = Math.ceil(this.thumbEl.getTop(true));
27348         
27349         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27350         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27351         
27352         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27353         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27354         
27355         x = x - this.mouseX;
27356         y = y - this.mouseY;
27357         
27358         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27359         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27360         
27361         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27362         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27363         
27364         this.previewEl.setLeft(bgX);
27365         this.previewEl.setTop(bgY);
27366         
27367         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27368         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27369     },
27370     
27371     onMouseUp : function(e)
27372     {   
27373         e.stopEvent();
27374         
27375         this.dragable = false;
27376     },
27377     
27378     onMouseWheel : function(e)
27379     {   
27380         e.stopEvent();
27381         
27382         this.startScale = this.scale;
27383         
27384         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27385         
27386         if(!this.zoomable()){
27387             this.scale = this.startScale;
27388             return;
27389         }
27390         
27391         this.draw();
27392         
27393         return;
27394     },
27395     
27396     zoomable : function()
27397     {
27398         var minScale = this.thumbEl.getWidth() / this.minWidth;
27399         
27400         if(this.minWidth < this.minHeight){
27401             minScale = this.thumbEl.getHeight() / this.minHeight;
27402         }
27403         
27404         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27405         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27406         
27407         if(
27408                 this.isDocument &&
27409                 (this.rotate == 0 || this.rotate == 180) && 
27410                 (
27411                     width > this.imageEl.OriginWidth || 
27412                     height > this.imageEl.OriginHeight ||
27413                     (width < this.minWidth && height < this.minHeight)
27414                 )
27415         ){
27416             return false;
27417         }
27418         
27419         if(
27420                 this.isDocument &&
27421                 (this.rotate == 90 || this.rotate == 270) && 
27422                 (
27423                     width > this.imageEl.OriginWidth || 
27424                     height > this.imageEl.OriginHeight ||
27425                     (width < this.minHeight && height < this.minWidth)
27426                 )
27427         ){
27428             return false;
27429         }
27430         
27431         if(
27432                 !this.isDocument &&
27433                 (this.rotate == 0 || this.rotate == 180) && 
27434                 (
27435                     width < this.minWidth || 
27436                     width > this.imageEl.OriginWidth || 
27437                     height < this.minHeight || 
27438                     height > this.imageEl.OriginHeight
27439                 )
27440         ){
27441             return false;
27442         }
27443         
27444         if(
27445                 !this.isDocument &&
27446                 (this.rotate == 90 || this.rotate == 270) && 
27447                 (
27448                     width < this.minHeight || 
27449                     width > this.imageEl.OriginWidth || 
27450                     height < this.minWidth || 
27451                     height > this.imageEl.OriginHeight
27452                 )
27453         ){
27454             return false;
27455         }
27456         
27457         return true;
27458         
27459     },
27460     
27461     onRotateLeft : function(e)
27462     {   
27463         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27464             
27465             var minScale = this.thumbEl.getWidth() / this.minWidth;
27466             
27467             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27468             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27469             
27470             this.startScale = this.scale;
27471             
27472             while (this.getScaleLevel() < minScale){
27473             
27474                 this.scale = this.scale + 1;
27475                 
27476                 if(!this.zoomable()){
27477                     break;
27478                 }
27479                 
27480                 if(
27481                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27482                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27483                 ){
27484                     continue;
27485                 }
27486                 
27487                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27488
27489                 this.draw();
27490                 
27491                 return;
27492             }
27493             
27494             this.scale = this.startScale;
27495             
27496             this.onRotateFail();
27497             
27498             return false;
27499         }
27500         
27501         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27502
27503         if(this.isDocument){
27504             this.setThumbBoxSize();
27505             this.setThumbBoxPosition();
27506             this.setCanvasPosition();
27507         }
27508         
27509         this.draw();
27510         
27511         this.fireEvent('rotate', this, 'left');
27512         
27513     },
27514     
27515     onRotateRight : function(e)
27516     {
27517         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27518             
27519             var minScale = this.thumbEl.getWidth() / this.minWidth;
27520         
27521             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27522             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27523             
27524             this.startScale = this.scale;
27525             
27526             while (this.getScaleLevel() < minScale){
27527             
27528                 this.scale = this.scale + 1;
27529                 
27530                 if(!this.zoomable()){
27531                     break;
27532                 }
27533                 
27534                 if(
27535                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27536                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27537                 ){
27538                     continue;
27539                 }
27540                 
27541                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27542
27543                 this.draw();
27544                 
27545                 return;
27546             }
27547             
27548             this.scale = this.startScale;
27549             
27550             this.onRotateFail();
27551             
27552             return false;
27553         }
27554         
27555         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27556
27557         if(this.isDocument){
27558             this.setThumbBoxSize();
27559             this.setThumbBoxPosition();
27560             this.setCanvasPosition();
27561         }
27562         
27563         this.draw();
27564         
27565         this.fireEvent('rotate', this, 'right');
27566     },
27567     
27568     onRotateFail : function()
27569     {
27570         this.errorEl.show(true);
27571         
27572         var _this = this;
27573         
27574         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27575     },
27576     
27577     draw : function()
27578     {
27579         this.previewEl.dom.innerHTML = '';
27580         
27581         var canvasEl = document.createElement("canvas");
27582         
27583         var contextEl = canvasEl.getContext("2d");
27584         
27585         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27586         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27587         var center = this.imageEl.OriginWidth / 2;
27588         
27589         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27590             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27591             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27592             center = this.imageEl.OriginHeight / 2;
27593         }
27594         
27595         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27596         
27597         contextEl.translate(center, center);
27598         contextEl.rotate(this.rotate * Math.PI / 180);
27599
27600         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27601         
27602         this.canvasEl = document.createElement("canvas");
27603         
27604         this.contextEl = this.canvasEl.getContext("2d");
27605         
27606         switch (this.rotate) {
27607             case 0 :
27608                 
27609                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27610                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27611                 
27612                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27613                 
27614                 break;
27615             case 90 : 
27616                 
27617                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27618                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27619                 
27620                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27621                     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);
27622                     break;
27623                 }
27624                 
27625                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27626                 
27627                 break;
27628             case 180 :
27629                 
27630                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27631                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27632                 
27633                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27634                     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);
27635                     break;
27636                 }
27637                 
27638                 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);
27639                 
27640                 break;
27641             case 270 :
27642                 
27643                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27644                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27645         
27646                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27647                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27648                     break;
27649                 }
27650                 
27651                 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);
27652                 
27653                 break;
27654             default : 
27655                 break;
27656         }
27657         
27658         this.previewEl.appendChild(this.canvasEl);
27659         
27660         this.setCanvasPosition();
27661     },
27662     
27663     crop : function()
27664     {
27665         if(!this.canvasLoaded){
27666             return;
27667         }
27668         
27669         var imageCanvas = document.createElement("canvas");
27670         
27671         var imageContext = imageCanvas.getContext("2d");
27672         
27673         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27674         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27675         
27676         var center = imageCanvas.width / 2;
27677         
27678         imageContext.translate(center, center);
27679         
27680         imageContext.rotate(this.rotate * Math.PI / 180);
27681         
27682         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27683         
27684         var canvas = document.createElement("canvas");
27685         
27686         var context = canvas.getContext("2d");
27687                 
27688         canvas.width = this.minWidth;
27689         canvas.height = this.minHeight;
27690
27691         switch (this.rotate) {
27692             case 0 :
27693                 
27694                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27695                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27696                 
27697                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27698                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27699                 
27700                 var targetWidth = this.minWidth - 2 * x;
27701                 var targetHeight = this.minHeight - 2 * y;
27702                 
27703                 var scale = 1;
27704                 
27705                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27706                     scale = targetWidth / width;
27707                 }
27708                 
27709                 if(x > 0 && y == 0){
27710                     scale = targetHeight / height;
27711                 }
27712                 
27713                 if(x > 0 && y > 0){
27714                     scale = targetWidth / width;
27715                     
27716                     if(width < height){
27717                         scale = targetHeight / height;
27718                     }
27719                 }
27720                 
27721                 context.scale(scale, scale);
27722                 
27723                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27724                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27725
27726                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27727                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27728
27729                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27730                 
27731                 break;
27732             case 90 : 
27733                 
27734                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27735                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27736                 
27737                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27738                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27739                 
27740                 var targetWidth = this.minWidth - 2 * x;
27741                 var targetHeight = this.minHeight - 2 * y;
27742                 
27743                 var scale = 1;
27744                 
27745                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27746                     scale = targetWidth / width;
27747                 }
27748                 
27749                 if(x > 0 && y == 0){
27750                     scale = targetHeight / height;
27751                 }
27752                 
27753                 if(x > 0 && y > 0){
27754                     scale = targetWidth / width;
27755                     
27756                     if(width < height){
27757                         scale = targetHeight / height;
27758                     }
27759                 }
27760                 
27761                 context.scale(scale, scale);
27762                 
27763                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27764                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27765
27766                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27767                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27768                 
27769                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27770                 
27771                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27772                 
27773                 break;
27774             case 180 :
27775                 
27776                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27777                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27778                 
27779                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27780                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27781                 
27782                 var targetWidth = this.minWidth - 2 * x;
27783                 var targetHeight = this.minHeight - 2 * y;
27784                 
27785                 var scale = 1;
27786                 
27787                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27788                     scale = targetWidth / width;
27789                 }
27790                 
27791                 if(x > 0 && y == 0){
27792                     scale = targetHeight / height;
27793                 }
27794                 
27795                 if(x > 0 && y > 0){
27796                     scale = targetWidth / width;
27797                     
27798                     if(width < height){
27799                         scale = targetHeight / height;
27800                     }
27801                 }
27802                 
27803                 context.scale(scale, scale);
27804                 
27805                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27806                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27807
27808                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27809                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27810
27811                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27812                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27813                 
27814                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27815                 
27816                 break;
27817             case 270 :
27818                 
27819                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27820                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27821                 
27822                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27823                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27824                 
27825                 var targetWidth = this.minWidth - 2 * x;
27826                 var targetHeight = this.minHeight - 2 * y;
27827                 
27828                 var scale = 1;
27829                 
27830                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27831                     scale = targetWidth / width;
27832                 }
27833                 
27834                 if(x > 0 && y == 0){
27835                     scale = targetHeight / height;
27836                 }
27837                 
27838                 if(x > 0 && y > 0){
27839                     scale = targetWidth / width;
27840                     
27841                     if(width < height){
27842                         scale = targetHeight / height;
27843                     }
27844                 }
27845                 
27846                 context.scale(scale, scale);
27847                 
27848                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27849                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27850
27851                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27852                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27853                 
27854                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27855                 
27856                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27857                 
27858                 break;
27859             default : 
27860                 break;
27861         }
27862         
27863         this.cropData = canvas.toDataURL(this.cropType);
27864         
27865         if(this.fireEvent('crop', this, this.cropData) !== false){
27866             this.process(this.file, this.cropData);
27867         }
27868         
27869         return;
27870         
27871     },
27872     
27873     setThumbBoxSize : function()
27874     {
27875         var width, height;
27876         
27877         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27878             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27879             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27880             
27881             this.minWidth = width;
27882             this.minHeight = height;
27883             
27884             if(this.rotate == 90 || this.rotate == 270){
27885                 this.minWidth = height;
27886                 this.minHeight = width;
27887             }
27888         }
27889         
27890         height = 300;
27891         width = Math.ceil(this.minWidth * height / this.minHeight);
27892         
27893         if(this.minWidth > this.minHeight){
27894             width = 300;
27895             height = Math.ceil(this.minHeight * width / this.minWidth);
27896         }
27897         
27898         this.thumbEl.setStyle({
27899             width : width + 'px',
27900             height : height + 'px'
27901         });
27902
27903         return;
27904             
27905     },
27906     
27907     setThumbBoxPosition : function()
27908     {
27909         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27910         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27911         
27912         this.thumbEl.setLeft(x);
27913         this.thumbEl.setTop(y);
27914         
27915     },
27916     
27917     baseRotateLevel : function()
27918     {
27919         this.baseRotate = 1;
27920         
27921         if(
27922                 typeof(this.exif) != 'undefined' &&
27923                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27924                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27925         ){
27926             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27927         }
27928         
27929         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27930         
27931     },
27932     
27933     baseScaleLevel : function()
27934     {
27935         var width, height;
27936         
27937         if(this.isDocument){
27938             
27939             if(this.baseRotate == 6 || this.baseRotate == 8){
27940             
27941                 height = this.thumbEl.getHeight();
27942                 this.baseScale = height / this.imageEl.OriginWidth;
27943
27944                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27945                     width = this.thumbEl.getWidth();
27946                     this.baseScale = width / this.imageEl.OriginHeight;
27947                 }
27948
27949                 return;
27950             }
27951
27952             height = this.thumbEl.getHeight();
27953             this.baseScale = height / this.imageEl.OriginHeight;
27954
27955             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27956                 width = this.thumbEl.getWidth();
27957                 this.baseScale = width / this.imageEl.OriginWidth;
27958             }
27959
27960             return;
27961         }
27962         
27963         if(this.baseRotate == 6 || this.baseRotate == 8){
27964             
27965             width = this.thumbEl.getHeight();
27966             this.baseScale = width / this.imageEl.OriginHeight;
27967             
27968             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27969                 height = this.thumbEl.getWidth();
27970                 this.baseScale = height / this.imageEl.OriginHeight;
27971             }
27972             
27973             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27974                 height = this.thumbEl.getWidth();
27975                 this.baseScale = height / this.imageEl.OriginHeight;
27976                 
27977                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27978                     width = this.thumbEl.getHeight();
27979                     this.baseScale = width / this.imageEl.OriginWidth;
27980                 }
27981             }
27982             
27983             return;
27984         }
27985         
27986         width = this.thumbEl.getWidth();
27987         this.baseScale = width / this.imageEl.OriginWidth;
27988         
27989         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27990             height = this.thumbEl.getHeight();
27991             this.baseScale = height / this.imageEl.OriginHeight;
27992         }
27993         
27994         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27995             
27996             height = this.thumbEl.getHeight();
27997             this.baseScale = height / this.imageEl.OriginHeight;
27998             
27999             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28000                 width = this.thumbEl.getWidth();
28001                 this.baseScale = width / this.imageEl.OriginWidth;
28002             }
28003             
28004         }
28005         
28006         return;
28007     },
28008     
28009     getScaleLevel : function()
28010     {
28011         return this.baseScale * Math.pow(1.1, this.scale);
28012     },
28013     
28014     onTouchStart : function(e)
28015     {
28016         if(!this.canvasLoaded){
28017             this.beforeSelectFile(e);
28018             return;
28019         }
28020         
28021         var touches = e.browserEvent.touches;
28022         
28023         if(!touches){
28024             return;
28025         }
28026         
28027         if(touches.length == 1){
28028             this.onMouseDown(e);
28029             return;
28030         }
28031         
28032         if(touches.length != 2){
28033             return;
28034         }
28035         
28036         var coords = [];
28037         
28038         for(var i = 0, finger; finger = touches[i]; i++){
28039             coords.push(finger.pageX, finger.pageY);
28040         }
28041         
28042         var x = Math.pow(coords[0] - coords[2], 2);
28043         var y = Math.pow(coords[1] - coords[3], 2);
28044         
28045         this.startDistance = Math.sqrt(x + y);
28046         
28047         this.startScale = this.scale;
28048         
28049         this.pinching = true;
28050         this.dragable = false;
28051         
28052     },
28053     
28054     onTouchMove : function(e)
28055     {
28056         if(!this.pinching && !this.dragable){
28057             return;
28058         }
28059         
28060         var touches = e.browserEvent.touches;
28061         
28062         if(!touches){
28063             return;
28064         }
28065         
28066         if(this.dragable){
28067             this.onMouseMove(e);
28068             return;
28069         }
28070         
28071         var coords = [];
28072         
28073         for(var i = 0, finger; finger = touches[i]; i++){
28074             coords.push(finger.pageX, finger.pageY);
28075         }
28076         
28077         var x = Math.pow(coords[0] - coords[2], 2);
28078         var y = Math.pow(coords[1] - coords[3], 2);
28079         
28080         this.endDistance = Math.sqrt(x + y);
28081         
28082         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28083         
28084         if(!this.zoomable()){
28085             this.scale = this.startScale;
28086             return;
28087         }
28088         
28089         this.draw();
28090         
28091     },
28092     
28093     onTouchEnd : function(e)
28094     {
28095         this.pinching = false;
28096         this.dragable = false;
28097         
28098     },
28099     
28100     process : function(file, crop)
28101     {
28102         if(this.loadMask){
28103             this.maskEl.mask(this.loadingText);
28104         }
28105         
28106         this.xhr = new XMLHttpRequest();
28107         
28108         file.xhr = this.xhr;
28109
28110         this.xhr.open(this.method, this.url, true);
28111         
28112         var headers = {
28113             "Accept": "application/json",
28114             "Cache-Control": "no-cache",
28115             "X-Requested-With": "XMLHttpRequest"
28116         };
28117         
28118         for (var headerName in headers) {
28119             var headerValue = headers[headerName];
28120             if (headerValue) {
28121                 this.xhr.setRequestHeader(headerName, headerValue);
28122             }
28123         }
28124         
28125         var _this = this;
28126         
28127         this.xhr.onload = function()
28128         {
28129             _this.xhrOnLoad(_this.xhr);
28130         }
28131         
28132         this.xhr.onerror = function()
28133         {
28134             _this.xhrOnError(_this.xhr);
28135         }
28136         
28137         var formData = new FormData();
28138
28139         formData.append('returnHTML', 'NO');
28140         
28141         if(crop){
28142             formData.append('crop', crop);
28143         }
28144         
28145         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28146             formData.append(this.paramName, file, file.name);
28147         }
28148         
28149         if(typeof(file.filename) != 'undefined'){
28150             formData.append('filename', file.filename);
28151         }
28152         
28153         if(typeof(file.mimetype) != 'undefined'){
28154             formData.append('mimetype', file.mimetype);
28155         }
28156         
28157         if(this.fireEvent('arrange', this, formData) != false){
28158             this.xhr.send(formData);
28159         };
28160     },
28161     
28162     xhrOnLoad : function(xhr)
28163     {
28164         if(this.loadMask){
28165             this.maskEl.unmask();
28166         }
28167         
28168         if (xhr.readyState !== 4) {
28169             this.fireEvent('exception', this, xhr);
28170             return;
28171         }
28172
28173         var response = Roo.decode(xhr.responseText);
28174         
28175         if(!response.success){
28176             this.fireEvent('exception', this, xhr);
28177             return;
28178         }
28179         
28180         var response = Roo.decode(xhr.responseText);
28181         
28182         this.fireEvent('upload', this, response);
28183         
28184     },
28185     
28186     xhrOnError : function()
28187     {
28188         if(this.loadMask){
28189             this.maskEl.unmask();
28190         }
28191         
28192         Roo.log('xhr on error');
28193         
28194         var response = Roo.decode(xhr.responseText);
28195           
28196         Roo.log(response);
28197         
28198     },
28199     
28200     prepare : function(file)
28201     {   
28202         if(this.loadMask){
28203             this.maskEl.mask(this.loadingText);
28204         }
28205         
28206         this.file = false;
28207         this.exif = {};
28208         
28209         if(typeof(file) === 'string'){
28210             this.loadCanvas(file);
28211             return;
28212         }
28213         
28214         if(!file || !this.urlAPI){
28215             return;
28216         }
28217         
28218         this.file = file;
28219         this.cropType = file.type;
28220         
28221         var _this = this;
28222         
28223         if(this.fireEvent('prepare', this, this.file) != false){
28224             
28225             var reader = new FileReader();
28226             
28227             reader.onload = function (e) {
28228                 if (e.target.error) {
28229                     Roo.log(e.target.error);
28230                     return;
28231                 }
28232                 
28233                 var buffer = e.target.result,
28234                     dataView = new DataView(buffer),
28235                     offset = 2,
28236                     maxOffset = dataView.byteLength - 4,
28237                     markerBytes,
28238                     markerLength;
28239                 
28240                 if (dataView.getUint16(0) === 0xffd8) {
28241                     while (offset < maxOffset) {
28242                         markerBytes = dataView.getUint16(offset);
28243                         
28244                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28245                             markerLength = dataView.getUint16(offset + 2) + 2;
28246                             if (offset + markerLength > dataView.byteLength) {
28247                                 Roo.log('Invalid meta data: Invalid segment size.');
28248                                 break;
28249                             }
28250                             
28251                             if(markerBytes == 0xffe1){
28252                                 _this.parseExifData(
28253                                     dataView,
28254                                     offset,
28255                                     markerLength
28256                                 );
28257                             }
28258                             
28259                             offset += markerLength;
28260                             
28261                             continue;
28262                         }
28263                         
28264                         break;
28265                     }
28266                     
28267                 }
28268                 
28269                 var url = _this.urlAPI.createObjectURL(_this.file);
28270                 
28271                 _this.loadCanvas(url);
28272                 
28273                 return;
28274             }
28275             
28276             reader.readAsArrayBuffer(this.file);
28277             
28278         }
28279         
28280     },
28281     
28282     parseExifData : function(dataView, offset, length)
28283     {
28284         var tiffOffset = offset + 10,
28285             littleEndian,
28286             dirOffset;
28287     
28288         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28289             // No Exif data, might be XMP data instead
28290             return;
28291         }
28292         
28293         // Check for the ASCII code for "Exif" (0x45786966):
28294         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28295             // No Exif data, might be XMP data instead
28296             return;
28297         }
28298         if (tiffOffset + 8 > dataView.byteLength) {
28299             Roo.log('Invalid Exif data: Invalid segment size.');
28300             return;
28301         }
28302         // Check for the two null bytes:
28303         if (dataView.getUint16(offset + 8) !== 0x0000) {
28304             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28305             return;
28306         }
28307         // Check the byte alignment:
28308         switch (dataView.getUint16(tiffOffset)) {
28309         case 0x4949:
28310             littleEndian = true;
28311             break;
28312         case 0x4D4D:
28313             littleEndian = false;
28314             break;
28315         default:
28316             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28317             return;
28318         }
28319         // Check for the TIFF tag marker (0x002A):
28320         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28321             Roo.log('Invalid Exif data: Missing TIFF marker.');
28322             return;
28323         }
28324         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28325         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28326         
28327         this.parseExifTags(
28328             dataView,
28329             tiffOffset,
28330             tiffOffset + dirOffset,
28331             littleEndian
28332         );
28333     },
28334     
28335     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28336     {
28337         var tagsNumber,
28338             dirEndOffset,
28339             i;
28340         if (dirOffset + 6 > dataView.byteLength) {
28341             Roo.log('Invalid Exif data: Invalid directory offset.');
28342             return;
28343         }
28344         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28345         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28346         if (dirEndOffset + 4 > dataView.byteLength) {
28347             Roo.log('Invalid Exif data: Invalid directory size.');
28348             return;
28349         }
28350         for (i = 0; i < tagsNumber; i += 1) {
28351             this.parseExifTag(
28352                 dataView,
28353                 tiffOffset,
28354                 dirOffset + 2 + 12 * i, // tag offset
28355                 littleEndian
28356             );
28357         }
28358         // Return the offset to the next directory:
28359         return dataView.getUint32(dirEndOffset, littleEndian);
28360     },
28361     
28362     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28363     {
28364         var tag = dataView.getUint16(offset, littleEndian);
28365         
28366         this.exif[tag] = this.getExifValue(
28367             dataView,
28368             tiffOffset,
28369             offset,
28370             dataView.getUint16(offset + 2, littleEndian), // tag type
28371             dataView.getUint32(offset + 4, littleEndian), // tag length
28372             littleEndian
28373         );
28374     },
28375     
28376     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28377     {
28378         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28379             tagSize,
28380             dataOffset,
28381             values,
28382             i,
28383             str,
28384             c;
28385     
28386         if (!tagType) {
28387             Roo.log('Invalid Exif data: Invalid tag type.');
28388             return;
28389         }
28390         
28391         tagSize = tagType.size * length;
28392         // Determine if the value is contained in the dataOffset bytes,
28393         // or if the value at the dataOffset is a pointer to the actual data:
28394         dataOffset = tagSize > 4 ?
28395                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28396         if (dataOffset + tagSize > dataView.byteLength) {
28397             Roo.log('Invalid Exif data: Invalid data offset.');
28398             return;
28399         }
28400         if (length === 1) {
28401             return tagType.getValue(dataView, dataOffset, littleEndian);
28402         }
28403         values = [];
28404         for (i = 0; i < length; i += 1) {
28405             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28406         }
28407         
28408         if (tagType.ascii) {
28409             str = '';
28410             // Concatenate the chars:
28411             for (i = 0; i < values.length; i += 1) {
28412                 c = values[i];
28413                 // Ignore the terminating NULL byte(s):
28414                 if (c === '\u0000') {
28415                     break;
28416                 }
28417                 str += c;
28418             }
28419             return str;
28420         }
28421         return values;
28422     }
28423     
28424 });
28425
28426 Roo.apply(Roo.bootstrap.UploadCropbox, {
28427     tags : {
28428         'Orientation': 0x0112
28429     },
28430     
28431     Orientation: {
28432             1: 0, //'top-left',
28433 //            2: 'top-right',
28434             3: 180, //'bottom-right',
28435 //            4: 'bottom-left',
28436 //            5: 'left-top',
28437             6: 90, //'right-top',
28438 //            7: 'right-bottom',
28439             8: 270 //'left-bottom'
28440     },
28441     
28442     exifTagTypes : {
28443         // byte, 8-bit unsigned int:
28444         1: {
28445             getValue: function (dataView, dataOffset) {
28446                 return dataView.getUint8(dataOffset);
28447             },
28448             size: 1
28449         },
28450         // ascii, 8-bit byte:
28451         2: {
28452             getValue: function (dataView, dataOffset) {
28453                 return String.fromCharCode(dataView.getUint8(dataOffset));
28454             },
28455             size: 1,
28456             ascii: true
28457         },
28458         // short, 16 bit int:
28459         3: {
28460             getValue: function (dataView, dataOffset, littleEndian) {
28461                 return dataView.getUint16(dataOffset, littleEndian);
28462             },
28463             size: 2
28464         },
28465         // long, 32 bit int:
28466         4: {
28467             getValue: function (dataView, dataOffset, littleEndian) {
28468                 return dataView.getUint32(dataOffset, littleEndian);
28469             },
28470             size: 4
28471         },
28472         // rational = two long values, first is numerator, second is denominator:
28473         5: {
28474             getValue: function (dataView, dataOffset, littleEndian) {
28475                 return dataView.getUint32(dataOffset, littleEndian) /
28476                     dataView.getUint32(dataOffset + 4, littleEndian);
28477             },
28478             size: 8
28479         },
28480         // slong, 32 bit signed int:
28481         9: {
28482             getValue: function (dataView, dataOffset, littleEndian) {
28483                 return dataView.getInt32(dataOffset, littleEndian);
28484             },
28485             size: 4
28486         },
28487         // srational, two slongs, first is numerator, second is denominator:
28488         10: {
28489             getValue: function (dataView, dataOffset, littleEndian) {
28490                 return dataView.getInt32(dataOffset, littleEndian) /
28491                     dataView.getInt32(dataOffset + 4, littleEndian);
28492             },
28493             size: 8
28494         }
28495     },
28496     
28497     footer : {
28498         STANDARD : [
28499             {
28500                 tag : 'div',
28501                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28502                 action : 'rotate-left',
28503                 cn : [
28504                     {
28505                         tag : 'button',
28506                         cls : 'btn btn-default',
28507                         html : '<i class="fa fa-undo"></i>'
28508                     }
28509                 ]
28510             },
28511             {
28512                 tag : 'div',
28513                 cls : 'btn-group roo-upload-cropbox-picture',
28514                 action : 'picture',
28515                 cn : [
28516                     {
28517                         tag : 'button',
28518                         cls : 'btn btn-default',
28519                         html : '<i class="fa fa-picture-o"></i>'
28520                     }
28521                 ]
28522             },
28523             {
28524                 tag : 'div',
28525                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28526                 action : 'rotate-right',
28527                 cn : [
28528                     {
28529                         tag : 'button',
28530                         cls : 'btn btn-default',
28531                         html : '<i class="fa fa-repeat"></i>'
28532                     }
28533                 ]
28534             }
28535         ],
28536         DOCUMENT : [
28537             {
28538                 tag : 'div',
28539                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28540                 action : 'rotate-left',
28541                 cn : [
28542                     {
28543                         tag : 'button',
28544                         cls : 'btn btn-default',
28545                         html : '<i class="fa fa-undo"></i>'
28546                     }
28547                 ]
28548             },
28549             {
28550                 tag : 'div',
28551                 cls : 'btn-group roo-upload-cropbox-download',
28552                 action : 'download',
28553                 cn : [
28554                     {
28555                         tag : 'button',
28556                         cls : 'btn btn-default',
28557                         html : '<i class="fa fa-download"></i>'
28558                     }
28559                 ]
28560             },
28561             {
28562                 tag : 'div',
28563                 cls : 'btn-group roo-upload-cropbox-crop',
28564                 action : 'crop',
28565                 cn : [
28566                     {
28567                         tag : 'button',
28568                         cls : 'btn btn-default',
28569                         html : '<i class="fa fa-crop"></i>'
28570                     }
28571                 ]
28572             },
28573             {
28574                 tag : 'div',
28575                 cls : 'btn-group roo-upload-cropbox-trash',
28576                 action : 'trash',
28577                 cn : [
28578                     {
28579                         tag : 'button',
28580                         cls : 'btn btn-default',
28581                         html : '<i class="fa fa-trash"></i>'
28582                     }
28583                 ]
28584             },
28585             {
28586                 tag : 'div',
28587                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28588                 action : 'rotate-right',
28589                 cn : [
28590                     {
28591                         tag : 'button',
28592                         cls : 'btn btn-default',
28593                         html : '<i class="fa fa-repeat"></i>'
28594                     }
28595                 ]
28596             }
28597         ],
28598         ROTATOR : [
28599             {
28600                 tag : 'div',
28601                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28602                 action : 'rotate-left',
28603                 cn : [
28604                     {
28605                         tag : 'button',
28606                         cls : 'btn btn-default',
28607                         html : '<i class="fa fa-undo"></i>'
28608                     }
28609                 ]
28610             },
28611             {
28612                 tag : 'div',
28613                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28614                 action : 'rotate-right',
28615                 cn : [
28616                     {
28617                         tag : 'button',
28618                         cls : 'btn btn-default',
28619                         html : '<i class="fa fa-repeat"></i>'
28620                     }
28621                 ]
28622             }
28623         ]
28624     }
28625 });
28626
28627 /*
28628 * Licence: LGPL
28629 */
28630
28631 /**
28632  * @class Roo.bootstrap.DocumentManager
28633  * @extends Roo.bootstrap.Component
28634  * Bootstrap DocumentManager class
28635  * @cfg {String} paramName default 'imageUpload'
28636  * @cfg {String} toolTipName default 'filename'
28637  * @cfg {String} method default POST
28638  * @cfg {String} url action url
28639  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28640  * @cfg {Boolean} multiple multiple upload default true
28641  * @cfg {Number} thumbSize default 300
28642  * @cfg {String} fieldLabel
28643  * @cfg {Number} labelWidth default 4
28644  * @cfg {String} labelAlign (left|top) default left
28645  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28646 * @cfg {Number} labellg set the width of label (1-12)
28647  * @cfg {Number} labelmd set the width of label (1-12)
28648  * @cfg {Number} labelsm set the width of label (1-12)
28649  * @cfg {Number} labelxs set the width of label (1-12)
28650  * 
28651  * @constructor
28652  * Create a new DocumentManager
28653  * @param {Object} config The config object
28654  */
28655
28656 Roo.bootstrap.DocumentManager = function(config){
28657     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28658     
28659     this.files = [];
28660     this.delegates = [];
28661     
28662     this.addEvents({
28663         /**
28664          * @event initial
28665          * Fire when initial the DocumentManager
28666          * @param {Roo.bootstrap.DocumentManager} this
28667          */
28668         "initial" : true,
28669         /**
28670          * @event inspect
28671          * inspect selected file
28672          * @param {Roo.bootstrap.DocumentManager} this
28673          * @param {File} file
28674          */
28675         "inspect" : true,
28676         /**
28677          * @event exception
28678          * Fire when xhr load exception
28679          * @param {Roo.bootstrap.DocumentManager} this
28680          * @param {XMLHttpRequest} xhr
28681          */
28682         "exception" : true,
28683         /**
28684          * @event afterupload
28685          * Fire when xhr load exception
28686          * @param {Roo.bootstrap.DocumentManager} this
28687          * @param {XMLHttpRequest} xhr
28688          */
28689         "afterupload" : true,
28690         /**
28691          * @event prepare
28692          * prepare the form data
28693          * @param {Roo.bootstrap.DocumentManager} this
28694          * @param {Object} formData
28695          */
28696         "prepare" : true,
28697         /**
28698          * @event remove
28699          * Fire when remove the file
28700          * @param {Roo.bootstrap.DocumentManager} this
28701          * @param {Object} file
28702          */
28703         "remove" : true,
28704         /**
28705          * @event refresh
28706          * Fire after refresh the file
28707          * @param {Roo.bootstrap.DocumentManager} this
28708          */
28709         "refresh" : true,
28710         /**
28711          * @event click
28712          * Fire after click the image
28713          * @param {Roo.bootstrap.DocumentManager} this
28714          * @param {Object} file
28715          */
28716         "click" : true,
28717         /**
28718          * @event edit
28719          * Fire when upload a image and editable set to true
28720          * @param {Roo.bootstrap.DocumentManager} this
28721          * @param {Object} file
28722          */
28723         "edit" : true,
28724         /**
28725          * @event beforeselectfile
28726          * Fire before select file
28727          * @param {Roo.bootstrap.DocumentManager} this
28728          */
28729         "beforeselectfile" : true,
28730         /**
28731          * @event process
28732          * Fire before process file
28733          * @param {Roo.bootstrap.DocumentManager} this
28734          * @param {Object} file
28735          */
28736         "process" : true,
28737         /**
28738          * @event previewrendered
28739          * Fire when preview rendered
28740          * @param {Roo.bootstrap.DocumentManager} this
28741          * @param {Object} file
28742          */
28743         "previewrendered" : true,
28744         /**
28745          */
28746         "previewResize" : true
28747         
28748     });
28749 };
28750
28751 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28752     
28753     boxes : 0,
28754     inputName : '',
28755     thumbSize : 300,
28756     multiple : true,
28757     files : false,
28758     method : 'POST',
28759     url : '',
28760     paramName : 'imageUpload',
28761     toolTipName : 'filename',
28762     fieldLabel : '',
28763     labelWidth : 4,
28764     labelAlign : 'left',
28765     editable : true,
28766     delegates : false,
28767     xhr : false, 
28768     
28769     labellg : 0,
28770     labelmd : 0,
28771     labelsm : 0,
28772     labelxs : 0,
28773     
28774     getAutoCreate : function()
28775     {   
28776         var managerWidget = {
28777             tag : 'div',
28778             cls : 'roo-document-manager',
28779             cn : [
28780                 {
28781                     tag : 'input',
28782                     cls : 'roo-document-manager-selector',
28783                     type : 'file'
28784                 },
28785                 {
28786                     tag : 'div',
28787                     cls : 'roo-document-manager-uploader',
28788                     cn : [
28789                         {
28790                             tag : 'div',
28791                             cls : 'roo-document-manager-upload-btn',
28792                             html : '<i class="fa fa-plus"></i>'
28793                         }
28794                     ]
28795                     
28796                 }
28797             ]
28798         };
28799         
28800         var content = [
28801             {
28802                 tag : 'div',
28803                 cls : 'column col-md-12',
28804                 cn : managerWidget
28805             }
28806         ];
28807         
28808         if(this.fieldLabel.length){
28809             
28810             content = [
28811                 {
28812                     tag : 'div',
28813                     cls : 'column col-md-12',
28814                     html : this.fieldLabel
28815                 },
28816                 {
28817                     tag : 'div',
28818                     cls : 'column col-md-12',
28819                     cn : managerWidget
28820                 }
28821             ];
28822
28823             if(this.labelAlign == 'left'){
28824                 content = [
28825                     {
28826                         tag : 'div',
28827                         cls : 'column',
28828                         html : this.fieldLabel
28829                     },
28830                     {
28831                         tag : 'div',
28832                         cls : 'column',
28833                         cn : managerWidget
28834                     }
28835                 ];
28836                 
28837                 if(this.labelWidth > 12){
28838                     content[0].style = "width: " + this.labelWidth + 'px';
28839                 }
28840
28841                 if(this.labelWidth < 13 && this.labelmd == 0){
28842                     this.labelmd = this.labelWidth;
28843                 }
28844
28845                 if(this.labellg > 0){
28846                     content[0].cls += ' col-lg-' + this.labellg;
28847                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28848                 }
28849
28850                 if(this.labelmd > 0){
28851                     content[0].cls += ' col-md-' + this.labelmd;
28852                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28853                 }
28854
28855                 if(this.labelsm > 0){
28856                     content[0].cls += ' col-sm-' + this.labelsm;
28857                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28858                 }
28859
28860                 if(this.labelxs > 0){
28861                     content[0].cls += ' col-xs-' + this.labelxs;
28862                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28863                 }
28864                 
28865             }
28866         }
28867         
28868         var cfg = {
28869             tag : 'div',
28870             cls : 'row clearfix',
28871             cn : content
28872         };
28873         
28874         return cfg;
28875         
28876     },
28877     
28878     initEvents : function()
28879     {
28880         this.managerEl = this.el.select('.roo-document-manager', true).first();
28881         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28882         
28883         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28884         this.selectorEl.hide();
28885         
28886         if(this.multiple){
28887             this.selectorEl.attr('multiple', 'multiple');
28888         }
28889         
28890         this.selectorEl.on('change', this.onFileSelected, this);
28891         
28892         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28893         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28894         
28895         this.uploader.on('click', this.onUploaderClick, this);
28896         
28897         this.renderProgressDialog();
28898         
28899         var _this = this;
28900         
28901         window.addEventListener("resize", function() { _this.refresh(); } );
28902         
28903         this.fireEvent('initial', this);
28904     },
28905     
28906     renderProgressDialog : function()
28907     {
28908         var _this = this;
28909         
28910         this.progressDialog = new Roo.bootstrap.Modal({
28911             cls : 'roo-document-manager-progress-dialog',
28912             allow_close : false,
28913             title : '',
28914             buttons : [
28915                 {
28916                     name  :'cancel',
28917                     weight : 'danger',
28918                     html : 'Cancel'
28919                 }
28920             ], 
28921             listeners : { 
28922                 btnclick : function() {
28923                     _this.uploadCancel();
28924                     this.hide();
28925                 }
28926             }
28927         });
28928          
28929         this.progressDialog.render(Roo.get(document.body));
28930          
28931         this.progress = new Roo.bootstrap.Progress({
28932             cls : 'roo-document-manager-progress',
28933             active : true,
28934             striped : true
28935         });
28936         
28937         this.progress.render(this.progressDialog.getChildContainer());
28938         
28939         this.progressBar = new Roo.bootstrap.ProgressBar({
28940             cls : 'roo-document-manager-progress-bar',
28941             aria_valuenow : 0,
28942             aria_valuemin : 0,
28943             aria_valuemax : 12,
28944             panel : 'success'
28945         });
28946         
28947         this.progressBar.render(this.progress.getChildContainer());
28948     },
28949     
28950     onUploaderClick : function(e)
28951     {
28952         e.preventDefault();
28953      
28954         if(this.fireEvent('beforeselectfile', this) != false){
28955             this.selectorEl.dom.click();
28956         }
28957         
28958     },
28959     
28960     onFileSelected : function(e)
28961     {
28962         e.preventDefault();
28963         
28964         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28965             return;
28966         }
28967         
28968         Roo.each(this.selectorEl.dom.files, function(file){
28969             if(this.fireEvent('inspect', this, file) != false){
28970                 this.files.push(file);
28971             }
28972         }, this);
28973         
28974         this.queue();
28975         
28976     },
28977     
28978     queue : function()
28979     {
28980         this.selectorEl.dom.value = '';
28981         
28982         if(!this.files || !this.files.length){
28983             return;
28984         }
28985         
28986         if(this.boxes > 0 && this.files.length > this.boxes){
28987             this.files = this.files.slice(0, this.boxes);
28988         }
28989         
28990         this.uploader.show();
28991         
28992         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28993             this.uploader.hide();
28994         }
28995         
28996         var _this = this;
28997         
28998         var files = [];
28999         
29000         var docs = [];
29001         
29002         Roo.each(this.files, function(file){
29003             
29004             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29005                 var f = this.renderPreview(file);
29006                 files.push(f);
29007                 return;
29008             }
29009             
29010             if(file.type.indexOf('image') != -1){
29011                 this.delegates.push(
29012                     (function(){
29013                         _this.process(file);
29014                     }).createDelegate(this)
29015                 );
29016         
29017                 return;
29018             }
29019             
29020             docs.push(
29021                 (function(){
29022                     _this.process(file);
29023                 }).createDelegate(this)
29024             );
29025             
29026         }, this);
29027         
29028         this.files = files;
29029         
29030         this.delegates = this.delegates.concat(docs);
29031         
29032         if(!this.delegates.length){
29033             this.refresh();
29034             return;
29035         }
29036         
29037         this.progressBar.aria_valuemax = this.delegates.length;
29038         
29039         this.arrange();
29040         
29041         return;
29042     },
29043     
29044     arrange : function()
29045     {
29046         if(!this.delegates.length){
29047             this.progressDialog.hide();
29048             this.refresh();
29049             return;
29050         }
29051         
29052         var delegate = this.delegates.shift();
29053         
29054         this.progressDialog.show();
29055         
29056         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29057         
29058         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29059         
29060         delegate();
29061     },
29062     
29063     refresh : function()
29064     {
29065         this.uploader.show();
29066         
29067         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29068             this.uploader.hide();
29069         }
29070         
29071         Roo.isTouch ? this.closable(false) : this.closable(true);
29072         
29073         this.fireEvent('refresh', this);
29074     },
29075     
29076     onRemove : function(e, el, o)
29077     {
29078         e.preventDefault();
29079         
29080         this.fireEvent('remove', this, o);
29081         
29082     },
29083     
29084     remove : function(o)
29085     {
29086         var files = [];
29087         
29088         Roo.each(this.files, function(file){
29089             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29090                 files.push(file);
29091                 return;
29092             }
29093
29094             o.target.remove();
29095
29096         }, this);
29097         
29098         this.files = files;
29099         
29100         this.refresh();
29101     },
29102     
29103     clear : function()
29104     {
29105         Roo.each(this.files, function(file){
29106             if(!file.target){
29107                 return;
29108             }
29109             
29110             file.target.remove();
29111
29112         }, this);
29113         
29114         this.files = [];
29115         
29116         this.refresh();
29117     },
29118     
29119     onClick : function(e, el, o)
29120     {
29121         e.preventDefault();
29122         
29123         this.fireEvent('click', this, o);
29124         
29125     },
29126     
29127     closable : function(closable)
29128     {
29129         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29130             
29131             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29132             
29133             if(closable){
29134                 el.show();
29135                 return;
29136             }
29137             
29138             el.hide();
29139             
29140         }, this);
29141     },
29142     
29143     xhrOnLoad : function(xhr)
29144     {
29145         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29146             el.remove();
29147         }, this);
29148         
29149         if (xhr.readyState !== 4) {
29150             this.arrange();
29151             this.fireEvent('exception', this, xhr);
29152             return;
29153         }
29154
29155         var response = Roo.decode(xhr.responseText);
29156         
29157         if(!response.success){
29158             this.arrange();
29159             this.fireEvent('exception', this, xhr);
29160             return;
29161         }
29162         
29163         var file = this.renderPreview(response.data);
29164         
29165         this.files.push(file);
29166         
29167         this.arrange();
29168         
29169         this.fireEvent('afterupload', this, xhr);
29170         
29171     },
29172     
29173     xhrOnError : function(xhr)
29174     {
29175         Roo.log('xhr on error');
29176         
29177         var response = Roo.decode(xhr.responseText);
29178           
29179         Roo.log(response);
29180         
29181         this.arrange();
29182     },
29183     
29184     process : function(file)
29185     {
29186         if(this.fireEvent('process', this, file) !== false){
29187             if(this.editable && file.type.indexOf('image') != -1){
29188                 this.fireEvent('edit', this, file);
29189                 return;
29190             }
29191
29192             this.uploadStart(file, false);
29193
29194             return;
29195         }
29196         
29197     },
29198     
29199     uploadStart : function(file, crop)
29200     {
29201         this.xhr = new XMLHttpRequest();
29202         
29203         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29204             this.arrange();
29205             return;
29206         }
29207         
29208         file.xhr = this.xhr;
29209             
29210         this.managerEl.createChild({
29211             tag : 'div',
29212             cls : 'roo-document-manager-loading',
29213             cn : [
29214                 {
29215                     tag : 'div',
29216                     tooltip : file.name,
29217                     cls : 'roo-document-manager-thumb',
29218                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29219                 }
29220             ]
29221
29222         });
29223
29224         this.xhr.open(this.method, this.url, true);
29225         
29226         var headers = {
29227             "Accept": "application/json",
29228             "Cache-Control": "no-cache",
29229             "X-Requested-With": "XMLHttpRequest"
29230         };
29231         
29232         for (var headerName in headers) {
29233             var headerValue = headers[headerName];
29234             if (headerValue) {
29235                 this.xhr.setRequestHeader(headerName, headerValue);
29236             }
29237         }
29238         
29239         var _this = this;
29240         
29241         this.xhr.onload = function()
29242         {
29243             _this.xhrOnLoad(_this.xhr);
29244         }
29245         
29246         this.xhr.onerror = function()
29247         {
29248             _this.xhrOnError(_this.xhr);
29249         }
29250         
29251         var formData = new FormData();
29252
29253         formData.append('returnHTML', 'NO');
29254         
29255         if(crop){
29256             formData.append('crop', crop);
29257         }
29258         
29259         formData.append(this.paramName, file, file.name);
29260         
29261         var options = {
29262             file : file, 
29263             manually : false
29264         };
29265         
29266         if(this.fireEvent('prepare', this, formData, options) != false){
29267             
29268             if(options.manually){
29269                 return;
29270             }
29271             
29272             this.xhr.send(formData);
29273             return;
29274         };
29275         
29276         this.uploadCancel();
29277     },
29278     
29279     uploadCancel : function()
29280     {
29281         if (this.xhr) {
29282             this.xhr.abort();
29283         }
29284         
29285         this.delegates = [];
29286         
29287         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29288             el.remove();
29289         }, this);
29290         
29291         this.arrange();
29292     },
29293     
29294     renderPreview : function(file)
29295     {
29296         if(typeof(file.target) != 'undefined' && file.target){
29297             return file;
29298         }
29299         
29300         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29301         
29302         var previewEl = this.managerEl.createChild({
29303             tag : 'div',
29304             cls : 'roo-document-manager-preview',
29305             cn : [
29306                 {
29307                     tag : 'div',
29308                     tooltip : file[this.toolTipName],
29309                     cls : 'roo-document-manager-thumb',
29310                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29311                 },
29312                 {
29313                     tag : 'button',
29314                     cls : 'close',
29315                     html : '<i class="fa fa-times-circle"></i>'
29316                 }
29317             ]
29318         });
29319
29320         var close = previewEl.select('button.close', true).first();
29321
29322         close.on('click', this.onRemove, this, file);
29323
29324         file.target = previewEl;
29325
29326         var image = previewEl.select('img', true).first();
29327         
29328         var _this = this;
29329         
29330         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29331         
29332         image.on('click', this.onClick, this, file);
29333         
29334         this.fireEvent('previewrendered', this, file);
29335         
29336         return file;
29337         
29338     },
29339     
29340     onPreviewLoad : function(file, image)
29341     {
29342         if(typeof(file.target) == 'undefined' || !file.target){
29343             return;
29344         }
29345         
29346         var width = image.dom.naturalWidth || image.dom.width;
29347         var height = image.dom.naturalHeight || image.dom.height;
29348         
29349         if(!this.previewResize) {
29350             return;
29351         }
29352         
29353         if(width > height){
29354             file.target.addClass('wide');
29355             return;
29356         }
29357         
29358         file.target.addClass('tall');
29359         return;
29360         
29361     },
29362     
29363     uploadFromSource : function(file, crop)
29364     {
29365         this.xhr = new XMLHttpRequest();
29366         
29367         this.managerEl.createChild({
29368             tag : 'div',
29369             cls : 'roo-document-manager-loading',
29370             cn : [
29371                 {
29372                     tag : 'div',
29373                     tooltip : file.name,
29374                     cls : 'roo-document-manager-thumb',
29375                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29376                 }
29377             ]
29378
29379         });
29380
29381         this.xhr.open(this.method, this.url, true);
29382         
29383         var headers = {
29384             "Accept": "application/json",
29385             "Cache-Control": "no-cache",
29386             "X-Requested-With": "XMLHttpRequest"
29387         };
29388         
29389         for (var headerName in headers) {
29390             var headerValue = headers[headerName];
29391             if (headerValue) {
29392                 this.xhr.setRequestHeader(headerName, headerValue);
29393             }
29394         }
29395         
29396         var _this = this;
29397         
29398         this.xhr.onload = function()
29399         {
29400             _this.xhrOnLoad(_this.xhr);
29401         }
29402         
29403         this.xhr.onerror = function()
29404         {
29405             _this.xhrOnError(_this.xhr);
29406         }
29407         
29408         var formData = new FormData();
29409
29410         formData.append('returnHTML', 'NO');
29411         
29412         formData.append('crop', crop);
29413         
29414         if(typeof(file.filename) != 'undefined'){
29415             formData.append('filename', file.filename);
29416         }
29417         
29418         if(typeof(file.mimetype) != 'undefined'){
29419             formData.append('mimetype', file.mimetype);
29420         }
29421         
29422         Roo.log(formData);
29423         
29424         if(this.fireEvent('prepare', this, formData) != false){
29425             this.xhr.send(formData);
29426         };
29427     }
29428 });
29429
29430 /*
29431 * Licence: LGPL
29432 */
29433
29434 /**
29435  * @class Roo.bootstrap.DocumentViewer
29436  * @extends Roo.bootstrap.Component
29437  * Bootstrap DocumentViewer class
29438  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29439  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29440  * 
29441  * @constructor
29442  * Create a new DocumentViewer
29443  * @param {Object} config The config object
29444  */
29445
29446 Roo.bootstrap.DocumentViewer = function(config){
29447     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29448     
29449     this.addEvents({
29450         /**
29451          * @event initial
29452          * Fire after initEvent
29453          * @param {Roo.bootstrap.DocumentViewer} this
29454          */
29455         "initial" : true,
29456         /**
29457          * @event click
29458          * Fire after click
29459          * @param {Roo.bootstrap.DocumentViewer} this
29460          */
29461         "click" : true,
29462         /**
29463          * @event download
29464          * Fire after download button
29465          * @param {Roo.bootstrap.DocumentViewer} this
29466          */
29467         "download" : true,
29468         /**
29469          * @event trash
29470          * Fire after trash button
29471          * @param {Roo.bootstrap.DocumentViewer} this
29472          */
29473         "trash" : true
29474         
29475     });
29476 };
29477
29478 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29479     
29480     showDownload : true,
29481     
29482     showTrash : true,
29483     
29484     getAutoCreate : function()
29485     {
29486         var cfg = {
29487             tag : 'div',
29488             cls : 'roo-document-viewer',
29489             cn : [
29490                 {
29491                     tag : 'div',
29492                     cls : 'roo-document-viewer-body',
29493                     cn : [
29494                         {
29495                             tag : 'div',
29496                             cls : 'roo-document-viewer-thumb',
29497                             cn : [
29498                                 {
29499                                     tag : 'img',
29500                                     cls : 'roo-document-viewer-image'
29501                                 }
29502                             ]
29503                         }
29504                     ]
29505                 },
29506                 {
29507                     tag : 'div',
29508                     cls : 'roo-document-viewer-footer',
29509                     cn : {
29510                         tag : 'div',
29511                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29512                         cn : [
29513                             {
29514                                 tag : 'div',
29515                                 cls : 'btn-group roo-document-viewer-download',
29516                                 cn : [
29517                                     {
29518                                         tag : 'button',
29519                                         cls : 'btn btn-default',
29520                                         html : '<i class="fa fa-download"></i>'
29521                                     }
29522                                 ]
29523                             },
29524                             {
29525                                 tag : 'div',
29526                                 cls : 'btn-group roo-document-viewer-trash',
29527                                 cn : [
29528                                     {
29529                                         tag : 'button',
29530                                         cls : 'btn btn-default',
29531                                         html : '<i class="fa fa-trash"></i>'
29532                                     }
29533                                 ]
29534                             }
29535                         ]
29536                     }
29537                 }
29538             ]
29539         };
29540         
29541         return cfg;
29542     },
29543     
29544     initEvents : function()
29545     {
29546         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29547         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29548         
29549         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29550         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29551         
29552         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29553         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29554         
29555         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29556         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29557         
29558         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29559         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29560         
29561         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29562         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29563         
29564         this.bodyEl.on('click', this.onClick, this);
29565         this.downloadBtn.on('click', this.onDownload, this);
29566         this.trashBtn.on('click', this.onTrash, this);
29567         
29568         this.downloadBtn.hide();
29569         this.trashBtn.hide();
29570         
29571         if(this.showDownload){
29572             this.downloadBtn.show();
29573         }
29574         
29575         if(this.showTrash){
29576             this.trashBtn.show();
29577         }
29578         
29579         if(!this.showDownload && !this.showTrash) {
29580             this.footerEl.hide();
29581         }
29582         
29583     },
29584     
29585     initial : function()
29586     {
29587         this.fireEvent('initial', this);
29588         
29589     },
29590     
29591     onClick : function(e)
29592     {
29593         e.preventDefault();
29594         
29595         this.fireEvent('click', this);
29596     },
29597     
29598     onDownload : function(e)
29599     {
29600         e.preventDefault();
29601         
29602         this.fireEvent('download', this);
29603     },
29604     
29605     onTrash : function(e)
29606     {
29607         e.preventDefault();
29608         
29609         this.fireEvent('trash', this);
29610     }
29611     
29612 });
29613 /*
29614  * - LGPL
29615  *
29616  * nav progress bar
29617  * 
29618  */
29619
29620 /**
29621  * @class Roo.bootstrap.NavProgressBar
29622  * @extends Roo.bootstrap.Component
29623  * Bootstrap NavProgressBar class
29624  * 
29625  * @constructor
29626  * Create a new nav progress bar
29627  * @param {Object} config The config object
29628  */
29629
29630 Roo.bootstrap.NavProgressBar = function(config){
29631     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29632
29633     this.bullets = this.bullets || [];
29634    
29635 //    Roo.bootstrap.NavProgressBar.register(this);
29636      this.addEvents({
29637         /**
29638              * @event changed
29639              * Fires when the active item changes
29640              * @param {Roo.bootstrap.NavProgressBar} this
29641              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29642              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29643          */
29644         'changed': true
29645      });
29646     
29647 };
29648
29649 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29650     
29651     bullets : [],
29652     barItems : [],
29653     
29654     getAutoCreate : function()
29655     {
29656         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29657         
29658         cfg = {
29659             tag : 'div',
29660             cls : 'roo-navigation-bar-group',
29661             cn : [
29662                 {
29663                     tag : 'div',
29664                     cls : 'roo-navigation-top-bar'
29665                 },
29666                 {
29667                     tag : 'div',
29668                     cls : 'roo-navigation-bullets-bar',
29669                     cn : [
29670                         {
29671                             tag : 'ul',
29672                             cls : 'roo-navigation-bar'
29673                         }
29674                     ]
29675                 },
29676                 
29677                 {
29678                     tag : 'div',
29679                     cls : 'roo-navigation-bottom-bar'
29680                 }
29681             ]
29682             
29683         };
29684         
29685         return cfg;
29686         
29687     },
29688     
29689     initEvents: function() 
29690     {
29691         
29692     },
29693     
29694     onRender : function(ct, position) 
29695     {
29696         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29697         
29698         if(this.bullets.length){
29699             Roo.each(this.bullets, function(b){
29700                this.addItem(b);
29701             }, this);
29702         }
29703         
29704         this.format();
29705         
29706     },
29707     
29708     addItem : function(cfg)
29709     {
29710         var item = new Roo.bootstrap.NavProgressItem(cfg);
29711         
29712         item.parentId = this.id;
29713         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29714         
29715         if(cfg.html){
29716             var top = new Roo.bootstrap.Element({
29717                 tag : 'div',
29718                 cls : 'roo-navigation-bar-text'
29719             });
29720             
29721             var bottom = new Roo.bootstrap.Element({
29722                 tag : 'div',
29723                 cls : 'roo-navigation-bar-text'
29724             });
29725             
29726             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29727             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29728             
29729             var topText = new Roo.bootstrap.Element({
29730                 tag : 'span',
29731                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29732             });
29733             
29734             var bottomText = new Roo.bootstrap.Element({
29735                 tag : 'span',
29736                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29737             });
29738             
29739             topText.onRender(top.el, null);
29740             bottomText.onRender(bottom.el, null);
29741             
29742             item.topEl = top;
29743             item.bottomEl = bottom;
29744         }
29745         
29746         this.barItems.push(item);
29747         
29748         return item;
29749     },
29750     
29751     getActive : function()
29752     {
29753         var active = false;
29754         
29755         Roo.each(this.barItems, function(v){
29756             
29757             if (!v.isActive()) {
29758                 return;
29759             }
29760             
29761             active = v;
29762             return false;
29763             
29764         });
29765         
29766         return active;
29767     },
29768     
29769     setActiveItem : function(item)
29770     {
29771         var prev = false;
29772         
29773         Roo.each(this.barItems, function(v){
29774             if (v.rid == item.rid) {
29775                 return ;
29776             }
29777             
29778             if (v.isActive()) {
29779                 v.setActive(false);
29780                 prev = v;
29781             }
29782         });
29783
29784         item.setActive(true);
29785         
29786         this.fireEvent('changed', this, item, prev);
29787     },
29788     
29789     getBarItem: function(rid)
29790     {
29791         var ret = false;
29792         
29793         Roo.each(this.barItems, function(e) {
29794             if (e.rid != rid) {
29795                 return;
29796             }
29797             
29798             ret =  e;
29799             return false;
29800         });
29801         
29802         return ret;
29803     },
29804     
29805     indexOfItem : function(item)
29806     {
29807         var index = false;
29808         
29809         Roo.each(this.barItems, function(v, i){
29810             
29811             if (v.rid != item.rid) {
29812                 return;
29813             }
29814             
29815             index = i;
29816             return false
29817         });
29818         
29819         return index;
29820     },
29821     
29822     setActiveNext : function()
29823     {
29824         var i = this.indexOfItem(this.getActive());
29825         
29826         if (i > this.barItems.length) {
29827             return;
29828         }
29829         
29830         this.setActiveItem(this.barItems[i+1]);
29831     },
29832     
29833     setActivePrev : function()
29834     {
29835         var i = this.indexOfItem(this.getActive());
29836         
29837         if (i  < 1) {
29838             return;
29839         }
29840         
29841         this.setActiveItem(this.barItems[i-1]);
29842     },
29843     
29844     format : function()
29845     {
29846         if(!this.barItems.length){
29847             return;
29848         }
29849      
29850         var width = 100 / this.barItems.length;
29851         
29852         Roo.each(this.barItems, function(i){
29853             i.el.setStyle('width', width + '%');
29854             i.topEl.el.setStyle('width', width + '%');
29855             i.bottomEl.el.setStyle('width', width + '%');
29856         }, this);
29857         
29858     }
29859     
29860 });
29861 /*
29862  * - LGPL
29863  *
29864  * Nav Progress Item
29865  * 
29866  */
29867
29868 /**
29869  * @class Roo.bootstrap.NavProgressItem
29870  * @extends Roo.bootstrap.Component
29871  * Bootstrap NavProgressItem class
29872  * @cfg {String} rid the reference id
29873  * @cfg {Boolean} active (true|false) Is item active default false
29874  * @cfg {Boolean} disabled (true|false) Is item active default false
29875  * @cfg {String} html
29876  * @cfg {String} position (top|bottom) text position default bottom
29877  * @cfg {String} icon show icon instead of number
29878  * 
29879  * @constructor
29880  * Create a new NavProgressItem
29881  * @param {Object} config The config object
29882  */
29883 Roo.bootstrap.NavProgressItem = function(config){
29884     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29885     this.addEvents({
29886         // raw events
29887         /**
29888          * @event click
29889          * The raw click event for the entire grid.
29890          * @param {Roo.bootstrap.NavProgressItem} this
29891          * @param {Roo.EventObject} e
29892          */
29893         "click" : true
29894     });
29895    
29896 };
29897
29898 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29899     
29900     rid : '',
29901     active : false,
29902     disabled : false,
29903     html : '',
29904     position : 'bottom',
29905     icon : false,
29906     
29907     getAutoCreate : function()
29908     {
29909         var iconCls = 'roo-navigation-bar-item-icon';
29910         
29911         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29912         
29913         var cfg = {
29914             tag: 'li',
29915             cls: 'roo-navigation-bar-item',
29916             cn : [
29917                 {
29918                     tag : 'i',
29919                     cls : iconCls
29920                 }
29921             ]
29922         };
29923         
29924         if(this.active){
29925             cfg.cls += ' active';
29926         }
29927         if(this.disabled){
29928             cfg.cls += ' disabled';
29929         }
29930         
29931         return cfg;
29932     },
29933     
29934     disable : function()
29935     {
29936         this.setDisabled(true);
29937     },
29938     
29939     enable : function()
29940     {
29941         this.setDisabled(false);
29942     },
29943     
29944     initEvents: function() 
29945     {
29946         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29947         
29948         this.iconEl.on('click', this.onClick, this);
29949     },
29950     
29951     onClick : function(e)
29952     {
29953         e.preventDefault();
29954         
29955         if(this.disabled){
29956             return;
29957         }
29958         
29959         if(this.fireEvent('click', this, e) === false){
29960             return;
29961         };
29962         
29963         this.parent().setActiveItem(this);
29964     },
29965     
29966     isActive: function () 
29967     {
29968         return this.active;
29969     },
29970     
29971     setActive : function(state)
29972     {
29973         if(this.active == state){
29974             return;
29975         }
29976         
29977         this.active = state;
29978         
29979         if (state) {
29980             this.el.addClass('active');
29981             return;
29982         }
29983         
29984         this.el.removeClass('active');
29985         
29986         return;
29987     },
29988     
29989     setDisabled : function(state)
29990     {
29991         if(this.disabled == state){
29992             return;
29993         }
29994         
29995         this.disabled = state;
29996         
29997         if (state) {
29998             this.el.addClass('disabled');
29999             return;
30000         }
30001         
30002         this.el.removeClass('disabled');
30003     },
30004     
30005     tooltipEl : function()
30006     {
30007         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30008     }
30009 });
30010  
30011
30012  /*
30013  * - LGPL
30014  *
30015  * FieldLabel
30016  * 
30017  */
30018
30019 /**
30020  * @class Roo.bootstrap.FieldLabel
30021  * @extends Roo.bootstrap.Component
30022  * Bootstrap FieldLabel class
30023  * @cfg {String} html contents of the element
30024  * @cfg {String} tag tag of the element default label
30025  * @cfg {String} cls class of the element
30026  * @cfg {String} target label target 
30027  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30028  * @cfg {String} invalidClass default "text-warning"
30029  * @cfg {String} validClass default "text-success"
30030  * @cfg {String} iconTooltip default "This field is required"
30031  * @cfg {String} indicatorpos (left|right) default left
30032  * 
30033  * @constructor
30034  * Create a new FieldLabel
30035  * @param {Object} config The config object
30036  */
30037
30038 Roo.bootstrap.FieldLabel = function(config){
30039     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30040     
30041     this.addEvents({
30042             /**
30043              * @event invalid
30044              * Fires after the field has been marked as invalid.
30045              * @param {Roo.form.FieldLabel} this
30046              * @param {String} msg The validation message
30047              */
30048             invalid : true,
30049             /**
30050              * @event valid
30051              * Fires after the field has been validated with no errors.
30052              * @param {Roo.form.FieldLabel} this
30053              */
30054             valid : true
30055         });
30056 };
30057
30058 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30059     
30060     tag: 'label',
30061     cls: '',
30062     html: '',
30063     target: '',
30064     allowBlank : true,
30065     invalidClass : 'has-warning',
30066     validClass : 'has-success',
30067     iconTooltip : 'This field is required',
30068     indicatorpos : 'left',
30069     
30070     getAutoCreate : function(){
30071         
30072         var cls = "";
30073         if (!this.allowBlank) {
30074             cls  = "visible";
30075         }
30076         
30077         var cfg = {
30078             tag : this.tag,
30079             cls : 'roo-bootstrap-field-label ' + this.cls,
30080             for : this.target,
30081             cn : [
30082                 {
30083                     tag : 'i',
30084                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30085                     tooltip : this.iconTooltip
30086                 },
30087                 {
30088                     tag : 'span',
30089                     html : this.html
30090                 }
30091             ] 
30092         };
30093         
30094         if(this.indicatorpos == 'right'){
30095             var cfg = {
30096                 tag : this.tag,
30097                 cls : 'roo-bootstrap-field-label ' + this.cls,
30098                 for : this.target,
30099                 cn : [
30100                     {
30101                         tag : 'span',
30102                         html : this.html
30103                     },
30104                     {
30105                         tag : 'i',
30106                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30107                         tooltip : this.iconTooltip
30108                     }
30109                 ] 
30110             };
30111         }
30112         
30113         return cfg;
30114     },
30115     
30116     initEvents: function() 
30117     {
30118         Roo.bootstrap.Element.superclass.initEvents.call(this);
30119         
30120         this.indicator = this.indicatorEl();
30121         
30122         if(this.indicator){
30123             this.indicator.removeClass('visible');
30124             this.indicator.addClass('invisible');
30125         }
30126         
30127         Roo.bootstrap.FieldLabel.register(this);
30128     },
30129     
30130     indicatorEl : function()
30131     {
30132         var indicator = this.el.select('i.roo-required-indicator',true).first();
30133         
30134         if(!indicator){
30135             return false;
30136         }
30137         
30138         return indicator;
30139         
30140     },
30141     
30142     /**
30143      * Mark this field as valid
30144      */
30145     markValid : function()
30146     {
30147         if(this.indicator){
30148             this.indicator.removeClass('visible');
30149             this.indicator.addClass('invisible');
30150         }
30151         
30152         this.el.removeClass(this.invalidClass);
30153         
30154         this.el.addClass(this.validClass);
30155         
30156         this.fireEvent('valid', this);
30157     },
30158     
30159     /**
30160      * Mark this field as invalid
30161      * @param {String} msg The validation message
30162      */
30163     markInvalid : function(msg)
30164     {
30165         if(this.indicator){
30166             this.indicator.removeClass('invisible');
30167             this.indicator.addClass('visible');
30168         }
30169         
30170         this.el.removeClass(this.validClass);
30171         
30172         this.el.addClass(this.invalidClass);
30173         
30174         this.fireEvent('invalid', this, msg);
30175     }
30176     
30177    
30178 });
30179
30180 Roo.apply(Roo.bootstrap.FieldLabel, {
30181     
30182     groups: {},
30183     
30184      /**
30185     * register a FieldLabel Group
30186     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30187     */
30188     register : function(label)
30189     {
30190         if(this.groups.hasOwnProperty(label.target)){
30191             return;
30192         }
30193      
30194         this.groups[label.target] = label;
30195         
30196     },
30197     /**
30198     * fetch a FieldLabel Group based on the target
30199     * @param {string} target
30200     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30201     */
30202     get: function(target) {
30203         if (typeof(this.groups[target]) == 'undefined') {
30204             return false;
30205         }
30206         
30207         return this.groups[target] ;
30208     }
30209 });
30210
30211  
30212
30213  /*
30214  * - LGPL
30215  *
30216  * page DateSplitField.
30217  * 
30218  */
30219
30220
30221 /**
30222  * @class Roo.bootstrap.DateSplitField
30223  * @extends Roo.bootstrap.Component
30224  * Bootstrap DateSplitField class
30225  * @cfg {string} fieldLabel - the label associated
30226  * @cfg {Number} labelWidth set the width of label (0-12)
30227  * @cfg {String} labelAlign (top|left)
30228  * @cfg {Boolean} dayAllowBlank (true|false) default false
30229  * @cfg {Boolean} monthAllowBlank (true|false) default false
30230  * @cfg {Boolean} yearAllowBlank (true|false) default false
30231  * @cfg {string} dayPlaceholder 
30232  * @cfg {string} monthPlaceholder
30233  * @cfg {string} yearPlaceholder
30234  * @cfg {string} dayFormat default 'd'
30235  * @cfg {string} monthFormat default 'm'
30236  * @cfg {string} yearFormat default 'Y'
30237  * @cfg {Number} labellg set the width of label (1-12)
30238  * @cfg {Number} labelmd set the width of label (1-12)
30239  * @cfg {Number} labelsm set the width of label (1-12)
30240  * @cfg {Number} labelxs set the width of label (1-12)
30241
30242  *     
30243  * @constructor
30244  * Create a new DateSplitField
30245  * @param {Object} config The config object
30246  */
30247
30248 Roo.bootstrap.DateSplitField = function(config){
30249     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30250     
30251     this.addEvents({
30252         // raw events
30253          /**
30254          * @event years
30255          * getting the data of years
30256          * @param {Roo.bootstrap.DateSplitField} this
30257          * @param {Object} years
30258          */
30259         "years" : true,
30260         /**
30261          * @event days
30262          * getting the data of days
30263          * @param {Roo.bootstrap.DateSplitField} this
30264          * @param {Object} days
30265          */
30266         "days" : true,
30267         /**
30268          * @event invalid
30269          * Fires after the field has been marked as invalid.
30270          * @param {Roo.form.Field} this
30271          * @param {String} msg The validation message
30272          */
30273         invalid : true,
30274        /**
30275          * @event valid
30276          * Fires after the field has been validated with no errors.
30277          * @param {Roo.form.Field} this
30278          */
30279         valid : true
30280     });
30281 };
30282
30283 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30284     
30285     fieldLabel : '',
30286     labelAlign : 'top',
30287     labelWidth : 3,
30288     dayAllowBlank : false,
30289     monthAllowBlank : false,
30290     yearAllowBlank : false,
30291     dayPlaceholder : '',
30292     monthPlaceholder : '',
30293     yearPlaceholder : '',
30294     dayFormat : 'd',
30295     monthFormat : 'm',
30296     yearFormat : 'Y',
30297     isFormField : true,
30298     labellg : 0,
30299     labelmd : 0,
30300     labelsm : 0,
30301     labelxs : 0,
30302     
30303     getAutoCreate : function()
30304     {
30305         var cfg = {
30306             tag : 'div',
30307             cls : 'row roo-date-split-field-group',
30308             cn : [
30309                 {
30310                     tag : 'input',
30311                     type : 'hidden',
30312                     cls : 'form-hidden-field roo-date-split-field-group-value',
30313                     name : this.name
30314                 }
30315             ]
30316         };
30317         
30318         var labelCls = 'col-md-12';
30319         var contentCls = 'col-md-4';
30320         
30321         if(this.fieldLabel){
30322             
30323             var label = {
30324                 tag : 'div',
30325                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30326                 cn : [
30327                     {
30328                         tag : 'label',
30329                         html : this.fieldLabel
30330                     }
30331                 ]
30332             };
30333             
30334             if(this.labelAlign == 'left'){
30335             
30336                 if(this.labelWidth > 12){
30337                     label.style = "width: " + this.labelWidth + 'px';
30338                 }
30339
30340                 if(this.labelWidth < 13 && this.labelmd == 0){
30341                     this.labelmd = this.labelWidth;
30342                 }
30343
30344                 if(this.labellg > 0){
30345                     labelCls = ' col-lg-' + this.labellg;
30346                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30347                 }
30348
30349                 if(this.labelmd > 0){
30350                     labelCls = ' col-md-' + this.labelmd;
30351                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30352                 }
30353
30354                 if(this.labelsm > 0){
30355                     labelCls = ' col-sm-' + this.labelsm;
30356                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30357                 }
30358
30359                 if(this.labelxs > 0){
30360                     labelCls = ' col-xs-' + this.labelxs;
30361                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30362                 }
30363             }
30364             
30365             label.cls += ' ' + labelCls;
30366             
30367             cfg.cn.push(label);
30368         }
30369         
30370         Roo.each(['day', 'month', 'year'], function(t){
30371             cfg.cn.push({
30372                 tag : 'div',
30373                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30374             });
30375         }, this);
30376         
30377         return cfg;
30378     },
30379     
30380     inputEl: function ()
30381     {
30382         return this.el.select('.roo-date-split-field-group-value', true).first();
30383     },
30384     
30385     onRender : function(ct, position) 
30386     {
30387         var _this = this;
30388         
30389         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30390         
30391         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30392         
30393         this.dayField = new Roo.bootstrap.ComboBox({
30394             allowBlank : this.dayAllowBlank,
30395             alwaysQuery : true,
30396             displayField : 'value',
30397             editable : false,
30398             fieldLabel : '',
30399             forceSelection : true,
30400             mode : 'local',
30401             placeholder : this.dayPlaceholder,
30402             selectOnFocus : true,
30403             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30404             triggerAction : 'all',
30405             typeAhead : true,
30406             valueField : 'value',
30407             store : new Roo.data.SimpleStore({
30408                 data : (function() {    
30409                     var days = [];
30410                     _this.fireEvent('days', _this, days);
30411                     return days;
30412                 })(),
30413                 fields : [ 'value' ]
30414             }),
30415             listeners : {
30416                 select : function (_self, record, index)
30417                 {
30418                     _this.setValue(_this.getValue());
30419                 }
30420             }
30421         });
30422
30423         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30424         
30425         this.monthField = new Roo.bootstrap.MonthField({
30426             after : '<i class=\"fa fa-calendar\"></i>',
30427             allowBlank : this.monthAllowBlank,
30428             placeholder : this.monthPlaceholder,
30429             readOnly : true,
30430             listeners : {
30431                 render : function (_self)
30432                 {
30433                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30434                         e.preventDefault();
30435                         _self.focus();
30436                     });
30437                 },
30438                 select : function (_self, oldvalue, newvalue)
30439                 {
30440                     _this.setValue(_this.getValue());
30441                 }
30442             }
30443         });
30444         
30445         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30446         
30447         this.yearField = new Roo.bootstrap.ComboBox({
30448             allowBlank : this.yearAllowBlank,
30449             alwaysQuery : true,
30450             displayField : 'value',
30451             editable : false,
30452             fieldLabel : '',
30453             forceSelection : true,
30454             mode : 'local',
30455             placeholder : this.yearPlaceholder,
30456             selectOnFocus : true,
30457             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30458             triggerAction : 'all',
30459             typeAhead : true,
30460             valueField : 'value',
30461             store : new Roo.data.SimpleStore({
30462                 data : (function() {
30463                     var years = [];
30464                     _this.fireEvent('years', _this, years);
30465                     return years;
30466                 })(),
30467                 fields : [ 'value' ]
30468             }),
30469             listeners : {
30470                 select : function (_self, record, index)
30471                 {
30472                     _this.setValue(_this.getValue());
30473                 }
30474             }
30475         });
30476
30477         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30478     },
30479     
30480     setValue : function(v, format)
30481     {
30482         this.inputEl.dom.value = v;
30483         
30484         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30485         
30486         var d = Date.parseDate(v, f);
30487         
30488         if(!d){
30489             this.validate();
30490             return;
30491         }
30492         
30493         this.setDay(d.format(this.dayFormat));
30494         this.setMonth(d.format(this.monthFormat));
30495         this.setYear(d.format(this.yearFormat));
30496         
30497         this.validate();
30498         
30499         return;
30500     },
30501     
30502     setDay : function(v)
30503     {
30504         this.dayField.setValue(v);
30505         this.inputEl.dom.value = this.getValue();
30506         this.validate();
30507         return;
30508     },
30509     
30510     setMonth : function(v)
30511     {
30512         this.monthField.setValue(v, true);
30513         this.inputEl.dom.value = this.getValue();
30514         this.validate();
30515         return;
30516     },
30517     
30518     setYear : function(v)
30519     {
30520         this.yearField.setValue(v);
30521         this.inputEl.dom.value = this.getValue();
30522         this.validate();
30523         return;
30524     },
30525     
30526     getDay : function()
30527     {
30528         return this.dayField.getValue();
30529     },
30530     
30531     getMonth : function()
30532     {
30533         return this.monthField.getValue();
30534     },
30535     
30536     getYear : function()
30537     {
30538         return this.yearField.getValue();
30539     },
30540     
30541     getValue : function()
30542     {
30543         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30544         
30545         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30546         
30547         return date;
30548     },
30549     
30550     reset : function()
30551     {
30552         this.setDay('');
30553         this.setMonth('');
30554         this.setYear('');
30555         this.inputEl.dom.value = '';
30556         this.validate();
30557         return;
30558     },
30559     
30560     validate : function()
30561     {
30562         var d = this.dayField.validate();
30563         var m = this.monthField.validate();
30564         var y = this.yearField.validate();
30565         
30566         var valid = true;
30567         
30568         if(
30569                 (!this.dayAllowBlank && !d) ||
30570                 (!this.monthAllowBlank && !m) ||
30571                 (!this.yearAllowBlank && !y)
30572         ){
30573             valid = false;
30574         }
30575         
30576         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30577             return valid;
30578         }
30579         
30580         if(valid){
30581             this.markValid();
30582             return valid;
30583         }
30584         
30585         this.markInvalid();
30586         
30587         return valid;
30588     },
30589     
30590     markValid : function()
30591     {
30592         
30593         var label = this.el.select('label', true).first();
30594         var icon = this.el.select('i.fa-star', true).first();
30595
30596         if(label && icon){
30597             icon.remove();
30598         }
30599         
30600         this.fireEvent('valid', this);
30601     },
30602     
30603      /**
30604      * Mark this field as invalid
30605      * @param {String} msg The validation message
30606      */
30607     markInvalid : function(msg)
30608     {
30609         
30610         var label = this.el.select('label', true).first();
30611         var icon = this.el.select('i.fa-star', true).first();
30612
30613         if(label && !icon){
30614             this.el.select('.roo-date-split-field-label', true).createChild({
30615                 tag : 'i',
30616                 cls : 'text-danger fa fa-lg fa-star',
30617                 tooltip : 'This field is required',
30618                 style : 'margin-right:5px;'
30619             }, label, true);
30620         }
30621         
30622         this.fireEvent('invalid', this, msg);
30623     },
30624     
30625     clearInvalid : function()
30626     {
30627         var label = this.el.select('label', true).first();
30628         var icon = this.el.select('i.fa-star', true).first();
30629
30630         if(label && icon){
30631             icon.remove();
30632         }
30633         
30634         this.fireEvent('valid', this);
30635     },
30636     
30637     getName: function()
30638     {
30639         return this.name;
30640     }
30641     
30642 });
30643
30644  /**
30645  *
30646  * This is based on 
30647  * http://masonry.desandro.com
30648  *
30649  * The idea is to render all the bricks based on vertical width...
30650  *
30651  * The original code extends 'outlayer' - we might need to use that....
30652  * 
30653  */
30654
30655
30656 /**
30657  * @class Roo.bootstrap.LayoutMasonry
30658  * @extends Roo.bootstrap.Component
30659  * Bootstrap Layout Masonry class
30660  * 
30661  * @constructor
30662  * Create a new Element
30663  * @param {Object} config The config object
30664  */
30665
30666 Roo.bootstrap.LayoutMasonry = function(config){
30667     
30668     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30669     
30670     this.bricks = [];
30671     
30672     Roo.bootstrap.LayoutMasonry.register(this);
30673     
30674     this.addEvents({
30675         // raw events
30676         /**
30677          * @event layout
30678          * Fire after layout the items
30679          * @param {Roo.bootstrap.LayoutMasonry} this
30680          * @param {Roo.EventObject} e
30681          */
30682         "layout" : true
30683     });
30684     
30685 };
30686
30687 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30688     
30689     /**
30690      * @cfg {Boolean} isLayoutInstant = no animation?
30691      */   
30692     isLayoutInstant : false, // needed?
30693    
30694     /**
30695      * @cfg {Number} boxWidth  width of the columns
30696      */   
30697     boxWidth : 450,
30698     
30699       /**
30700      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30701      */   
30702     boxHeight : 0,
30703     
30704     /**
30705      * @cfg {Number} padWidth padding below box..
30706      */   
30707     padWidth : 10, 
30708     
30709     /**
30710      * @cfg {Number} gutter gutter width..
30711      */   
30712     gutter : 10,
30713     
30714      /**
30715      * @cfg {Number} maxCols maximum number of columns
30716      */   
30717     
30718     maxCols: 0,
30719     
30720     /**
30721      * @cfg {Boolean} isAutoInitial defalut true
30722      */   
30723     isAutoInitial : true, 
30724     
30725     containerWidth: 0,
30726     
30727     /**
30728      * @cfg {Boolean} isHorizontal defalut false
30729      */   
30730     isHorizontal : false, 
30731
30732     currentSize : null,
30733     
30734     tag: 'div',
30735     
30736     cls: '',
30737     
30738     bricks: null, //CompositeElement
30739     
30740     cols : 1,
30741     
30742     _isLayoutInited : false,
30743     
30744 //    isAlternative : false, // only use for vertical layout...
30745     
30746     /**
30747      * @cfg {Number} alternativePadWidth padding below box..
30748      */   
30749     alternativePadWidth : 50,
30750     
30751     selectedBrick : [],
30752     
30753     getAutoCreate : function(){
30754         
30755         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30756         
30757         var cfg = {
30758             tag: this.tag,
30759             cls: 'blog-masonary-wrapper ' + this.cls,
30760             cn : {
30761                 cls : 'mas-boxes masonary'
30762             }
30763         };
30764         
30765         return cfg;
30766     },
30767     
30768     getChildContainer: function( )
30769     {
30770         if (this.boxesEl) {
30771             return this.boxesEl;
30772         }
30773         
30774         this.boxesEl = this.el.select('.mas-boxes').first();
30775         
30776         return this.boxesEl;
30777     },
30778     
30779     
30780     initEvents : function()
30781     {
30782         var _this = this;
30783         
30784         if(this.isAutoInitial){
30785             Roo.log('hook children rendered');
30786             this.on('childrenrendered', function() {
30787                 Roo.log('children rendered');
30788                 _this.initial();
30789             } ,this);
30790         }
30791     },
30792     
30793     initial : function()
30794     {
30795         this.selectedBrick = [];
30796         
30797         this.currentSize = this.el.getBox(true);
30798         
30799         Roo.EventManager.onWindowResize(this.resize, this); 
30800
30801         if(!this.isAutoInitial){
30802             this.layout();
30803             return;
30804         }
30805         
30806         this.layout();
30807         
30808         return;
30809         //this.layout.defer(500,this);
30810         
30811     },
30812     
30813     resize : function()
30814     {
30815         var cs = this.el.getBox(true);
30816         
30817         if (
30818                 this.currentSize.width == cs.width && 
30819                 this.currentSize.x == cs.x && 
30820                 this.currentSize.height == cs.height && 
30821                 this.currentSize.y == cs.y 
30822         ) {
30823             Roo.log("no change in with or X or Y");
30824             return;
30825         }
30826         
30827         this.currentSize = cs;
30828         
30829         this.layout();
30830         
30831     },
30832     
30833     layout : function()
30834     {   
30835         this._resetLayout();
30836         
30837         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30838         
30839         this.layoutItems( isInstant );
30840       
30841         this._isLayoutInited = true;
30842         
30843         this.fireEvent('layout', this);
30844         
30845     },
30846     
30847     _resetLayout : function()
30848     {
30849         if(this.isHorizontal){
30850             this.horizontalMeasureColumns();
30851             return;
30852         }
30853         
30854         this.verticalMeasureColumns();
30855         
30856     },
30857     
30858     verticalMeasureColumns : function()
30859     {
30860         this.getContainerWidth();
30861         
30862 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30863 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30864 //            return;
30865 //        }
30866         
30867         var boxWidth = this.boxWidth + this.padWidth;
30868         
30869         if(this.containerWidth < this.boxWidth){
30870             boxWidth = this.containerWidth
30871         }
30872         
30873         var containerWidth = this.containerWidth;
30874         
30875         var cols = Math.floor(containerWidth / boxWidth);
30876         
30877         this.cols = Math.max( cols, 1 );
30878         
30879         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30880         
30881         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30882         
30883         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30884         
30885         this.colWidth = boxWidth + avail - this.padWidth;
30886         
30887         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30888         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30889     },
30890     
30891     horizontalMeasureColumns : function()
30892     {
30893         this.getContainerWidth();
30894         
30895         var boxWidth = this.boxWidth;
30896         
30897         if(this.containerWidth < boxWidth){
30898             boxWidth = this.containerWidth;
30899         }
30900         
30901         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30902         
30903         this.el.setHeight(boxWidth);
30904         
30905     },
30906     
30907     getContainerWidth : function()
30908     {
30909         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30910     },
30911     
30912     layoutItems : function( isInstant )
30913     {
30914         Roo.log(this.bricks);
30915         
30916         var items = Roo.apply([], this.bricks);
30917         
30918         if(this.isHorizontal){
30919             this._horizontalLayoutItems( items , isInstant );
30920             return;
30921         }
30922         
30923 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30924 //            this._verticalAlternativeLayoutItems( items , isInstant );
30925 //            return;
30926 //        }
30927         
30928         this._verticalLayoutItems( items , isInstant );
30929         
30930     },
30931     
30932     _verticalLayoutItems : function ( items , isInstant)
30933     {
30934         if ( !items || !items.length ) {
30935             return;
30936         }
30937         
30938         var standard = [
30939             ['xs', 'xs', 'xs', 'tall'],
30940             ['xs', 'xs', 'tall'],
30941             ['xs', 'xs', 'sm'],
30942             ['xs', 'xs', 'xs'],
30943             ['xs', 'tall'],
30944             ['xs', 'sm'],
30945             ['xs', 'xs'],
30946             ['xs'],
30947             
30948             ['sm', 'xs', 'xs'],
30949             ['sm', 'xs'],
30950             ['sm'],
30951             
30952             ['tall', 'xs', 'xs', 'xs'],
30953             ['tall', 'xs', 'xs'],
30954             ['tall', 'xs'],
30955             ['tall']
30956             
30957         ];
30958         
30959         var queue = [];
30960         
30961         var boxes = [];
30962         
30963         var box = [];
30964         
30965         Roo.each(items, function(item, k){
30966             
30967             switch (item.size) {
30968                 // these layouts take up a full box,
30969                 case 'md' :
30970                 case 'md-left' :
30971                 case 'md-right' :
30972                 case 'wide' :
30973                     
30974                     if(box.length){
30975                         boxes.push(box);
30976                         box = [];
30977                     }
30978                     
30979                     boxes.push([item]);
30980                     
30981                     break;
30982                     
30983                 case 'xs' :
30984                 case 'sm' :
30985                 case 'tall' :
30986                     
30987                     box.push(item);
30988                     
30989                     break;
30990                 default :
30991                     break;
30992                     
30993             }
30994             
30995         }, this);
30996         
30997         if(box.length){
30998             boxes.push(box);
30999             box = [];
31000         }
31001         
31002         var filterPattern = function(box, length)
31003         {
31004             if(!box.length){
31005                 return;
31006             }
31007             
31008             var match = false;
31009             
31010             var pattern = box.slice(0, length);
31011             
31012             var format = [];
31013             
31014             Roo.each(pattern, function(i){
31015                 format.push(i.size);
31016             }, this);
31017             
31018             Roo.each(standard, function(s){
31019                 
31020                 if(String(s) != String(format)){
31021                     return;
31022                 }
31023                 
31024                 match = true;
31025                 return false;
31026                 
31027             }, this);
31028             
31029             if(!match && length == 1){
31030                 return;
31031             }
31032             
31033             if(!match){
31034                 filterPattern(box, length - 1);
31035                 return;
31036             }
31037                 
31038             queue.push(pattern);
31039
31040             box = box.slice(length, box.length);
31041
31042             filterPattern(box, 4);
31043
31044             return;
31045             
31046         }
31047         
31048         Roo.each(boxes, function(box, k){
31049             
31050             if(!box.length){
31051                 return;
31052             }
31053             
31054             if(box.length == 1){
31055                 queue.push(box);
31056                 return;
31057             }
31058             
31059             filterPattern(box, 4);
31060             
31061         }, this);
31062         
31063         this._processVerticalLayoutQueue( queue, isInstant );
31064         
31065     },
31066     
31067 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31068 //    {
31069 //        if ( !items || !items.length ) {
31070 //            return;
31071 //        }
31072 //
31073 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31074 //        
31075 //    },
31076     
31077     _horizontalLayoutItems : function ( items , isInstant)
31078     {
31079         if ( !items || !items.length || items.length < 3) {
31080             return;
31081         }
31082         
31083         items.reverse();
31084         
31085         var eItems = items.slice(0, 3);
31086         
31087         items = items.slice(3, items.length);
31088         
31089         var standard = [
31090             ['xs', 'xs', 'xs', 'wide'],
31091             ['xs', 'xs', 'wide'],
31092             ['xs', 'xs', 'sm'],
31093             ['xs', 'xs', 'xs'],
31094             ['xs', 'wide'],
31095             ['xs', 'sm'],
31096             ['xs', 'xs'],
31097             ['xs'],
31098             
31099             ['sm', 'xs', 'xs'],
31100             ['sm', 'xs'],
31101             ['sm'],
31102             
31103             ['wide', 'xs', 'xs', 'xs'],
31104             ['wide', 'xs', 'xs'],
31105             ['wide', 'xs'],
31106             ['wide'],
31107             
31108             ['wide-thin']
31109         ];
31110         
31111         var queue = [];
31112         
31113         var boxes = [];
31114         
31115         var box = [];
31116         
31117         Roo.each(items, function(item, k){
31118             
31119             switch (item.size) {
31120                 case 'md' :
31121                 case 'md-left' :
31122                 case 'md-right' :
31123                 case 'tall' :
31124                     
31125                     if(box.length){
31126                         boxes.push(box);
31127                         box = [];
31128                     }
31129                     
31130                     boxes.push([item]);
31131                     
31132                     break;
31133                     
31134                 case 'xs' :
31135                 case 'sm' :
31136                 case 'wide' :
31137                 case 'wide-thin' :
31138                     
31139                     box.push(item);
31140                     
31141                     break;
31142                 default :
31143                     break;
31144                     
31145             }
31146             
31147         }, this);
31148         
31149         if(box.length){
31150             boxes.push(box);
31151             box = [];
31152         }
31153         
31154         var filterPattern = function(box, length)
31155         {
31156             if(!box.length){
31157                 return;
31158             }
31159             
31160             var match = false;
31161             
31162             var pattern = box.slice(0, length);
31163             
31164             var format = [];
31165             
31166             Roo.each(pattern, function(i){
31167                 format.push(i.size);
31168             }, this);
31169             
31170             Roo.each(standard, function(s){
31171                 
31172                 if(String(s) != String(format)){
31173                     return;
31174                 }
31175                 
31176                 match = true;
31177                 return false;
31178                 
31179             }, this);
31180             
31181             if(!match && length == 1){
31182                 return;
31183             }
31184             
31185             if(!match){
31186                 filterPattern(box, length - 1);
31187                 return;
31188             }
31189                 
31190             queue.push(pattern);
31191
31192             box = box.slice(length, box.length);
31193
31194             filterPattern(box, 4);
31195
31196             return;
31197             
31198         }
31199         
31200         Roo.each(boxes, function(box, k){
31201             
31202             if(!box.length){
31203                 return;
31204             }
31205             
31206             if(box.length == 1){
31207                 queue.push(box);
31208                 return;
31209             }
31210             
31211             filterPattern(box, 4);
31212             
31213         }, this);
31214         
31215         
31216         var prune = [];
31217         
31218         var pos = this.el.getBox(true);
31219         
31220         var minX = pos.x;
31221         
31222         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31223         
31224         var hit_end = false;
31225         
31226         Roo.each(queue, function(box){
31227             
31228             if(hit_end){
31229                 
31230                 Roo.each(box, function(b){
31231                 
31232                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31233                     b.el.hide();
31234
31235                 }, this);
31236
31237                 return;
31238             }
31239             
31240             var mx = 0;
31241             
31242             Roo.each(box, function(b){
31243                 
31244                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31245                 b.el.show();
31246
31247                 mx = Math.max(mx, b.x);
31248                 
31249             }, this);
31250             
31251             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31252             
31253             if(maxX < minX){
31254                 
31255                 Roo.each(box, function(b){
31256                 
31257                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31258                     b.el.hide();
31259                     
31260                 }, this);
31261                 
31262                 hit_end = true;
31263                 
31264                 return;
31265             }
31266             
31267             prune.push(box);
31268             
31269         }, this);
31270         
31271         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31272     },
31273     
31274     /** Sets position of item in DOM
31275     * @param {Element} item
31276     * @param {Number} x - horizontal position
31277     * @param {Number} y - vertical position
31278     * @param {Boolean} isInstant - disables transitions
31279     */
31280     _processVerticalLayoutQueue : function( queue, isInstant )
31281     {
31282         var pos = this.el.getBox(true);
31283         var x = pos.x;
31284         var y = pos.y;
31285         var maxY = [];
31286         
31287         for (var i = 0; i < this.cols; i++){
31288             maxY[i] = pos.y;
31289         }
31290         
31291         Roo.each(queue, function(box, k){
31292             
31293             var col = k % this.cols;
31294             
31295             Roo.each(box, function(b,kk){
31296                 
31297                 b.el.position('absolute');
31298                 
31299                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31300                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31301                 
31302                 if(b.size == 'md-left' || b.size == 'md-right'){
31303                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31304                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31305                 }
31306                 
31307                 b.el.setWidth(width);
31308                 b.el.setHeight(height);
31309                 // iframe?
31310                 b.el.select('iframe',true).setSize(width,height);
31311                 
31312             }, this);
31313             
31314             for (var i = 0; i < this.cols; i++){
31315                 
31316                 if(maxY[i] < maxY[col]){
31317                     col = i;
31318                     continue;
31319                 }
31320                 
31321                 col = Math.min(col, i);
31322                 
31323             }
31324             
31325             x = pos.x + col * (this.colWidth + this.padWidth);
31326             
31327             y = maxY[col];
31328             
31329             var positions = [];
31330             
31331             switch (box.length){
31332                 case 1 :
31333                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31334                     break;
31335                 case 2 :
31336                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31337                     break;
31338                 case 3 :
31339                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31340                     break;
31341                 case 4 :
31342                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31343                     break;
31344                 default :
31345                     break;
31346             }
31347             
31348             Roo.each(box, function(b,kk){
31349                 
31350                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31351                 
31352                 var sz = b.el.getSize();
31353                 
31354                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31355                 
31356             }, this);
31357             
31358         }, this);
31359         
31360         var mY = 0;
31361         
31362         for (var i = 0; i < this.cols; i++){
31363             mY = Math.max(mY, maxY[i]);
31364         }
31365         
31366         this.el.setHeight(mY - pos.y);
31367         
31368     },
31369     
31370 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31371 //    {
31372 //        var pos = this.el.getBox(true);
31373 //        var x = pos.x;
31374 //        var y = pos.y;
31375 //        var maxX = pos.right;
31376 //        
31377 //        var maxHeight = 0;
31378 //        
31379 //        Roo.each(items, function(item, k){
31380 //            
31381 //            var c = k % 2;
31382 //            
31383 //            item.el.position('absolute');
31384 //                
31385 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31386 //
31387 //            item.el.setWidth(width);
31388 //
31389 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31390 //
31391 //            item.el.setHeight(height);
31392 //            
31393 //            if(c == 0){
31394 //                item.el.setXY([x, y], isInstant ? false : true);
31395 //            } else {
31396 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31397 //            }
31398 //            
31399 //            y = y + height + this.alternativePadWidth;
31400 //            
31401 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31402 //            
31403 //        }, this);
31404 //        
31405 //        this.el.setHeight(maxHeight);
31406 //        
31407 //    },
31408     
31409     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31410     {
31411         var pos = this.el.getBox(true);
31412         
31413         var minX = pos.x;
31414         var minY = pos.y;
31415         
31416         var maxX = pos.right;
31417         
31418         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31419         
31420         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31421         
31422         Roo.each(queue, function(box, k){
31423             
31424             Roo.each(box, function(b, kk){
31425                 
31426                 b.el.position('absolute');
31427                 
31428                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31429                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31430                 
31431                 if(b.size == 'md-left' || b.size == 'md-right'){
31432                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31433                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31434                 }
31435                 
31436                 b.el.setWidth(width);
31437                 b.el.setHeight(height);
31438                 
31439             }, this);
31440             
31441             if(!box.length){
31442                 return;
31443             }
31444             
31445             var positions = [];
31446             
31447             switch (box.length){
31448                 case 1 :
31449                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31450                     break;
31451                 case 2 :
31452                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31453                     break;
31454                 case 3 :
31455                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31456                     break;
31457                 case 4 :
31458                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31459                     break;
31460                 default :
31461                     break;
31462             }
31463             
31464             Roo.each(box, function(b,kk){
31465                 
31466                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31467                 
31468                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31469                 
31470             }, this);
31471             
31472         }, this);
31473         
31474     },
31475     
31476     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31477     {
31478         Roo.each(eItems, function(b,k){
31479             
31480             b.size = (k == 0) ? 'sm' : 'xs';
31481             b.x = (k == 0) ? 2 : 1;
31482             b.y = (k == 0) ? 2 : 1;
31483             
31484             b.el.position('absolute');
31485             
31486             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31487                 
31488             b.el.setWidth(width);
31489             
31490             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31491             
31492             b.el.setHeight(height);
31493             
31494         }, this);
31495
31496         var positions = [];
31497         
31498         positions.push({
31499             x : maxX - this.unitWidth * 2 - this.gutter,
31500             y : minY
31501         });
31502         
31503         positions.push({
31504             x : maxX - this.unitWidth,
31505             y : minY + (this.unitWidth + this.gutter) * 2
31506         });
31507         
31508         positions.push({
31509             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31510             y : minY
31511         });
31512         
31513         Roo.each(eItems, function(b,k){
31514             
31515             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31516
31517         }, this);
31518         
31519     },
31520     
31521     getVerticalOneBoxColPositions : function(x, y, box)
31522     {
31523         var pos = [];
31524         
31525         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31526         
31527         if(box[0].size == 'md-left'){
31528             rand = 0;
31529         }
31530         
31531         if(box[0].size == 'md-right'){
31532             rand = 1;
31533         }
31534         
31535         pos.push({
31536             x : x + (this.unitWidth + this.gutter) * rand,
31537             y : y
31538         });
31539         
31540         return pos;
31541     },
31542     
31543     getVerticalTwoBoxColPositions : function(x, y, box)
31544     {
31545         var pos = [];
31546         
31547         if(box[0].size == 'xs'){
31548             
31549             pos.push({
31550                 x : x,
31551                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31552             });
31553
31554             pos.push({
31555                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31556                 y : y
31557             });
31558             
31559             return pos;
31560             
31561         }
31562         
31563         pos.push({
31564             x : x,
31565             y : y
31566         });
31567
31568         pos.push({
31569             x : x + (this.unitWidth + this.gutter) * 2,
31570             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31571         });
31572         
31573         return pos;
31574         
31575     },
31576     
31577     getVerticalThreeBoxColPositions : function(x, y, box)
31578     {
31579         var pos = [];
31580         
31581         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31582             
31583             pos.push({
31584                 x : x,
31585                 y : y
31586             });
31587
31588             pos.push({
31589                 x : x + (this.unitWidth + this.gutter) * 1,
31590                 y : y
31591             });
31592             
31593             pos.push({
31594                 x : x + (this.unitWidth + this.gutter) * 2,
31595                 y : y
31596             });
31597             
31598             return pos;
31599             
31600         }
31601         
31602         if(box[0].size == 'xs' && box[1].size == 'xs'){
31603             
31604             pos.push({
31605                 x : x,
31606                 y : y
31607             });
31608
31609             pos.push({
31610                 x : x,
31611                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31612             });
31613             
31614             pos.push({
31615                 x : x + (this.unitWidth + this.gutter) * 1,
31616                 y : y
31617             });
31618             
31619             return pos;
31620             
31621         }
31622         
31623         pos.push({
31624             x : x,
31625             y : y
31626         });
31627
31628         pos.push({
31629             x : x + (this.unitWidth + this.gutter) * 2,
31630             y : y
31631         });
31632
31633         pos.push({
31634             x : x + (this.unitWidth + this.gutter) * 2,
31635             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31636         });
31637             
31638         return pos;
31639         
31640     },
31641     
31642     getVerticalFourBoxColPositions : function(x, y, box)
31643     {
31644         var pos = [];
31645         
31646         if(box[0].size == 'xs'){
31647             
31648             pos.push({
31649                 x : x,
31650                 y : y
31651             });
31652
31653             pos.push({
31654                 x : x,
31655                 y : y + (this.unitHeight + this.gutter) * 1
31656             });
31657             
31658             pos.push({
31659                 x : x,
31660                 y : y + (this.unitHeight + this.gutter) * 2
31661             });
31662             
31663             pos.push({
31664                 x : x + (this.unitWidth + this.gutter) * 1,
31665                 y : y
31666             });
31667             
31668             return pos;
31669             
31670         }
31671         
31672         pos.push({
31673             x : x,
31674             y : y
31675         });
31676
31677         pos.push({
31678             x : x + (this.unitWidth + this.gutter) * 2,
31679             y : y
31680         });
31681
31682         pos.push({
31683             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31684             y : y + (this.unitHeight + this.gutter) * 1
31685         });
31686
31687         pos.push({
31688             x : x + (this.unitWidth + this.gutter) * 2,
31689             y : y + (this.unitWidth + this.gutter) * 2
31690         });
31691
31692         return pos;
31693         
31694     },
31695     
31696     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31697     {
31698         var pos = [];
31699         
31700         if(box[0].size == 'md-left'){
31701             pos.push({
31702                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31703                 y : minY
31704             });
31705             
31706             return pos;
31707         }
31708         
31709         if(box[0].size == 'md-right'){
31710             pos.push({
31711                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31712                 y : minY + (this.unitWidth + this.gutter) * 1
31713             });
31714             
31715             return pos;
31716         }
31717         
31718         var rand = Math.floor(Math.random() * (4 - box[0].y));
31719         
31720         pos.push({
31721             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31722             y : minY + (this.unitWidth + this.gutter) * rand
31723         });
31724         
31725         return pos;
31726         
31727     },
31728     
31729     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31730     {
31731         var pos = [];
31732         
31733         if(box[0].size == 'xs'){
31734             
31735             pos.push({
31736                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31737                 y : minY
31738             });
31739
31740             pos.push({
31741                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31742                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31743             });
31744             
31745             return pos;
31746             
31747         }
31748         
31749         pos.push({
31750             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31751             y : minY
31752         });
31753
31754         pos.push({
31755             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31756             y : minY + (this.unitWidth + this.gutter) * 2
31757         });
31758         
31759         return pos;
31760         
31761     },
31762     
31763     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31764     {
31765         var pos = [];
31766         
31767         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31768             
31769             pos.push({
31770                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31771                 y : minY
31772             });
31773
31774             pos.push({
31775                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31776                 y : minY + (this.unitWidth + this.gutter) * 1
31777             });
31778             
31779             pos.push({
31780                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31781                 y : minY + (this.unitWidth + this.gutter) * 2
31782             });
31783             
31784             return pos;
31785             
31786         }
31787         
31788         if(box[0].size == 'xs' && box[1].size == 'xs'){
31789             
31790             pos.push({
31791                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31792                 y : minY
31793             });
31794
31795             pos.push({
31796                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31797                 y : minY
31798             });
31799             
31800             pos.push({
31801                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31802                 y : minY + (this.unitWidth + this.gutter) * 1
31803             });
31804             
31805             return pos;
31806             
31807         }
31808         
31809         pos.push({
31810             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31811             y : minY
31812         });
31813
31814         pos.push({
31815             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31816             y : minY + (this.unitWidth + this.gutter) * 2
31817         });
31818
31819         pos.push({
31820             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31821             y : minY + (this.unitWidth + this.gutter) * 2
31822         });
31823             
31824         return pos;
31825         
31826     },
31827     
31828     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31829     {
31830         var pos = [];
31831         
31832         if(box[0].size == 'xs'){
31833             
31834             pos.push({
31835                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31836                 y : minY
31837             });
31838
31839             pos.push({
31840                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31841                 y : minY
31842             });
31843             
31844             pos.push({
31845                 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),
31846                 y : minY
31847             });
31848             
31849             pos.push({
31850                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31851                 y : minY + (this.unitWidth + this.gutter) * 1
31852             });
31853             
31854             return pos;
31855             
31856         }
31857         
31858         pos.push({
31859             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31860             y : minY
31861         });
31862         
31863         pos.push({
31864             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31865             y : minY + (this.unitWidth + this.gutter) * 2
31866         });
31867         
31868         pos.push({
31869             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31870             y : minY + (this.unitWidth + this.gutter) * 2
31871         });
31872         
31873         pos.push({
31874             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),
31875             y : minY + (this.unitWidth + this.gutter) * 2
31876         });
31877
31878         return pos;
31879         
31880     },
31881     
31882     /**
31883     * remove a Masonry Brick
31884     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31885     */
31886     removeBrick : function(brick_id)
31887     {
31888         if (!brick_id) {
31889             return;
31890         }
31891         
31892         for (var i = 0; i<this.bricks.length; i++) {
31893             if (this.bricks[i].id == brick_id) {
31894                 this.bricks.splice(i,1);
31895                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31896                 this.initial();
31897             }
31898         }
31899     },
31900     
31901     /**
31902     * adds a Masonry Brick
31903     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31904     */
31905     addBrick : function(cfg)
31906     {
31907         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31908         //this.register(cn);
31909         cn.parentId = this.id;
31910         cn.onRender(this.el, null);
31911         return cn;
31912     },
31913     
31914     /**
31915     * register a Masonry Brick
31916     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31917     */
31918     
31919     register : function(brick)
31920     {
31921         this.bricks.push(brick);
31922         brick.masonryId = this.id;
31923     },
31924     
31925     /**
31926     * clear all the Masonry Brick
31927     */
31928     clearAll : function()
31929     {
31930         this.bricks = [];
31931         //this.getChildContainer().dom.innerHTML = "";
31932         this.el.dom.innerHTML = '';
31933     },
31934     
31935     getSelected : function()
31936     {
31937         if (!this.selectedBrick) {
31938             return false;
31939         }
31940         
31941         return this.selectedBrick;
31942     }
31943 });
31944
31945 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31946     
31947     groups: {},
31948      /**
31949     * register a Masonry Layout
31950     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31951     */
31952     
31953     register : function(layout)
31954     {
31955         this.groups[layout.id] = layout;
31956     },
31957     /**
31958     * fetch a  Masonry Layout based on the masonry layout ID
31959     * @param {string} the masonry layout to add
31960     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31961     */
31962     
31963     get: function(layout_id) {
31964         if (typeof(this.groups[layout_id]) == 'undefined') {
31965             return false;
31966         }
31967         return this.groups[layout_id] ;
31968     }
31969     
31970     
31971     
31972 });
31973
31974  
31975
31976  /**
31977  *
31978  * This is based on 
31979  * http://masonry.desandro.com
31980  *
31981  * The idea is to render all the bricks based on vertical width...
31982  *
31983  * The original code extends 'outlayer' - we might need to use that....
31984  * 
31985  */
31986
31987
31988 /**
31989  * @class Roo.bootstrap.LayoutMasonryAuto
31990  * @extends Roo.bootstrap.Component
31991  * Bootstrap Layout Masonry class
31992  * 
31993  * @constructor
31994  * Create a new Element
31995  * @param {Object} config The config object
31996  */
31997
31998 Roo.bootstrap.LayoutMasonryAuto = function(config){
31999     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32000 };
32001
32002 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32003     
32004       /**
32005      * @cfg {Boolean} isFitWidth  - resize the width..
32006      */   
32007     isFitWidth : false,  // options..
32008     /**
32009      * @cfg {Boolean} isOriginLeft = left align?
32010      */   
32011     isOriginLeft : true,
32012     /**
32013      * @cfg {Boolean} isOriginTop = top align?
32014      */   
32015     isOriginTop : false,
32016     /**
32017      * @cfg {Boolean} isLayoutInstant = no animation?
32018      */   
32019     isLayoutInstant : false, // needed?
32020     /**
32021      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32022      */   
32023     isResizingContainer : true,
32024     /**
32025      * @cfg {Number} columnWidth  width of the columns 
32026      */   
32027     
32028     columnWidth : 0,
32029     
32030     /**
32031      * @cfg {Number} maxCols maximum number of columns
32032      */   
32033     
32034     maxCols: 0,
32035     /**
32036      * @cfg {Number} padHeight padding below box..
32037      */   
32038     
32039     padHeight : 10, 
32040     
32041     /**
32042      * @cfg {Boolean} isAutoInitial defalut true
32043      */   
32044     
32045     isAutoInitial : true, 
32046     
32047     // private?
32048     gutter : 0,
32049     
32050     containerWidth: 0,
32051     initialColumnWidth : 0,
32052     currentSize : null,
32053     
32054     colYs : null, // array.
32055     maxY : 0,
32056     padWidth: 10,
32057     
32058     
32059     tag: 'div',
32060     cls: '',
32061     bricks: null, //CompositeElement
32062     cols : 0, // array?
32063     // element : null, // wrapped now this.el
32064     _isLayoutInited : null, 
32065     
32066     
32067     getAutoCreate : function(){
32068         
32069         var cfg = {
32070             tag: this.tag,
32071             cls: 'blog-masonary-wrapper ' + this.cls,
32072             cn : {
32073                 cls : 'mas-boxes masonary'
32074             }
32075         };
32076         
32077         return cfg;
32078     },
32079     
32080     getChildContainer: function( )
32081     {
32082         if (this.boxesEl) {
32083             return this.boxesEl;
32084         }
32085         
32086         this.boxesEl = this.el.select('.mas-boxes').first();
32087         
32088         return this.boxesEl;
32089     },
32090     
32091     
32092     initEvents : function()
32093     {
32094         var _this = this;
32095         
32096         if(this.isAutoInitial){
32097             Roo.log('hook children rendered');
32098             this.on('childrenrendered', function() {
32099                 Roo.log('children rendered');
32100                 _this.initial();
32101             } ,this);
32102         }
32103         
32104     },
32105     
32106     initial : function()
32107     {
32108         this.reloadItems();
32109
32110         this.currentSize = this.el.getBox(true);
32111
32112         /// was window resize... - let's see if this works..
32113         Roo.EventManager.onWindowResize(this.resize, this); 
32114
32115         if(!this.isAutoInitial){
32116             this.layout();
32117             return;
32118         }
32119         
32120         this.layout.defer(500,this);
32121     },
32122     
32123     reloadItems: function()
32124     {
32125         this.bricks = this.el.select('.masonry-brick', true);
32126         
32127         this.bricks.each(function(b) {
32128             //Roo.log(b.getSize());
32129             if (!b.attr('originalwidth')) {
32130                 b.attr('originalwidth',  b.getSize().width);
32131             }
32132             
32133         });
32134         
32135         Roo.log(this.bricks.elements.length);
32136     },
32137     
32138     resize : function()
32139     {
32140         Roo.log('resize');
32141         var cs = this.el.getBox(true);
32142         
32143         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32144             Roo.log("no change in with or X");
32145             return;
32146         }
32147         this.currentSize = cs;
32148         this.layout();
32149     },
32150     
32151     layout : function()
32152     {
32153          Roo.log('layout');
32154         this._resetLayout();
32155         //this._manageStamps();
32156       
32157         // don't animate first layout
32158         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32159         this.layoutItems( isInstant );
32160       
32161         // flag for initalized
32162         this._isLayoutInited = true;
32163     },
32164     
32165     layoutItems : function( isInstant )
32166     {
32167         //var items = this._getItemsForLayout( this.items );
32168         // original code supports filtering layout items.. we just ignore it..
32169         
32170         this._layoutItems( this.bricks , isInstant );
32171       
32172         this._postLayout();
32173     },
32174     _layoutItems : function ( items , isInstant)
32175     {
32176        //this.fireEvent( 'layout', this, items );
32177     
32178
32179         if ( !items || !items.elements.length ) {
32180           // no items, emit event with empty array
32181             return;
32182         }
32183
32184         var queue = [];
32185         items.each(function(item) {
32186             Roo.log("layout item");
32187             Roo.log(item);
32188             // get x/y object from method
32189             var position = this._getItemLayoutPosition( item );
32190             // enqueue
32191             position.item = item;
32192             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32193             queue.push( position );
32194         }, this);
32195       
32196         this._processLayoutQueue( queue );
32197     },
32198     /** Sets position of item in DOM
32199     * @param {Element} item
32200     * @param {Number} x - horizontal position
32201     * @param {Number} y - vertical position
32202     * @param {Boolean} isInstant - disables transitions
32203     */
32204     _processLayoutQueue : function( queue )
32205     {
32206         for ( var i=0, len = queue.length; i < len; i++ ) {
32207             var obj = queue[i];
32208             obj.item.position('absolute');
32209             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32210         }
32211     },
32212       
32213     
32214     /**
32215     * Any logic you want to do after each layout,
32216     * i.e. size the container
32217     */
32218     _postLayout : function()
32219     {
32220         this.resizeContainer();
32221     },
32222     
32223     resizeContainer : function()
32224     {
32225         if ( !this.isResizingContainer ) {
32226             return;
32227         }
32228         var size = this._getContainerSize();
32229         if ( size ) {
32230             this.el.setSize(size.width,size.height);
32231             this.boxesEl.setSize(size.width,size.height);
32232         }
32233     },
32234     
32235     
32236     
32237     _resetLayout : function()
32238     {
32239         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32240         this.colWidth = this.el.getWidth();
32241         //this.gutter = this.el.getWidth(); 
32242         
32243         this.measureColumns();
32244
32245         // reset column Y
32246         var i = this.cols;
32247         this.colYs = [];
32248         while (i--) {
32249             this.colYs.push( 0 );
32250         }
32251     
32252         this.maxY = 0;
32253     },
32254
32255     measureColumns : function()
32256     {
32257         this.getContainerWidth();
32258       // if columnWidth is 0, default to outerWidth of first item
32259         if ( !this.columnWidth ) {
32260             var firstItem = this.bricks.first();
32261             Roo.log(firstItem);
32262             this.columnWidth  = this.containerWidth;
32263             if (firstItem && firstItem.attr('originalwidth') ) {
32264                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32265             }
32266             // columnWidth fall back to item of first element
32267             Roo.log("set column width?");
32268                         this.initialColumnWidth = this.columnWidth  ;
32269
32270             // if first elem has no width, default to size of container
32271             
32272         }
32273         
32274         
32275         if (this.initialColumnWidth) {
32276             this.columnWidth = this.initialColumnWidth;
32277         }
32278         
32279         
32280             
32281         // column width is fixed at the top - however if container width get's smaller we should
32282         // reduce it...
32283         
32284         // this bit calcs how man columns..
32285             
32286         var columnWidth = this.columnWidth += this.gutter;
32287       
32288         // calculate columns
32289         var containerWidth = this.containerWidth + this.gutter;
32290         
32291         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32292         // fix rounding errors, typically with gutters
32293         var excess = columnWidth - containerWidth % columnWidth;
32294         
32295         
32296         // if overshoot is less than a pixel, round up, otherwise floor it
32297         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32298         cols = Math[ mathMethod ]( cols );
32299         this.cols = Math.max( cols, 1 );
32300         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32301         
32302          // padding positioning..
32303         var totalColWidth = this.cols * this.columnWidth;
32304         var padavail = this.containerWidth - totalColWidth;
32305         // so for 2 columns - we need 3 'pads'
32306         
32307         var padNeeded = (1+this.cols) * this.padWidth;
32308         
32309         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32310         
32311         this.columnWidth += padExtra
32312         //this.padWidth = Math.floor(padavail /  ( this.cols));
32313         
32314         // adjust colum width so that padding is fixed??
32315         
32316         // we have 3 columns ... total = width * 3
32317         // we have X left over... that should be used by 
32318         
32319         //if (this.expandC) {
32320             
32321         //}
32322         
32323         
32324         
32325     },
32326     
32327     getContainerWidth : function()
32328     {
32329        /* // container is parent if fit width
32330         var container = this.isFitWidth ? this.element.parentNode : this.element;
32331         // check that this.size and size are there
32332         // IE8 triggers resize on body size change, so they might not be
32333         
32334         var size = getSize( container );  //FIXME
32335         this.containerWidth = size && size.innerWidth; //FIXME
32336         */
32337          
32338         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32339         
32340     },
32341     
32342     _getItemLayoutPosition : function( item )  // what is item?
32343     {
32344         // we resize the item to our columnWidth..
32345       
32346         item.setWidth(this.columnWidth);
32347         item.autoBoxAdjust  = false;
32348         
32349         var sz = item.getSize();
32350  
32351         // how many columns does this brick span
32352         var remainder = this.containerWidth % this.columnWidth;
32353         
32354         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32355         // round if off by 1 pixel, otherwise use ceil
32356         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32357         colSpan = Math.min( colSpan, this.cols );
32358         
32359         // normally this should be '1' as we dont' currently allow multi width columns..
32360         
32361         var colGroup = this._getColGroup( colSpan );
32362         // get the minimum Y value from the columns
32363         var minimumY = Math.min.apply( Math, colGroup );
32364         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32365         
32366         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32367          
32368         // position the brick
32369         var position = {
32370             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32371             y: this.currentSize.y + minimumY + this.padHeight
32372         };
32373         
32374         Roo.log(position);
32375         // apply setHeight to necessary columns
32376         var setHeight = minimumY + sz.height + this.padHeight;
32377         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32378         
32379         var setSpan = this.cols + 1 - colGroup.length;
32380         for ( var i = 0; i < setSpan; i++ ) {
32381           this.colYs[ shortColIndex + i ] = setHeight ;
32382         }
32383       
32384         return position;
32385     },
32386     
32387     /**
32388      * @param {Number} colSpan - number of columns the element spans
32389      * @returns {Array} colGroup
32390      */
32391     _getColGroup : function( colSpan )
32392     {
32393         if ( colSpan < 2 ) {
32394           // if brick spans only one column, use all the column Ys
32395           return this.colYs;
32396         }
32397       
32398         var colGroup = [];
32399         // how many different places could this brick fit horizontally
32400         var groupCount = this.cols + 1 - colSpan;
32401         // for each group potential horizontal position
32402         for ( var i = 0; i < groupCount; i++ ) {
32403           // make an array of colY values for that one group
32404           var groupColYs = this.colYs.slice( i, i + colSpan );
32405           // and get the max value of the array
32406           colGroup[i] = Math.max.apply( Math, groupColYs );
32407         }
32408         return colGroup;
32409     },
32410     /*
32411     _manageStamp : function( stamp )
32412     {
32413         var stampSize =  stamp.getSize();
32414         var offset = stamp.getBox();
32415         // get the columns that this stamp affects
32416         var firstX = this.isOriginLeft ? offset.x : offset.right;
32417         var lastX = firstX + stampSize.width;
32418         var firstCol = Math.floor( firstX / this.columnWidth );
32419         firstCol = Math.max( 0, firstCol );
32420         
32421         var lastCol = Math.floor( lastX / this.columnWidth );
32422         // lastCol should not go over if multiple of columnWidth #425
32423         lastCol -= lastX % this.columnWidth ? 0 : 1;
32424         lastCol = Math.min( this.cols - 1, lastCol );
32425         
32426         // set colYs to bottom of the stamp
32427         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32428             stampSize.height;
32429             
32430         for ( var i = firstCol; i <= lastCol; i++ ) {
32431           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32432         }
32433     },
32434     */
32435     
32436     _getContainerSize : function()
32437     {
32438         this.maxY = Math.max.apply( Math, this.colYs );
32439         var size = {
32440             height: this.maxY
32441         };
32442       
32443         if ( this.isFitWidth ) {
32444             size.width = this._getContainerFitWidth();
32445         }
32446       
32447         return size;
32448     },
32449     
32450     _getContainerFitWidth : function()
32451     {
32452         var unusedCols = 0;
32453         // count unused columns
32454         var i = this.cols;
32455         while ( --i ) {
32456           if ( this.colYs[i] !== 0 ) {
32457             break;
32458           }
32459           unusedCols++;
32460         }
32461         // fit container to columns that have been used
32462         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32463     },
32464     
32465     needsResizeLayout : function()
32466     {
32467         var previousWidth = this.containerWidth;
32468         this.getContainerWidth();
32469         return previousWidth !== this.containerWidth;
32470     }
32471  
32472 });
32473
32474  
32475
32476  /*
32477  * - LGPL
32478  *
32479  * element
32480  * 
32481  */
32482
32483 /**
32484  * @class Roo.bootstrap.MasonryBrick
32485  * @extends Roo.bootstrap.Component
32486  * Bootstrap MasonryBrick class
32487  * 
32488  * @constructor
32489  * Create a new MasonryBrick
32490  * @param {Object} config The config object
32491  */
32492
32493 Roo.bootstrap.MasonryBrick = function(config){
32494     
32495     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32496     
32497     Roo.bootstrap.MasonryBrick.register(this);
32498     
32499     this.addEvents({
32500         // raw events
32501         /**
32502          * @event click
32503          * When a MasonryBrick is clcik
32504          * @param {Roo.bootstrap.MasonryBrick} this
32505          * @param {Roo.EventObject} e
32506          */
32507         "click" : true
32508     });
32509 };
32510
32511 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32512     
32513     /**
32514      * @cfg {String} title
32515      */   
32516     title : '',
32517     /**
32518      * @cfg {String} html
32519      */   
32520     html : '',
32521     /**
32522      * @cfg {String} bgimage
32523      */   
32524     bgimage : '',
32525     /**
32526      * @cfg {String} videourl
32527      */   
32528     videourl : '',
32529     /**
32530      * @cfg {String} cls
32531      */   
32532     cls : '',
32533     /**
32534      * @cfg {String} href
32535      */   
32536     href : '',
32537     /**
32538      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32539      */   
32540     size : 'xs',
32541     
32542     /**
32543      * @cfg {String} placetitle (center|bottom)
32544      */   
32545     placetitle : '',
32546     
32547     /**
32548      * @cfg {Boolean} isFitContainer defalut true
32549      */   
32550     isFitContainer : true, 
32551     
32552     /**
32553      * @cfg {Boolean} preventDefault defalut false
32554      */   
32555     preventDefault : false, 
32556     
32557     /**
32558      * @cfg {Boolean} inverse defalut false
32559      */   
32560     maskInverse : false, 
32561     
32562     getAutoCreate : function()
32563     {
32564         if(!this.isFitContainer){
32565             return this.getSplitAutoCreate();
32566         }
32567         
32568         var cls = 'masonry-brick masonry-brick-full';
32569         
32570         if(this.href.length){
32571             cls += ' masonry-brick-link';
32572         }
32573         
32574         if(this.bgimage.length){
32575             cls += ' masonry-brick-image';
32576         }
32577         
32578         if(this.maskInverse){
32579             cls += ' mask-inverse';
32580         }
32581         
32582         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32583             cls += ' enable-mask';
32584         }
32585         
32586         if(this.size){
32587             cls += ' masonry-' + this.size + '-brick';
32588         }
32589         
32590         if(this.placetitle.length){
32591             
32592             switch (this.placetitle) {
32593                 case 'center' :
32594                     cls += ' masonry-center-title';
32595                     break;
32596                 case 'bottom' :
32597                     cls += ' masonry-bottom-title';
32598                     break;
32599                 default:
32600                     break;
32601             }
32602             
32603         } else {
32604             if(!this.html.length && !this.bgimage.length){
32605                 cls += ' masonry-center-title';
32606             }
32607
32608             if(!this.html.length && this.bgimage.length){
32609                 cls += ' masonry-bottom-title';
32610             }
32611         }
32612         
32613         if(this.cls){
32614             cls += ' ' + this.cls;
32615         }
32616         
32617         var cfg = {
32618             tag: (this.href.length) ? 'a' : 'div',
32619             cls: cls,
32620             cn: [
32621                 {
32622                     tag: 'div',
32623                     cls: 'masonry-brick-mask'
32624                 },
32625                 {
32626                     tag: 'div',
32627                     cls: 'masonry-brick-paragraph',
32628                     cn: []
32629                 }
32630             ]
32631         };
32632         
32633         if(this.href.length){
32634             cfg.href = this.href;
32635         }
32636         
32637         var cn = cfg.cn[1].cn;
32638         
32639         if(this.title.length){
32640             cn.push({
32641                 tag: 'h4',
32642                 cls: 'masonry-brick-title',
32643                 html: this.title
32644             });
32645         }
32646         
32647         if(this.html.length){
32648             cn.push({
32649                 tag: 'p',
32650                 cls: 'masonry-brick-text',
32651                 html: this.html
32652             });
32653         }
32654         
32655         if (!this.title.length && !this.html.length) {
32656             cfg.cn[1].cls += ' hide';
32657         }
32658         
32659         if(this.bgimage.length){
32660             cfg.cn.push({
32661                 tag: 'img',
32662                 cls: 'masonry-brick-image-view',
32663                 src: this.bgimage
32664             });
32665         }
32666         
32667         if(this.videourl.length){
32668             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32669             // youtube support only?
32670             cfg.cn.push({
32671                 tag: 'iframe',
32672                 cls: 'masonry-brick-image-view',
32673                 src: vurl,
32674                 frameborder : 0,
32675                 allowfullscreen : true
32676             });
32677         }
32678         
32679         return cfg;
32680         
32681     },
32682     
32683     getSplitAutoCreate : function()
32684     {
32685         var cls = 'masonry-brick masonry-brick-split';
32686         
32687         if(this.href.length){
32688             cls += ' masonry-brick-link';
32689         }
32690         
32691         if(this.bgimage.length){
32692             cls += ' masonry-brick-image';
32693         }
32694         
32695         if(this.size){
32696             cls += ' masonry-' + this.size + '-brick';
32697         }
32698         
32699         switch (this.placetitle) {
32700             case 'center' :
32701                 cls += ' masonry-center-title';
32702                 break;
32703             case 'bottom' :
32704                 cls += ' masonry-bottom-title';
32705                 break;
32706             default:
32707                 if(!this.bgimage.length){
32708                     cls += ' masonry-center-title';
32709                 }
32710
32711                 if(this.bgimage.length){
32712                     cls += ' masonry-bottom-title';
32713                 }
32714                 break;
32715         }
32716         
32717         if(this.cls){
32718             cls += ' ' + this.cls;
32719         }
32720         
32721         var cfg = {
32722             tag: (this.href.length) ? 'a' : 'div',
32723             cls: cls,
32724             cn: [
32725                 {
32726                     tag: 'div',
32727                     cls: 'masonry-brick-split-head',
32728                     cn: [
32729                         {
32730                             tag: 'div',
32731                             cls: 'masonry-brick-paragraph',
32732                             cn: []
32733                         }
32734                     ]
32735                 },
32736                 {
32737                     tag: 'div',
32738                     cls: 'masonry-brick-split-body',
32739                     cn: []
32740                 }
32741             ]
32742         };
32743         
32744         if(this.href.length){
32745             cfg.href = this.href;
32746         }
32747         
32748         if(this.title.length){
32749             cfg.cn[0].cn[0].cn.push({
32750                 tag: 'h4',
32751                 cls: 'masonry-brick-title',
32752                 html: this.title
32753             });
32754         }
32755         
32756         if(this.html.length){
32757             cfg.cn[1].cn.push({
32758                 tag: 'p',
32759                 cls: 'masonry-brick-text',
32760                 html: this.html
32761             });
32762         }
32763
32764         if(this.bgimage.length){
32765             cfg.cn[0].cn.push({
32766                 tag: 'img',
32767                 cls: 'masonry-brick-image-view',
32768                 src: this.bgimage
32769             });
32770         }
32771         
32772         if(this.videourl.length){
32773             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32774             // youtube support only?
32775             cfg.cn[0].cn.cn.push({
32776                 tag: 'iframe',
32777                 cls: 'masonry-brick-image-view',
32778                 src: vurl,
32779                 frameborder : 0,
32780                 allowfullscreen : true
32781             });
32782         }
32783         
32784         return cfg;
32785     },
32786     
32787     initEvents: function() 
32788     {
32789         switch (this.size) {
32790             case 'xs' :
32791                 this.x = 1;
32792                 this.y = 1;
32793                 break;
32794             case 'sm' :
32795                 this.x = 2;
32796                 this.y = 2;
32797                 break;
32798             case 'md' :
32799             case 'md-left' :
32800             case 'md-right' :
32801                 this.x = 3;
32802                 this.y = 3;
32803                 break;
32804             case 'tall' :
32805                 this.x = 2;
32806                 this.y = 3;
32807                 break;
32808             case 'wide' :
32809                 this.x = 3;
32810                 this.y = 2;
32811                 break;
32812             case 'wide-thin' :
32813                 this.x = 3;
32814                 this.y = 1;
32815                 break;
32816                         
32817             default :
32818                 break;
32819         }
32820         
32821         if(Roo.isTouch){
32822             this.el.on('touchstart', this.onTouchStart, this);
32823             this.el.on('touchmove', this.onTouchMove, this);
32824             this.el.on('touchend', this.onTouchEnd, this);
32825             this.el.on('contextmenu', this.onContextMenu, this);
32826         } else {
32827             this.el.on('mouseenter'  ,this.enter, this);
32828             this.el.on('mouseleave', this.leave, this);
32829             this.el.on('click', this.onClick, this);
32830         }
32831         
32832         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32833             this.parent().bricks.push(this);   
32834         }
32835         
32836     },
32837     
32838     onClick: function(e, el)
32839     {
32840         var time = this.endTimer - this.startTimer;
32841         // Roo.log(e.preventDefault());
32842         if(Roo.isTouch){
32843             if(time > 1000){
32844                 e.preventDefault();
32845                 return;
32846             }
32847         }
32848         
32849         if(!this.preventDefault){
32850             return;
32851         }
32852         
32853         e.preventDefault();
32854         
32855         if (this.activeClass != '') {
32856             this.selectBrick();
32857         }
32858         
32859         this.fireEvent('click', this, e);
32860     },
32861     
32862     enter: function(e, el)
32863     {
32864         e.preventDefault();
32865         
32866         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32867             return;
32868         }
32869         
32870         if(this.bgimage.length && this.html.length){
32871             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32872         }
32873     },
32874     
32875     leave: function(e, el)
32876     {
32877         e.preventDefault();
32878         
32879         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32880             return;
32881         }
32882         
32883         if(this.bgimage.length && this.html.length){
32884             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32885         }
32886     },
32887     
32888     onTouchStart: function(e, el)
32889     {
32890 //        e.preventDefault();
32891         
32892         this.touchmoved = false;
32893         
32894         if(!this.isFitContainer){
32895             return;
32896         }
32897         
32898         if(!this.bgimage.length || !this.html.length){
32899             return;
32900         }
32901         
32902         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32903         
32904         this.timer = new Date().getTime();
32905         
32906     },
32907     
32908     onTouchMove: function(e, el)
32909     {
32910         this.touchmoved = true;
32911     },
32912     
32913     onContextMenu : function(e,el)
32914     {
32915         e.preventDefault();
32916         e.stopPropagation();
32917         return false;
32918     },
32919     
32920     onTouchEnd: function(e, el)
32921     {
32922 //        e.preventDefault();
32923         
32924         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32925         
32926             this.leave(e,el);
32927             
32928             return;
32929         }
32930         
32931         if(!this.bgimage.length || !this.html.length){
32932             
32933             if(this.href.length){
32934                 window.location.href = this.href;
32935             }
32936             
32937             return;
32938         }
32939         
32940         if(!this.isFitContainer){
32941             return;
32942         }
32943         
32944         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32945         
32946         window.location.href = this.href;
32947     },
32948     
32949     //selection on single brick only
32950     selectBrick : function() {
32951         
32952         if (!this.parentId) {
32953             return;
32954         }
32955         
32956         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32957         var index = m.selectedBrick.indexOf(this.id);
32958         
32959         if ( index > -1) {
32960             m.selectedBrick.splice(index,1);
32961             this.el.removeClass(this.activeClass);
32962             return;
32963         }
32964         
32965         for(var i = 0; i < m.selectedBrick.length; i++) {
32966             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32967             b.el.removeClass(b.activeClass);
32968         }
32969         
32970         m.selectedBrick = [];
32971         
32972         m.selectedBrick.push(this.id);
32973         this.el.addClass(this.activeClass);
32974         return;
32975     },
32976     
32977     isSelected : function(){
32978         return this.el.hasClass(this.activeClass);
32979         
32980     }
32981 });
32982
32983 Roo.apply(Roo.bootstrap.MasonryBrick, {
32984     
32985     //groups: {},
32986     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32987      /**
32988     * register a Masonry Brick
32989     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32990     */
32991     
32992     register : function(brick)
32993     {
32994         //this.groups[brick.id] = brick;
32995         this.groups.add(brick.id, brick);
32996     },
32997     /**
32998     * fetch a  masonry brick based on the masonry brick ID
32999     * @param {string} the masonry brick to add
33000     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33001     */
33002     
33003     get: function(brick_id) 
33004     {
33005         // if (typeof(this.groups[brick_id]) == 'undefined') {
33006         //     return false;
33007         // }
33008         // return this.groups[brick_id] ;
33009         
33010         if(this.groups.key(brick_id)) {
33011             return this.groups.key(brick_id);
33012         }
33013         
33014         return false;
33015     }
33016     
33017     
33018     
33019 });
33020
33021  /*
33022  * - LGPL
33023  *
33024  * element
33025  * 
33026  */
33027
33028 /**
33029  * @class Roo.bootstrap.Brick
33030  * @extends Roo.bootstrap.Component
33031  * Bootstrap Brick class
33032  * 
33033  * @constructor
33034  * Create a new Brick
33035  * @param {Object} config The config object
33036  */
33037
33038 Roo.bootstrap.Brick = function(config){
33039     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33040     
33041     this.addEvents({
33042         // raw events
33043         /**
33044          * @event click
33045          * When a Brick is click
33046          * @param {Roo.bootstrap.Brick} this
33047          * @param {Roo.EventObject} e
33048          */
33049         "click" : true
33050     });
33051 };
33052
33053 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33054     
33055     /**
33056      * @cfg {String} title
33057      */   
33058     title : '',
33059     /**
33060      * @cfg {String} html
33061      */   
33062     html : '',
33063     /**
33064      * @cfg {String} bgimage
33065      */   
33066     bgimage : '',
33067     /**
33068      * @cfg {String} cls
33069      */   
33070     cls : '',
33071     /**
33072      * @cfg {String} href
33073      */   
33074     href : '',
33075     /**
33076      * @cfg {String} video
33077      */   
33078     video : '',
33079     /**
33080      * @cfg {Boolean} square
33081      */   
33082     square : true,
33083     
33084     getAutoCreate : function()
33085     {
33086         var cls = 'roo-brick';
33087         
33088         if(this.href.length){
33089             cls += ' roo-brick-link';
33090         }
33091         
33092         if(this.bgimage.length){
33093             cls += ' roo-brick-image';
33094         }
33095         
33096         if(!this.html.length && !this.bgimage.length){
33097             cls += ' roo-brick-center-title';
33098         }
33099         
33100         if(!this.html.length && this.bgimage.length){
33101             cls += ' roo-brick-bottom-title';
33102         }
33103         
33104         if(this.cls){
33105             cls += ' ' + this.cls;
33106         }
33107         
33108         var cfg = {
33109             tag: (this.href.length) ? 'a' : 'div',
33110             cls: cls,
33111             cn: [
33112                 {
33113                     tag: 'div',
33114                     cls: 'roo-brick-paragraph',
33115                     cn: []
33116                 }
33117             ]
33118         };
33119         
33120         if(this.href.length){
33121             cfg.href = this.href;
33122         }
33123         
33124         var cn = cfg.cn[0].cn;
33125         
33126         if(this.title.length){
33127             cn.push({
33128                 tag: 'h4',
33129                 cls: 'roo-brick-title',
33130                 html: this.title
33131             });
33132         }
33133         
33134         if(this.html.length){
33135             cn.push({
33136                 tag: 'p',
33137                 cls: 'roo-brick-text',
33138                 html: this.html
33139             });
33140         } else {
33141             cn.cls += ' hide';
33142         }
33143         
33144         if(this.bgimage.length){
33145             cfg.cn.push({
33146                 tag: 'img',
33147                 cls: 'roo-brick-image-view',
33148                 src: this.bgimage
33149             });
33150         }
33151         
33152         return cfg;
33153     },
33154     
33155     initEvents: function() 
33156     {
33157         if(this.title.length || this.html.length){
33158             this.el.on('mouseenter'  ,this.enter, this);
33159             this.el.on('mouseleave', this.leave, this);
33160         }
33161         
33162         Roo.EventManager.onWindowResize(this.resize, this); 
33163         
33164         if(this.bgimage.length){
33165             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33166             this.imageEl.on('load', this.onImageLoad, this);
33167             return;
33168         }
33169         
33170         this.resize();
33171     },
33172     
33173     onImageLoad : function()
33174     {
33175         this.resize();
33176     },
33177     
33178     resize : function()
33179     {
33180         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33181         
33182         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33183         
33184         if(this.bgimage.length){
33185             var image = this.el.select('.roo-brick-image-view', true).first();
33186             
33187             image.setWidth(paragraph.getWidth());
33188             
33189             if(this.square){
33190                 image.setHeight(paragraph.getWidth());
33191             }
33192             
33193             this.el.setHeight(image.getHeight());
33194             paragraph.setHeight(image.getHeight());
33195             
33196         }
33197         
33198     },
33199     
33200     enter: function(e, el)
33201     {
33202         e.preventDefault();
33203         
33204         if(this.bgimage.length){
33205             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33206             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33207         }
33208     },
33209     
33210     leave: function(e, el)
33211     {
33212         e.preventDefault();
33213         
33214         if(this.bgimage.length){
33215             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33216             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33217         }
33218     }
33219     
33220 });
33221
33222  
33223
33224  /*
33225  * - LGPL
33226  *
33227  * Number field 
33228  */
33229
33230 /**
33231  * @class Roo.bootstrap.NumberField
33232  * @extends Roo.bootstrap.Input
33233  * Bootstrap NumberField class
33234  * 
33235  * 
33236  * 
33237  * 
33238  * @constructor
33239  * Create a new NumberField
33240  * @param {Object} config The config object
33241  */
33242
33243 Roo.bootstrap.NumberField = function(config){
33244     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33245 };
33246
33247 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33248     
33249     /**
33250      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33251      */
33252     allowDecimals : true,
33253     /**
33254      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33255      */
33256     decimalSeparator : ".",
33257     /**
33258      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33259      */
33260     decimalPrecision : 2,
33261     /**
33262      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33263      */
33264     allowNegative : true,
33265     
33266     /**
33267      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33268      */
33269     allowZero: true,
33270     /**
33271      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33272      */
33273     minValue : Number.NEGATIVE_INFINITY,
33274     /**
33275      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33276      */
33277     maxValue : Number.MAX_VALUE,
33278     /**
33279      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33280      */
33281     minText : "The minimum value for this field is {0}",
33282     /**
33283      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33284      */
33285     maxText : "The maximum value for this field is {0}",
33286     /**
33287      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33288      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33289      */
33290     nanText : "{0} is not a valid number",
33291     /**
33292      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33293      */
33294     thousandsDelimiter : false,
33295     /**
33296      * @cfg {String} valueAlign alignment of value
33297      */
33298     valueAlign : "left",
33299
33300     getAutoCreate : function()
33301     {
33302         var hiddenInput = {
33303             tag: 'input',
33304             type: 'hidden',
33305             id: Roo.id(),
33306             cls: 'hidden-number-input'
33307         };
33308         
33309         if (this.name) {
33310             hiddenInput.name = this.name;
33311         }
33312         
33313         this.name = '';
33314         
33315         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33316         
33317         this.name = hiddenInput.name;
33318         
33319         if(cfg.cn.length > 0) {
33320             cfg.cn.push(hiddenInput);
33321         }
33322         
33323         return cfg;
33324     },
33325
33326     // private
33327     initEvents : function()
33328     {   
33329         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33330         
33331         var allowed = "0123456789";
33332         
33333         if(this.allowDecimals){
33334             allowed += this.decimalSeparator;
33335         }
33336         
33337         if(this.allowNegative){
33338             allowed += "-";
33339         }
33340         
33341         if(this.thousandsDelimiter) {
33342             allowed += ",";
33343         }
33344         
33345         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33346         
33347         var keyPress = function(e){
33348             
33349             var k = e.getKey();
33350             
33351             var c = e.getCharCode();
33352             
33353             if(
33354                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33355                     allowed.indexOf(String.fromCharCode(c)) === -1
33356             ){
33357                 e.stopEvent();
33358                 return;
33359             }
33360             
33361             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33362                 return;
33363             }
33364             
33365             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33366                 e.stopEvent();
33367             }
33368         };
33369         
33370         this.el.on("keypress", keyPress, this);
33371     },
33372     
33373     validateValue : function(value)
33374     {
33375         
33376         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33377             return false;
33378         }
33379         
33380         var num = this.parseValue(value);
33381         
33382         if(isNaN(num)){
33383             this.markInvalid(String.format(this.nanText, value));
33384             return false;
33385         }
33386         
33387         if(num < this.minValue){
33388             this.markInvalid(String.format(this.minText, this.minValue));
33389             return false;
33390         }
33391         
33392         if(num > this.maxValue){
33393             this.markInvalid(String.format(this.maxText, this.maxValue));
33394             return false;
33395         }
33396         
33397         return true;
33398     },
33399
33400     getValue : function()
33401     {
33402         var v = this.hiddenEl().getValue();
33403         
33404         return this.fixPrecision(this.parseValue(v));
33405     },
33406
33407     parseValue : function(value)
33408     {
33409         if(this.thousandsDelimiter) {
33410             value += "";
33411             r = new RegExp(",", "g");
33412             value = value.replace(r, "");
33413         }
33414         
33415         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33416         return isNaN(value) ? '' : value;
33417     },
33418
33419     fixPrecision : function(value)
33420     {
33421         if(this.thousandsDelimiter) {
33422             value += "";
33423             r = new RegExp(",", "g");
33424             value = value.replace(r, "");
33425         }
33426         
33427         var nan = isNaN(value);
33428         
33429         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33430             return nan ? '' : value;
33431         }
33432         return parseFloat(value).toFixed(this.decimalPrecision);
33433     },
33434
33435     setValue : function(v)
33436     {
33437         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33438         
33439         this.value = v;
33440         
33441         if(this.rendered){
33442             
33443             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33444             
33445             this.inputEl().dom.value = (v == '') ? '' :
33446                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33447             
33448             if(!this.allowZero && v === '0') {
33449                 this.hiddenEl().dom.value = '';
33450                 this.inputEl().dom.value = '';
33451             }
33452             
33453             this.validate();
33454         }
33455     },
33456
33457     decimalPrecisionFcn : function(v)
33458     {
33459         return Math.floor(v);
33460     },
33461
33462     beforeBlur : function()
33463     {
33464         var v = this.parseValue(this.getRawValue());
33465         
33466         if(v || v === 0 || v === ''){
33467             this.setValue(v);
33468         }
33469     },
33470     
33471     hiddenEl : function()
33472     {
33473         return this.el.select('input.hidden-number-input',true).first();
33474     }
33475     
33476 });
33477
33478  
33479
33480 /*
33481 * Licence: LGPL
33482 */
33483
33484 /**
33485  * @class Roo.bootstrap.DocumentSlider
33486  * @extends Roo.bootstrap.Component
33487  * Bootstrap DocumentSlider class
33488  * 
33489  * @constructor
33490  * Create a new DocumentViewer
33491  * @param {Object} config The config object
33492  */
33493
33494 Roo.bootstrap.DocumentSlider = function(config){
33495     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33496     
33497     this.files = [];
33498     
33499     this.addEvents({
33500         /**
33501          * @event initial
33502          * Fire after initEvent
33503          * @param {Roo.bootstrap.DocumentSlider} this
33504          */
33505         "initial" : true,
33506         /**
33507          * @event update
33508          * Fire after update
33509          * @param {Roo.bootstrap.DocumentSlider} this
33510          */
33511         "update" : true,
33512         /**
33513          * @event click
33514          * Fire after click
33515          * @param {Roo.bootstrap.DocumentSlider} this
33516          */
33517         "click" : true
33518     });
33519 };
33520
33521 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33522     
33523     files : false,
33524     
33525     indicator : 0,
33526     
33527     getAutoCreate : function()
33528     {
33529         var cfg = {
33530             tag : 'div',
33531             cls : 'roo-document-slider',
33532             cn : [
33533                 {
33534                     tag : 'div',
33535                     cls : 'roo-document-slider-header',
33536                     cn : [
33537                         {
33538                             tag : 'div',
33539                             cls : 'roo-document-slider-header-title'
33540                         }
33541                     ]
33542                 },
33543                 {
33544                     tag : 'div',
33545                     cls : 'roo-document-slider-body',
33546                     cn : [
33547                         {
33548                             tag : 'div',
33549                             cls : 'roo-document-slider-prev',
33550                             cn : [
33551                                 {
33552                                     tag : 'i',
33553                                     cls : 'fa fa-chevron-left'
33554                                 }
33555                             ]
33556                         },
33557                         {
33558                             tag : 'div',
33559                             cls : 'roo-document-slider-thumb',
33560                             cn : [
33561                                 {
33562                                     tag : 'img',
33563                                     cls : 'roo-document-slider-image'
33564                                 }
33565                             ]
33566                         },
33567                         {
33568                             tag : 'div',
33569                             cls : 'roo-document-slider-next',
33570                             cn : [
33571                                 {
33572                                     tag : 'i',
33573                                     cls : 'fa fa-chevron-right'
33574                                 }
33575                             ]
33576                         }
33577                     ]
33578                 }
33579             ]
33580         };
33581         
33582         return cfg;
33583     },
33584     
33585     initEvents : function()
33586     {
33587         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33588         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33589         
33590         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33591         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33592         
33593         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33594         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33595         
33596         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33597         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33598         
33599         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33600         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33601         
33602         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33603         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33604         
33605         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33606         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33607         
33608         this.thumbEl.on('click', this.onClick, this);
33609         
33610         this.prevIndicator.on('click', this.prev, this);
33611         
33612         this.nextIndicator.on('click', this.next, this);
33613         
33614     },
33615     
33616     initial : function()
33617     {
33618         if(this.files.length){
33619             this.indicator = 1;
33620             this.update()
33621         }
33622         
33623         this.fireEvent('initial', this);
33624     },
33625     
33626     update : function()
33627     {
33628         this.imageEl.attr('src', this.files[this.indicator - 1]);
33629         
33630         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33631         
33632         this.prevIndicator.show();
33633         
33634         if(this.indicator == 1){
33635             this.prevIndicator.hide();
33636         }
33637         
33638         this.nextIndicator.show();
33639         
33640         if(this.indicator == this.files.length){
33641             this.nextIndicator.hide();
33642         }
33643         
33644         this.thumbEl.scrollTo('top');
33645         
33646         this.fireEvent('update', this);
33647     },
33648     
33649     onClick : function(e)
33650     {
33651         e.preventDefault();
33652         
33653         this.fireEvent('click', this);
33654     },
33655     
33656     prev : function(e)
33657     {
33658         e.preventDefault();
33659         
33660         this.indicator = Math.max(1, this.indicator - 1);
33661         
33662         this.update();
33663     },
33664     
33665     next : function(e)
33666     {
33667         e.preventDefault();
33668         
33669         this.indicator = Math.min(this.files.length, this.indicator + 1);
33670         
33671         this.update();
33672     }
33673 });
33674 /*
33675  * - LGPL
33676  *
33677  * RadioSet
33678  *
33679  *
33680  */
33681
33682 /**
33683  * @class Roo.bootstrap.RadioSet
33684  * @extends Roo.bootstrap.Input
33685  * Bootstrap RadioSet class
33686  * @cfg {String} indicatorpos (left|right) default left
33687  * @cfg {Boolean} inline (true|false) inline the element (default true)
33688  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33689  * @constructor
33690  * Create a new RadioSet
33691  * @param {Object} config The config object
33692  */
33693
33694 Roo.bootstrap.RadioSet = function(config){
33695     
33696     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33697     
33698     this.radioes = [];
33699     
33700     Roo.bootstrap.RadioSet.register(this);
33701     
33702     this.addEvents({
33703         /**
33704         * @event check
33705         * Fires when the element is checked or unchecked.
33706         * @param {Roo.bootstrap.RadioSet} this This radio
33707         * @param {Roo.bootstrap.Radio} item The checked item
33708         */
33709        check : true,
33710        /**
33711         * @event click
33712         * Fires when the element is click.
33713         * @param {Roo.bootstrap.RadioSet} this This radio set
33714         * @param {Roo.bootstrap.Radio} item The checked item
33715         * @param {Roo.EventObject} e The event object
33716         */
33717        click : true
33718     });
33719     
33720 };
33721
33722 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33723
33724     radioes : false,
33725     
33726     inline : true,
33727     
33728     weight : '',
33729     
33730     indicatorpos : 'left',
33731     
33732     getAutoCreate : function()
33733     {
33734         var label = {
33735             tag : 'label',
33736             cls : 'roo-radio-set-label',
33737             cn : [
33738                 {
33739                     tag : 'span',
33740                     html : this.fieldLabel
33741                 }
33742             ]
33743         };
33744         
33745         if(this.indicatorpos == 'left'){
33746             label.cn.unshift({
33747                 tag : 'i',
33748                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33749                 tooltip : 'This field is required'
33750             });
33751         } else {
33752             label.cn.push({
33753                 tag : 'i',
33754                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33755                 tooltip : 'This field is required'
33756             });
33757         }
33758         
33759         var items = {
33760             tag : 'div',
33761             cls : 'roo-radio-set-items'
33762         };
33763         
33764         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33765         
33766         if (align === 'left' && this.fieldLabel.length) {
33767             
33768             items = {
33769                 cls : "roo-radio-set-right", 
33770                 cn: [
33771                     items
33772                 ]
33773             };
33774             
33775             if(this.labelWidth > 12){
33776                 label.style = "width: " + this.labelWidth + 'px';
33777             }
33778             
33779             if(this.labelWidth < 13 && this.labelmd == 0){
33780                 this.labelmd = this.labelWidth;
33781             }
33782             
33783             if(this.labellg > 0){
33784                 label.cls += ' col-lg-' + this.labellg;
33785                 items.cls += ' col-lg-' + (12 - this.labellg);
33786             }
33787             
33788             if(this.labelmd > 0){
33789                 label.cls += ' col-md-' + this.labelmd;
33790                 items.cls += ' col-md-' + (12 - this.labelmd);
33791             }
33792             
33793             if(this.labelsm > 0){
33794                 label.cls += ' col-sm-' + this.labelsm;
33795                 items.cls += ' col-sm-' + (12 - this.labelsm);
33796             }
33797             
33798             if(this.labelxs > 0){
33799                 label.cls += ' col-xs-' + this.labelxs;
33800                 items.cls += ' col-xs-' + (12 - this.labelxs);
33801             }
33802         }
33803         
33804         var cfg = {
33805             tag : 'div',
33806             cls : 'roo-radio-set',
33807             cn : [
33808                 {
33809                     tag : 'input',
33810                     cls : 'roo-radio-set-input',
33811                     type : 'hidden',
33812                     name : this.name,
33813                     value : this.value ? this.value :  ''
33814                 },
33815                 label,
33816                 items
33817             ]
33818         };
33819         
33820         if(this.weight.length){
33821             cfg.cls += ' roo-radio-' + this.weight;
33822         }
33823         
33824         if(this.inline) {
33825             cfg.cls += ' roo-radio-set-inline';
33826         }
33827         
33828         var settings=this;
33829         ['xs','sm','md','lg'].map(function(size){
33830             if (settings[size]) {
33831                 cfg.cls += ' col-' + size + '-' + settings[size];
33832             }
33833         });
33834         
33835         return cfg;
33836         
33837     },
33838
33839     initEvents : function()
33840     {
33841         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33842         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33843         
33844         if(!this.fieldLabel.length){
33845             this.labelEl.hide();
33846         }
33847         
33848         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33849         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33850         
33851         this.indicator = this.indicatorEl();
33852         
33853         if(this.indicator){
33854             this.indicator.addClass('invisible');
33855         }
33856         
33857         this.originalValue = this.getValue();
33858         
33859     },
33860     
33861     inputEl: function ()
33862     {
33863         return this.el.select('.roo-radio-set-input', true).first();
33864     },
33865     
33866     getChildContainer : function()
33867     {
33868         return this.itemsEl;
33869     },
33870     
33871     register : function(item)
33872     {
33873         this.radioes.push(item);
33874         
33875     },
33876     
33877     validate : function()
33878     {   
33879         if(this.getVisibilityEl().hasClass('hidden')){
33880             return true;
33881         }
33882         
33883         var valid = false;
33884         
33885         Roo.each(this.radioes, function(i){
33886             if(!i.checked){
33887                 return;
33888             }
33889             
33890             valid = true;
33891             return false;
33892         });
33893         
33894         if(this.allowBlank) {
33895             return true;
33896         }
33897         
33898         if(this.disabled || valid){
33899             this.markValid();
33900             return true;
33901         }
33902         
33903         this.markInvalid();
33904         return false;
33905         
33906     },
33907     
33908     markValid : function()
33909     {
33910         if(this.labelEl.isVisible(true)){
33911             this.indicatorEl().removeClass('visible');
33912             this.indicatorEl().addClass('invisible');
33913         }
33914         
33915         this.el.removeClass([this.invalidClass, this.validClass]);
33916         this.el.addClass(this.validClass);
33917         
33918         this.fireEvent('valid', this);
33919     },
33920     
33921     markInvalid : function(msg)
33922     {
33923         if(this.allowBlank || this.disabled){
33924             return;
33925         }
33926         
33927         if(this.labelEl.isVisible(true)){
33928             this.indicatorEl().removeClass('invisible');
33929             this.indicatorEl().addClass('visible');
33930         }
33931         
33932         this.el.removeClass([this.invalidClass, this.validClass]);
33933         this.el.addClass(this.invalidClass);
33934         
33935         this.fireEvent('invalid', this, msg);
33936         
33937     },
33938     
33939     setValue : function(v, suppressEvent)
33940     {   
33941         if(this.value === v){
33942             return;
33943         }
33944         
33945         this.value = v;
33946         
33947         if(this.rendered){
33948             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33949         }
33950         
33951         Roo.each(this.radioes, function(i){
33952             i.checked = false;
33953             i.el.removeClass('checked');
33954         });
33955         
33956         Roo.each(this.radioes, function(i){
33957             
33958             if(i.value === v || i.value.toString() === v.toString()){
33959                 i.checked = true;
33960                 i.el.addClass('checked');
33961                 
33962                 if(suppressEvent !== true){
33963                     this.fireEvent('check', this, i);
33964                 }
33965                 
33966                 return false;
33967             }
33968             
33969         }, this);
33970         
33971         this.validate();
33972     },
33973     
33974     clearInvalid : function(){
33975         
33976         if(!this.el || this.preventMark){
33977             return;
33978         }
33979         
33980         this.el.removeClass([this.invalidClass]);
33981         
33982         this.fireEvent('valid', this);
33983     }
33984     
33985 });
33986
33987 Roo.apply(Roo.bootstrap.RadioSet, {
33988     
33989     groups: {},
33990     
33991     register : function(set)
33992     {
33993         this.groups[set.name] = set;
33994     },
33995     
33996     get: function(name) 
33997     {
33998         if (typeof(this.groups[name]) == 'undefined') {
33999             return false;
34000         }
34001         
34002         return this.groups[name] ;
34003     }
34004     
34005 });
34006 /*
34007  * Based on:
34008  * Ext JS Library 1.1.1
34009  * Copyright(c) 2006-2007, Ext JS, LLC.
34010  *
34011  * Originally Released Under LGPL - original licence link has changed is not relivant.
34012  *
34013  * Fork - LGPL
34014  * <script type="text/javascript">
34015  */
34016
34017
34018 /**
34019  * @class Roo.bootstrap.SplitBar
34020  * @extends Roo.util.Observable
34021  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34022  * <br><br>
34023  * Usage:
34024  * <pre><code>
34025 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34026                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34027 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34028 split.minSize = 100;
34029 split.maxSize = 600;
34030 split.animate = true;
34031 split.on('moved', splitterMoved);
34032 </code></pre>
34033  * @constructor
34034  * Create a new SplitBar
34035  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34036  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34037  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34038  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34039                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34040                         position of the SplitBar).
34041  */
34042 Roo.bootstrap.SplitBar = function(cfg){
34043     
34044     /** @private */
34045     
34046     //{
34047     //  dragElement : elm
34048     //  resizingElement: el,
34049         // optional..
34050     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34051     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34052         // existingProxy ???
34053     //}
34054     
34055     this.el = Roo.get(cfg.dragElement, true);
34056     this.el.dom.unselectable = "on";
34057     /** @private */
34058     this.resizingEl = Roo.get(cfg.resizingElement, true);
34059
34060     /**
34061      * @private
34062      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34063      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34064      * @type Number
34065      */
34066     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34067     
34068     /**
34069      * The minimum size of the resizing element. (Defaults to 0)
34070      * @type Number
34071      */
34072     this.minSize = 0;
34073     
34074     /**
34075      * The maximum size of the resizing element. (Defaults to 2000)
34076      * @type Number
34077      */
34078     this.maxSize = 2000;
34079     
34080     /**
34081      * Whether to animate the transition to the new size
34082      * @type Boolean
34083      */
34084     this.animate = false;
34085     
34086     /**
34087      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34088      * @type Boolean
34089      */
34090     this.useShim = false;
34091     
34092     /** @private */
34093     this.shim = null;
34094     
34095     if(!cfg.existingProxy){
34096         /** @private */
34097         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34098     }else{
34099         this.proxy = Roo.get(cfg.existingProxy).dom;
34100     }
34101     /** @private */
34102     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34103     
34104     /** @private */
34105     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34106     
34107     /** @private */
34108     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34109     
34110     /** @private */
34111     this.dragSpecs = {};
34112     
34113     /**
34114      * @private The adapter to use to positon and resize elements
34115      */
34116     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34117     this.adapter.init(this);
34118     
34119     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34120         /** @private */
34121         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34122         this.el.addClass("roo-splitbar-h");
34123     }else{
34124         /** @private */
34125         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34126         this.el.addClass("roo-splitbar-v");
34127     }
34128     
34129     this.addEvents({
34130         /**
34131          * @event resize
34132          * Fires when the splitter is moved (alias for {@link #event-moved})
34133          * @param {Roo.bootstrap.SplitBar} this
34134          * @param {Number} newSize the new width or height
34135          */
34136         "resize" : true,
34137         /**
34138          * @event moved
34139          * Fires when the splitter is moved
34140          * @param {Roo.bootstrap.SplitBar} this
34141          * @param {Number} newSize the new width or height
34142          */
34143         "moved" : true,
34144         /**
34145          * @event beforeresize
34146          * Fires before the splitter is dragged
34147          * @param {Roo.bootstrap.SplitBar} this
34148          */
34149         "beforeresize" : true,
34150
34151         "beforeapply" : true
34152     });
34153
34154     Roo.util.Observable.call(this);
34155 };
34156
34157 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34158     onStartProxyDrag : function(x, y){
34159         this.fireEvent("beforeresize", this);
34160         if(!this.overlay){
34161             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34162             o.unselectable();
34163             o.enableDisplayMode("block");
34164             // all splitbars share the same overlay
34165             Roo.bootstrap.SplitBar.prototype.overlay = o;
34166         }
34167         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34168         this.overlay.show();
34169         Roo.get(this.proxy).setDisplayed("block");
34170         var size = this.adapter.getElementSize(this);
34171         this.activeMinSize = this.getMinimumSize();;
34172         this.activeMaxSize = this.getMaximumSize();;
34173         var c1 = size - this.activeMinSize;
34174         var c2 = Math.max(this.activeMaxSize - size, 0);
34175         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34176             this.dd.resetConstraints();
34177             this.dd.setXConstraint(
34178                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34179                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34180             );
34181             this.dd.setYConstraint(0, 0);
34182         }else{
34183             this.dd.resetConstraints();
34184             this.dd.setXConstraint(0, 0);
34185             this.dd.setYConstraint(
34186                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34187                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34188             );
34189          }
34190         this.dragSpecs.startSize = size;
34191         this.dragSpecs.startPoint = [x, y];
34192         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34193     },
34194     
34195     /** 
34196      * @private Called after the drag operation by the DDProxy
34197      */
34198     onEndProxyDrag : function(e){
34199         Roo.get(this.proxy).setDisplayed(false);
34200         var endPoint = Roo.lib.Event.getXY(e);
34201         if(this.overlay){
34202             this.overlay.hide();
34203         }
34204         var newSize;
34205         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34206             newSize = this.dragSpecs.startSize + 
34207                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34208                     endPoint[0] - this.dragSpecs.startPoint[0] :
34209                     this.dragSpecs.startPoint[0] - endPoint[0]
34210                 );
34211         }else{
34212             newSize = this.dragSpecs.startSize + 
34213                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34214                     endPoint[1] - this.dragSpecs.startPoint[1] :
34215                     this.dragSpecs.startPoint[1] - endPoint[1]
34216                 );
34217         }
34218         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34219         if(newSize != this.dragSpecs.startSize){
34220             if(this.fireEvent('beforeapply', this, newSize) !== false){
34221                 this.adapter.setElementSize(this, newSize);
34222                 this.fireEvent("moved", this, newSize);
34223                 this.fireEvent("resize", this, newSize);
34224             }
34225         }
34226     },
34227     
34228     /**
34229      * Get the adapter this SplitBar uses
34230      * @return The adapter object
34231      */
34232     getAdapter : function(){
34233         return this.adapter;
34234     },
34235     
34236     /**
34237      * Set the adapter this SplitBar uses
34238      * @param {Object} adapter A SplitBar adapter object
34239      */
34240     setAdapter : function(adapter){
34241         this.adapter = adapter;
34242         this.adapter.init(this);
34243     },
34244     
34245     /**
34246      * Gets the minimum size for the resizing element
34247      * @return {Number} The minimum size
34248      */
34249     getMinimumSize : function(){
34250         return this.minSize;
34251     },
34252     
34253     /**
34254      * Sets the minimum size for the resizing element
34255      * @param {Number} minSize The minimum size
34256      */
34257     setMinimumSize : function(minSize){
34258         this.minSize = minSize;
34259     },
34260     
34261     /**
34262      * Gets the maximum size for the resizing element
34263      * @return {Number} The maximum size
34264      */
34265     getMaximumSize : function(){
34266         return this.maxSize;
34267     },
34268     
34269     /**
34270      * Sets the maximum size for the resizing element
34271      * @param {Number} maxSize The maximum size
34272      */
34273     setMaximumSize : function(maxSize){
34274         this.maxSize = maxSize;
34275     },
34276     
34277     /**
34278      * Sets the initialize size for the resizing element
34279      * @param {Number} size The initial size
34280      */
34281     setCurrentSize : function(size){
34282         var oldAnimate = this.animate;
34283         this.animate = false;
34284         this.adapter.setElementSize(this, size);
34285         this.animate = oldAnimate;
34286     },
34287     
34288     /**
34289      * Destroy this splitbar. 
34290      * @param {Boolean} removeEl True to remove the element
34291      */
34292     destroy : function(removeEl){
34293         if(this.shim){
34294             this.shim.remove();
34295         }
34296         this.dd.unreg();
34297         this.proxy.parentNode.removeChild(this.proxy);
34298         if(removeEl){
34299             this.el.remove();
34300         }
34301     }
34302 });
34303
34304 /**
34305  * @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.
34306  */
34307 Roo.bootstrap.SplitBar.createProxy = function(dir){
34308     var proxy = new Roo.Element(document.createElement("div"));
34309     proxy.unselectable();
34310     var cls = 'roo-splitbar-proxy';
34311     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34312     document.body.appendChild(proxy.dom);
34313     return proxy.dom;
34314 };
34315
34316 /** 
34317  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34318  * Default Adapter. It assumes the splitter and resizing element are not positioned
34319  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34320  */
34321 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34322 };
34323
34324 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34325     // do nothing for now
34326     init : function(s){
34327     
34328     },
34329     /**
34330      * Called before drag operations to get the current size of the resizing element. 
34331      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34332      */
34333      getElementSize : function(s){
34334         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34335             return s.resizingEl.getWidth();
34336         }else{
34337             return s.resizingEl.getHeight();
34338         }
34339     },
34340     
34341     /**
34342      * Called after drag operations to set the size of the resizing element.
34343      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34344      * @param {Number} newSize The new size to set
34345      * @param {Function} onComplete A function to be invoked when resizing is complete
34346      */
34347     setElementSize : function(s, newSize, onComplete){
34348         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34349             if(!s.animate){
34350                 s.resizingEl.setWidth(newSize);
34351                 if(onComplete){
34352                     onComplete(s, newSize);
34353                 }
34354             }else{
34355                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34356             }
34357         }else{
34358             
34359             if(!s.animate){
34360                 s.resizingEl.setHeight(newSize);
34361                 if(onComplete){
34362                     onComplete(s, newSize);
34363                 }
34364             }else{
34365                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34366             }
34367         }
34368     }
34369 };
34370
34371 /** 
34372  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34373  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34374  * Adapter that  moves the splitter element to align with the resized sizing element. 
34375  * Used with an absolute positioned SplitBar.
34376  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34377  * document.body, make sure you assign an id to the body element.
34378  */
34379 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34380     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34381     this.container = Roo.get(container);
34382 };
34383
34384 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34385     init : function(s){
34386         this.basic.init(s);
34387     },
34388     
34389     getElementSize : function(s){
34390         return this.basic.getElementSize(s);
34391     },
34392     
34393     setElementSize : function(s, newSize, onComplete){
34394         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34395     },
34396     
34397     moveSplitter : function(s){
34398         var yes = Roo.bootstrap.SplitBar;
34399         switch(s.placement){
34400             case yes.LEFT:
34401                 s.el.setX(s.resizingEl.getRight());
34402                 break;
34403             case yes.RIGHT:
34404                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34405                 break;
34406             case yes.TOP:
34407                 s.el.setY(s.resizingEl.getBottom());
34408                 break;
34409             case yes.BOTTOM:
34410                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34411                 break;
34412         }
34413     }
34414 };
34415
34416 /**
34417  * Orientation constant - Create a vertical SplitBar
34418  * @static
34419  * @type Number
34420  */
34421 Roo.bootstrap.SplitBar.VERTICAL = 1;
34422
34423 /**
34424  * Orientation constant - Create a horizontal SplitBar
34425  * @static
34426  * @type Number
34427  */
34428 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34429
34430 /**
34431  * Placement constant - The resizing element is to the left of the splitter element
34432  * @static
34433  * @type Number
34434  */
34435 Roo.bootstrap.SplitBar.LEFT = 1;
34436
34437 /**
34438  * Placement constant - The resizing element is to the right of the splitter element
34439  * @static
34440  * @type Number
34441  */
34442 Roo.bootstrap.SplitBar.RIGHT = 2;
34443
34444 /**
34445  * Placement constant - The resizing element is positioned above the splitter element
34446  * @static
34447  * @type Number
34448  */
34449 Roo.bootstrap.SplitBar.TOP = 3;
34450
34451 /**
34452  * Placement constant - The resizing element is positioned under splitter element
34453  * @static
34454  * @type Number
34455  */
34456 Roo.bootstrap.SplitBar.BOTTOM = 4;
34457 Roo.namespace("Roo.bootstrap.layout");/*
34458  * Based on:
34459  * Ext JS Library 1.1.1
34460  * Copyright(c) 2006-2007, Ext JS, LLC.
34461  *
34462  * Originally Released Under LGPL - original licence link has changed is not relivant.
34463  *
34464  * Fork - LGPL
34465  * <script type="text/javascript">
34466  */
34467
34468 /**
34469  * @class Roo.bootstrap.layout.Manager
34470  * @extends Roo.bootstrap.Component
34471  * Base class for layout managers.
34472  */
34473 Roo.bootstrap.layout.Manager = function(config)
34474 {
34475     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34476
34477
34478
34479
34480
34481     /** false to disable window resize monitoring @type Boolean */
34482     this.monitorWindowResize = true;
34483     this.regions = {};
34484     this.addEvents({
34485         /**
34486          * @event layout
34487          * Fires when a layout is performed.
34488          * @param {Roo.LayoutManager} this
34489          */
34490         "layout" : true,
34491         /**
34492          * @event regionresized
34493          * Fires when the user resizes a region.
34494          * @param {Roo.LayoutRegion} region The resized region
34495          * @param {Number} newSize The new size (width for east/west, height for north/south)
34496          */
34497         "regionresized" : true,
34498         /**
34499          * @event regioncollapsed
34500          * Fires when a region is collapsed.
34501          * @param {Roo.LayoutRegion} region The collapsed region
34502          */
34503         "regioncollapsed" : true,
34504         /**
34505          * @event regionexpanded
34506          * Fires when a region is expanded.
34507          * @param {Roo.LayoutRegion} region The expanded region
34508          */
34509         "regionexpanded" : true
34510     });
34511     this.updating = false;
34512
34513     if (config.el) {
34514         this.el = Roo.get(config.el);
34515         this.initEvents();
34516     }
34517
34518 };
34519
34520 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34521
34522
34523     regions : null,
34524
34525     monitorWindowResize : true,
34526
34527
34528     updating : false,
34529
34530
34531     onRender : function(ct, position)
34532     {
34533         if(!this.el){
34534             this.el = Roo.get(ct);
34535             this.initEvents();
34536         }
34537         //this.fireEvent('render',this);
34538     },
34539
34540
34541     initEvents: function()
34542     {
34543
34544
34545         // ie scrollbar fix
34546         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34547             document.body.scroll = "no";
34548         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34549             this.el.position('relative');
34550         }
34551         this.id = this.el.id;
34552         this.el.addClass("roo-layout-container");
34553         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34554         if(this.el.dom != document.body ) {
34555             this.el.on('resize', this.layout,this);
34556             this.el.on('show', this.layout,this);
34557         }
34558
34559     },
34560
34561     /**
34562      * Returns true if this layout is currently being updated
34563      * @return {Boolean}
34564      */
34565     isUpdating : function(){
34566         return this.updating;
34567     },
34568
34569     /**
34570      * Suspend the LayoutManager from doing auto-layouts while
34571      * making multiple add or remove calls
34572      */
34573     beginUpdate : function(){
34574         this.updating = true;
34575     },
34576
34577     /**
34578      * Restore auto-layouts and optionally disable the manager from performing a layout
34579      * @param {Boolean} noLayout true to disable a layout update
34580      */
34581     endUpdate : function(noLayout){
34582         this.updating = false;
34583         if(!noLayout){
34584             this.layout();
34585         }
34586     },
34587
34588     layout: function(){
34589         // abstract...
34590     },
34591
34592     onRegionResized : function(region, newSize){
34593         this.fireEvent("regionresized", region, newSize);
34594         this.layout();
34595     },
34596
34597     onRegionCollapsed : function(region){
34598         this.fireEvent("regioncollapsed", region);
34599     },
34600
34601     onRegionExpanded : function(region){
34602         this.fireEvent("regionexpanded", region);
34603     },
34604
34605     /**
34606      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34607      * performs box-model adjustments.
34608      * @return {Object} The size as an object {width: (the width), height: (the height)}
34609      */
34610     getViewSize : function()
34611     {
34612         var size;
34613         if(this.el.dom != document.body){
34614             size = this.el.getSize();
34615         }else{
34616             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34617         }
34618         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34619         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34620         return size;
34621     },
34622
34623     /**
34624      * Returns the Element this layout is bound to.
34625      * @return {Roo.Element}
34626      */
34627     getEl : function(){
34628         return this.el;
34629     },
34630
34631     /**
34632      * Returns the specified region.
34633      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34634      * @return {Roo.LayoutRegion}
34635      */
34636     getRegion : function(target){
34637         return this.regions[target.toLowerCase()];
34638     },
34639
34640     onWindowResize : function(){
34641         if(this.monitorWindowResize){
34642             this.layout();
34643         }
34644     }
34645 });
34646 /*
34647  * Based on:
34648  * Ext JS Library 1.1.1
34649  * Copyright(c) 2006-2007, Ext JS, LLC.
34650  *
34651  * Originally Released Under LGPL - original licence link has changed is not relivant.
34652  *
34653  * Fork - LGPL
34654  * <script type="text/javascript">
34655  */
34656 /**
34657  * @class Roo.bootstrap.layout.Border
34658  * @extends Roo.bootstrap.layout.Manager
34659  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34660  * please see: examples/bootstrap/nested.html<br><br>
34661  
34662 <b>The container the layout is rendered into can be either the body element or any other element.
34663 If it is not the body element, the container needs to either be an absolute positioned element,
34664 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34665 the container size if it is not the body element.</b>
34666
34667 * @constructor
34668 * Create a new Border
34669 * @param {Object} config Configuration options
34670  */
34671 Roo.bootstrap.layout.Border = function(config){
34672     config = config || {};
34673     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34674     
34675     
34676     
34677     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34678         if(config[region]){
34679             config[region].region = region;
34680             this.addRegion(config[region]);
34681         }
34682     },this);
34683     
34684 };
34685
34686 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34687
34688 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34689     /**
34690      * Creates and adds a new region if it doesn't already exist.
34691      * @param {String} target The target region key (north, south, east, west or center).
34692      * @param {Object} config The regions config object
34693      * @return {BorderLayoutRegion} The new region
34694      */
34695     addRegion : function(config)
34696     {
34697         if(!this.regions[config.region]){
34698             var r = this.factory(config);
34699             this.bindRegion(r);
34700         }
34701         return this.regions[config.region];
34702     },
34703
34704     // private (kinda)
34705     bindRegion : function(r){
34706         this.regions[r.config.region] = r;
34707         
34708         r.on("visibilitychange",    this.layout, this);
34709         r.on("paneladded",          this.layout, this);
34710         r.on("panelremoved",        this.layout, this);
34711         r.on("invalidated",         this.layout, this);
34712         r.on("resized",             this.onRegionResized, this);
34713         r.on("collapsed",           this.onRegionCollapsed, this);
34714         r.on("expanded",            this.onRegionExpanded, this);
34715     },
34716
34717     /**
34718      * Performs a layout update.
34719      */
34720     layout : function()
34721     {
34722         if(this.updating) {
34723             return;
34724         }
34725         
34726         // render all the rebions if they have not been done alreayd?
34727         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34728             if(this.regions[region] && !this.regions[region].bodyEl){
34729                 this.regions[region].onRender(this.el)
34730             }
34731         },this);
34732         
34733         var size = this.getViewSize();
34734         var w = size.width;
34735         var h = size.height;
34736         var centerW = w;
34737         var centerH = h;
34738         var centerY = 0;
34739         var centerX = 0;
34740         //var x = 0, y = 0;
34741
34742         var rs = this.regions;
34743         var north = rs["north"];
34744         var south = rs["south"]; 
34745         var west = rs["west"];
34746         var east = rs["east"];
34747         var center = rs["center"];
34748         //if(this.hideOnLayout){ // not supported anymore
34749             //c.el.setStyle("display", "none");
34750         //}
34751         if(north && north.isVisible()){
34752             var b = north.getBox();
34753             var m = north.getMargins();
34754             b.width = w - (m.left+m.right);
34755             b.x = m.left;
34756             b.y = m.top;
34757             centerY = b.height + b.y + m.bottom;
34758             centerH -= centerY;
34759             north.updateBox(this.safeBox(b));
34760         }
34761         if(south && south.isVisible()){
34762             var b = south.getBox();
34763             var m = south.getMargins();
34764             b.width = w - (m.left+m.right);
34765             b.x = m.left;
34766             var totalHeight = (b.height + m.top + m.bottom);
34767             b.y = h - totalHeight + m.top;
34768             centerH -= totalHeight;
34769             south.updateBox(this.safeBox(b));
34770         }
34771         if(west && west.isVisible()){
34772             var b = west.getBox();
34773             var m = west.getMargins();
34774             b.height = centerH - (m.top+m.bottom);
34775             b.x = m.left;
34776             b.y = centerY + m.top;
34777             var totalWidth = (b.width + m.left + m.right);
34778             centerX += totalWidth;
34779             centerW -= totalWidth;
34780             west.updateBox(this.safeBox(b));
34781         }
34782         if(east && east.isVisible()){
34783             var b = east.getBox();
34784             var m = east.getMargins();
34785             b.height = centerH - (m.top+m.bottom);
34786             var totalWidth = (b.width + m.left + m.right);
34787             b.x = w - totalWidth + m.left;
34788             b.y = centerY + m.top;
34789             centerW -= totalWidth;
34790             east.updateBox(this.safeBox(b));
34791         }
34792         if(center){
34793             var m = center.getMargins();
34794             var centerBox = {
34795                 x: centerX + m.left,
34796                 y: centerY + m.top,
34797                 width: centerW - (m.left+m.right),
34798                 height: centerH - (m.top+m.bottom)
34799             };
34800             //if(this.hideOnLayout){
34801                 //center.el.setStyle("display", "block");
34802             //}
34803             center.updateBox(this.safeBox(centerBox));
34804         }
34805         this.el.repaint();
34806         this.fireEvent("layout", this);
34807     },
34808
34809     // private
34810     safeBox : function(box){
34811         box.width = Math.max(0, box.width);
34812         box.height = Math.max(0, box.height);
34813         return box;
34814     },
34815
34816     /**
34817      * Adds a ContentPanel (or subclass) to this layout.
34818      * @param {String} target The target region key (north, south, east, west or center).
34819      * @param {Roo.ContentPanel} panel The panel to add
34820      * @return {Roo.ContentPanel} The added panel
34821      */
34822     add : function(target, panel){
34823          
34824         target = target.toLowerCase();
34825         return this.regions[target].add(panel);
34826     },
34827
34828     /**
34829      * Remove a ContentPanel (or subclass) to this layout.
34830      * @param {String} target The target region key (north, south, east, west or center).
34831      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34832      * @return {Roo.ContentPanel} The removed panel
34833      */
34834     remove : function(target, panel){
34835         target = target.toLowerCase();
34836         return this.regions[target].remove(panel);
34837     },
34838
34839     /**
34840      * Searches all regions for a panel with the specified id
34841      * @param {String} panelId
34842      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34843      */
34844     findPanel : function(panelId){
34845         var rs = this.regions;
34846         for(var target in rs){
34847             if(typeof rs[target] != "function"){
34848                 var p = rs[target].getPanel(panelId);
34849                 if(p){
34850                     return p;
34851                 }
34852             }
34853         }
34854         return null;
34855     },
34856
34857     /**
34858      * Searches all regions for a panel with the specified id and activates (shows) it.
34859      * @param {String/ContentPanel} panelId The panels id or the panel itself
34860      * @return {Roo.ContentPanel} The shown panel or null
34861      */
34862     showPanel : function(panelId) {
34863       var rs = this.regions;
34864       for(var target in rs){
34865          var r = rs[target];
34866          if(typeof r != "function"){
34867             if(r.hasPanel(panelId)){
34868                return r.showPanel(panelId);
34869             }
34870          }
34871       }
34872       return null;
34873    },
34874
34875    /**
34876      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34877      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34878      */
34879    /*
34880     restoreState : function(provider){
34881         if(!provider){
34882             provider = Roo.state.Manager;
34883         }
34884         var sm = new Roo.LayoutStateManager();
34885         sm.init(this, provider);
34886     },
34887 */
34888  
34889  
34890     /**
34891      * Adds a xtype elements to the layout.
34892      * <pre><code>
34893
34894 layout.addxtype({
34895        xtype : 'ContentPanel',
34896        region: 'west',
34897        items: [ .... ]
34898    }
34899 );
34900
34901 layout.addxtype({
34902         xtype : 'NestedLayoutPanel',
34903         region: 'west',
34904         layout: {
34905            center: { },
34906            west: { }   
34907         },
34908         items : [ ... list of content panels or nested layout panels.. ]
34909    }
34910 );
34911 </code></pre>
34912      * @param {Object} cfg Xtype definition of item to add.
34913      */
34914     addxtype : function(cfg)
34915     {
34916         // basically accepts a pannel...
34917         // can accept a layout region..!?!?
34918         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34919         
34920         
34921         // theory?  children can only be panels??
34922         
34923         //if (!cfg.xtype.match(/Panel$/)) {
34924         //    return false;
34925         //}
34926         var ret = false;
34927         
34928         if (typeof(cfg.region) == 'undefined') {
34929             Roo.log("Failed to add Panel, region was not set");
34930             Roo.log(cfg);
34931             return false;
34932         }
34933         var region = cfg.region;
34934         delete cfg.region;
34935         
34936           
34937         var xitems = [];
34938         if (cfg.items) {
34939             xitems = cfg.items;
34940             delete cfg.items;
34941         }
34942         var nb = false;
34943         
34944         switch(cfg.xtype) 
34945         {
34946             case 'Content':  // ContentPanel (el, cfg)
34947             case 'Scroll':  // ContentPanel (el, cfg)
34948             case 'View': 
34949                 cfg.autoCreate = true;
34950                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34951                 //} else {
34952                 //    var el = this.el.createChild();
34953                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34954                 //}
34955                 
34956                 this.add(region, ret);
34957                 break;
34958             
34959             /*
34960             case 'TreePanel': // our new panel!
34961                 cfg.el = this.el.createChild();
34962                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34963                 this.add(region, ret);
34964                 break;
34965             */
34966             
34967             case 'Nest': 
34968                 // create a new Layout (which is  a Border Layout...
34969                 
34970                 var clayout = cfg.layout;
34971                 clayout.el  = this.el.createChild();
34972                 clayout.items   = clayout.items  || [];
34973                 
34974                 delete cfg.layout;
34975                 
34976                 // replace this exitems with the clayout ones..
34977                 xitems = clayout.items;
34978                  
34979                 // force background off if it's in center...
34980                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34981                     cfg.background = false;
34982                 }
34983                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34984                 
34985                 
34986                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34987                 //console.log('adding nested layout panel '  + cfg.toSource());
34988                 this.add(region, ret);
34989                 nb = {}; /// find first...
34990                 break;
34991             
34992             case 'Grid':
34993                 
34994                 // needs grid and region
34995                 
34996                 //var el = this.getRegion(region).el.createChild();
34997                 /*
34998                  *var el = this.el.createChild();
34999                 // create the grid first...
35000                 cfg.grid.container = el;
35001                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35002                 */
35003                 
35004                 if (region == 'center' && this.active ) {
35005                     cfg.background = false;
35006                 }
35007                 
35008                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35009                 
35010                 this.add(region, ret);
35011                 /*
35012                 if (cfg.background) {
35013                     // render grid on panel activation (if panel background)
35014                     ret.on('activate', function(gp) {
35015                         if (!gp.grid.rendered) {
35016                     //        gp.grid.render(el);
35017                         }
35018                     });
35019                 } else {
35020                   //  cfg.grid.render(el);
35021                 }
35022                 */
35023                 break;
35024            
35025            
35026             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35027                 // it was the old xcomponent building that caused this before.
35028                 // espeically if border is the top element in the tree.
35029                 ret = this;
35030                 break; 
35031                 
35032                     
35033                 
35034                 
35035                 
35036             default:
35037                 /*
35038                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35039                     
35040                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35041                     this.add(region, ret);
35042                 } else {
35043                 */
35044                     Roo.log(cfg);
35045                     throw "Can not add '" + cfg.xtype + "' to Border";
35046                     return null;
35047              
35048                                 
35049              
35050         }
35051         this.beginUpdate();
35052         // add children..
35053         var region = '';
35054         var abn = {};
35055         Roo.each(xitems, function(i)  {
35056             region = nb && i.region ? i.region : false;
35057             
35058             var add = ret.addxtype(i);
35059            
35060             if (region) {
35061                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35062                 if (!i.background) {
35063                     abn[region] = nb[region] ;
35064                 }
35065             }
35066             
35067         });
35068         this.endUpdate();
35069
35070         // make the last non-background panel active..
35071         //if (nb) { Roo.log(abn); }
35072         if (nb) {
35073             
35074             for(var r in abn) {
35075                 region = this.getRegion(r);
35076                 if (region) {
35077                     // tried using nb[r], but it does not work..
35078                      
35079                     region.showPanel(abn[r]);
35080                    
35081                 }
35082             }
35083         }
35084         return ret;
35085         
35086     },
35087     
35088     
35089 // private
35090     factory : function(cfg)
35091     {
35092         
35093         var validRegions = Roo.bootstrap.layout.Border.regions;
35094
35095         var target = cfg.region;
35096         cfg.mgr = this;
35097         
35098         var r = Roo.bootstrap.layout;
35099         Roo.log(target);
35100         switch(target){
35101             case "north":
35102                 return new r.North(cfg);
35103             case "south":
35104                 return new r.South(cfg);
35105             case "east":
35106                 return new r.East(cfg);
35107             case "west":
35108                 return new r.West(cfg);
35109             case "center":
35110                 return new r.Center(cfg);
35111         }
35112         throw 'Layout region "'+target+'" not supported.';
35113     }
35114     
35115     
35116 });
35117  /*
35118  * Based on:
35119  * Ext JS Library 1.1.1
35120  * Copyright(c) 2006-2007, Ext JS, LLC.
35121  *
35122  * Originally Released Under LGPL - original licence link has changed is not relivant.
35123  *
35124  * Fork - LGPL
35125  * <script type="text/javascript">
35126  */
35127  
35128 /**
35129  * @class Roo.bootstrap.layout.Basic
35130  * @extends Roo.util.Observable
35131  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35132  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35133  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35134  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35135  * @cfg {string}   region  the region that it inhabits..
35136  * @cfg {bool}   skipConfig skip config?
35137  * 
35138
35139  */
35140 Roo.bootstrap.layout.Basic = function(config){
35141     
35142     this.mgr = config.mgr;
35143     
35144     this.position = config.region;
35145     
35146     var skipConfig = config.skipConfig;
35147     
35148     this.events = {
35149         /**
35150          * @scope Roo.BasicLayoutRegion
35151          */
35152         
35153         /**
35154          * @event beforeremove
35155          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35156          * @param {Roo.LayoutRegion} this
35157          * @param {Roo.ContentPanel} panel The panel
35158          * @param {Object} e The cancel event object
35159          */
35160         "beforeremove" : true,
35161         /**
35162          * @event invalidated
35163          * Fires when the layout for this region is changed.
35164          * @param {Roo.LayoutRegion} this
35165          */
35166         "invalidated" : true,
35167         /**
35168          * @event visibilitychange
35169          * Fires when this region is shown or hidden 
35170          * @param {Roo.LayoutRegion} this
35171          * @param {Boolean} visibility true or false
35172          */
35173         "visibilitychange" : true,
35174         /**
35175          * @event paneladded
35176          * Fires when a panel is added. 
35177          * @param {Roo.LayoutRegion} this
35178          * @param {Roo.ContentPanel} panel The panel
35179          */
35180         "paneladded" : true,
35181         /**
35182          * @event panelremoved
35183          * Fires when a panel is removed. 
35184          * @param {Roo.LayoutRegion} this
35185          * @param {Roo.ContentPanel} panel The panel
35186          */
35187         "panelremoved" : true,
35188         /**
35189          * @event beforecollapse
35190          * Fires when this region before collapse.
35191          * @param {Roo.LayoutRegion} this
35192          */
35193         "beforecollapse" : true,
35194         /**
35195          * @event collapsed
35196          * Fires when this region is collapsed.
35197          * @param {Roo.LayoutRegion} this
35198          */
35199         "collapsed" : true,
35200         /**
35201          * @event expanded
35202          * Fires when this region is expanded.
35203          * @param {Roo.LayoutRegion} this
35204          */
35205         "expanded" : true,
35206         /**
35207          * @event slideshow
35208          * Fires when this region is slid into view.
35209          * @param {Roo.LayoutRegion} this
35210          */
35211         "slideshow" : true,
35212         /**
35213          * @event slidehide
35214          * Fires when this region slides out of view. 
35215          * @param {Roo.LayoutRegion} this
35216          */
35217         "slidehide" : true,
35218         /**
35219          * @event panelactivated
35220          * Fires when a panel is activated. 
35221          * @param {Roo.LayoutRegion} this
35222          * @param {Roo.ContentPanel} panel The activated panel
35223          */
35224         "panelactivated" : true,
35225         /**
35226          * @event resized
35227          * Fires when the user resizes this region. 
35228          * @param {Roo.LayoutRegion} this
35229          * @param {Number} newSize The new size (width for east/west, height for north/south)
35230          */
35231         "resized" : true
35232     };
35233     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35234     this.panels = new Roo.util.MixedCollection();
35235     this.panels.getKey = this.getPanelId.createDelegate(this);
35236     this.box = null;
35237     this.activePanel = null;
35238     // ensure listeners are added...
35239     
35240     if (config.listeners || config.events) {
35241         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35242             listeners : config.listeners || {},
35243             events : config.events || {}
35244         });
35245     }
35246     
35247     if(skipConfig !== true){
35248         this.applyConfig(config);
35249     }
35250 };
35251
35252 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35253 {
35254     getPanelId : function(p){
35255         return p.getId();
35256     },
35257     
35258     applyConfig : function(config){
35259         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35260         this.config = config;
35261         
35262     },
35263     
35264     /**
35265      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35266      * the width, for horizontal (north, south) the height.
35267      * @param {Number} newSize The new width or height
35268      */
35269     resizeTo : function(newSize){
35270         var el = this.el ? this.el :
35271                  (this.activePanel ? this.activePanel.getEl() : null);
35272         if(el){
35273             switch(this.position){
35274                 case "east":
35275                 case "west":
35276                     el.setWidth(newSize);
35277                     this.fireEvent("resized", this, newSize);
35278                 break;
35279                 case "north":
35280                 case "south":
35281                     el.setHeight(newSize);
35282                     this.fireEvent("resized", this, newSize);
35283                 break;                
35284             }
35285         }
35286     },
35287     
35288     getBox : function(){
35289         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35290     },
35291     
35292     getMargins : function(){
35293         return this.margins;
35294     },
35295     
35296     updateBox : function(box){
35297         this.box = box;
35298         var el = this.activePanel.getEl();
35299         el.dom.style.left = box.x + "px";
35300         el.dom.style.top = box.y + "px";
35301         this.activePanel.setSize(box.width, box.height);
35302     },
35303     
35304     /**
35305      * Returns the container element for this region.
35306      * @return {Roo.Element}
35307      */
35308     getEl : function(){
35309         return this.activePanel;
35310     },
35311     
35312     /**
35313      * Returns true if this region is currently visible.
35314      * @return {Boolean}
35315      */
35316     isVisible : function(){
35317         return this.activePanel ? true : false;
35318     },
35319     
35320     setActivePanel : function(panel){
35321         panel = this.getPanel(panel);
35322         if(this.activePanel && this.activePanel != panel){
35323             this.activePanel.setActiveState(false);
35324             this.activePanel.getEl().setLeftTop(-10000,-10000);
35325         }
35326         this.activePanel = panel;
35327         panel.setActiveState(true);
35328         if(this.box){
35329             panel.setSize(this.box.width, this.box.height);
35330         }
35331         this.fireEvent("panelactivated", this, panel);
35332         this.fireEvent("invalidated");
35333     },
35334     
35335     /**
35336      * Show the specified panel.
35337      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35338      * @return {Roo.ContentPanel} The shown panel or null
35339      */
35340     showPanel : function(panel){
35341         panel = this.getPanel(panel);
35342         if(panel){
35343             this.setActivePanel(panel);
35344         }
35345         return panel;
35346     },
35347     
35348     /**
35349      * Get the active panel for this region.
35350      * @return {Roo.ContentPanel} The active panel or null
35351      */
35352     getActivePanel : function(){
35353         return this.activePanel;
35354     },
35355     
35356     /**
35357      * Add the passed ContentPanel(s)
35358      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35359      * @return {Roo.ContentPanel} The panel added (if only one was added)
35360      */
35361     add : function(panel){
35362         if(arguments.length > 1){
35363             for(var i = 0, len = arguments.length; i < len; i++) {
35364                 this.add(arguments[i]);
35365             }
35366             return null;
35367         }
35368         if(this.hasPanel(panel)){
35369             this.showPanel(panel);
35370             return panel;
35371         }
35372         var el = panel.getEl();
35373         if(el.dom.parentNode != this.mgr.el.dom){
35374             this.mgr.el.dom.appendChild(el.dom);
35375         }
35376         if(panel.setRegion){
35377             panel.setRegion(this);
35378         }
35379         this.panels.add(panel);
35380         el.setStyle("position", "absolute");
35381         if(!panel.background){
35382             this.setActivePanel(panel);
35383             if(this.config.initialSize && this.panels.getCount()==1){
35384                 this.resizeTo(this.config.initialSize);
35385             }
35386         }
35387         this.fireEvent("paneladded", this, panel);
35388         return panel;
35389     },
35390     
35391     /**
35392      * Returns true if the panel is in this region.
35393      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35394      * @return {Boolean}
35395      */
35396     hasPanel : function(panel){
35397         if(typeof panel == "object"){ // must be panel obj
35398             panel = panel.getId();
35399         }
35400         return this.getPanel(panel) ? true : false;
35401     },
35402     
35403     /**
35404      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35405      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35406      * @param {Boolean} preservePanel Overrides the config preservePanel option
35407      * @return {Roo.ContentPanel} The panel that was removed
35408      */
35409     remove : function(panel, preservePanel){
35410         panel = this.getPanel(panel);
35411         if(!panel){
35412             return null;
35413         }
35414         var e = {};
35415         this.fireEvent("beforeremove", this, panel, e);
35416         if(e.cancel === true){
35417             return null;
35418         }
35419         var panelId = panel.getId();
35420         this.panels.removeKey(panelId);
35421         return panel;
35422     },
35423     
35424     /**
35425      * Returns the panel specified or null if it's not in this region.
35426      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35427      * @return {Roo.ContentPanel}
35428      */
35429     getPanel : function(id){
35430         if(typeof id == "object"){ // must be panel obj
35431             return id;
35432         }
35433         return this.panels.get(id);
35434     },
35435     
35436     /**
35437      * Returns this regions position (north/south/east/west/center).
35438      * @return {String} 
35439      */
35440     getPosition: function(){
35441         return this.position;    
35442     }
35443 });/*
35444  * Based on:
35445  * Ext JS Library 1.1.1
35446  * Copyright(c) 2006-2007, Ext JS, LLC.
35447  *
35448  * Originally Released Under LGPL - original licence link has changed is not relivant.
35449  *
35450  * Fork - LGPL
35451  * <script type="text/javascript">
35452  */
35453  
35454 /**
35455  * @class Roo.bootstrap.layout.Region
35456  * @extends Roo.bootstrap.layout.Basic
35457  * This class represents a region in a layout manager.
35458  
35459  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35460  * @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})
35461  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35462  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35463  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35464  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35465  * @cfg {String}    title           The title for the region (overrides panel titles)
35466  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35467  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35468  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35469  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35470  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35471  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35472  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35473  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35474  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35475  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35476
35477  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35478  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35479  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35480  * @cfg {Number}    width           For East/West panels
35481  * @cfg {Number}    height          For North/South panels
35482  * @cfg {Boolean}   split           To show the splitter
35483  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35484  * 
35485  * @cfg {string}   cls             Extra CSS classes to add to region
35486  * 
35487  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35488  * @cfg {string}   region  the region that it inhabits..
35489  *
35490
35491  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35492  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35493
35494  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35495  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35496  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35497  */
35498 Roo.bootstrap.layout.Region = function(config)
35499 {
35500     this.applyConfig(config);
35501
35502     var mgr = config.mgr;
35503     var pos = config.region;
35504     config.skipConfig = true;
35505     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35506     
35507     if (mgr.el) {
35508         this.onRender(mgr.el);   
35509     }
35510      
35511     this.visible = true;
35512     this.collapsed = false;
35513     this.unrendered_panels = [];
35514 };
35515
35516 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35517
35518     position: '', // set by wrapper (eg. north/south etc..)
35519     unrendered_panels : null,  // unrendered panels.
35520     createBody : function(){
35521         /** This region's body element 
35522         * @type Roo.Element */
35523         this.bodyEl = this.el.createChild({
35524                 tag: "div",
35525                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35526         });
35527     },
35528
35529     onRender: function(ctr, pos)
35530     {
35531         var dh = Roo.DomHelper;
35532         /** This region's container element 
35533         * @type Roo.Element */
35534         this.el = dh.append(ctr.dom, {
35535                 tag: "div",
35536                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35537             }, true);
35538         /** This region's title element 
35539         * @type Roo.Element */
35540     
35541         this.titleEl = dh.append(this.el.dom,
35542             {
35543                     tag: "div",
35544                     unselectable: "on",
35545                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35546                     children:[
35547                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35548                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35549                     ]}, true);
35550         
35551         this.titleEl.enableDisplayMode();
35552         /** This region's title text element 
35553         * @type HTMLElement */
35554         this.titleTextEl = this.titleEl.dom.firstChild;
35555         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35556         /*
35557         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35558         this.closeBtn.enableDisplayMode();
35559         this.closeBtn.on("click", this.closeClicked, this);
35560         this.closeBtn.hide();
35561     */
35562         this.createBody(this.config);
35563         if(this.config.hideWhenEmpty){
35564             this.hide();
35565             this.on("paneladded", this.validateVisibility, this);
35566             this.on("panelremoved", this.validateVisibility, this);
35567         }
35568         if(this.autoScroll){
35569             this.bodyEl.setStyle("overflow", "auto");
35570         }else{
35571             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35572         }
35573         //if(c.titlebar !== false){
35574             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35575                 this.titleEl.hide();
35576             }else{
35577                 this.titleEl.show();
35578                 if(this.config.title){
35579                     this.titleTextEl.innerHTML = this.config.title;
35580                 }
35581             }
35582         //}
35583         if(this.config.collapsed){
35584             this.collapse(true);
35585         }
35586         if(this.config.hidden){
35587             this.hide();
35588         }
35589         
35590         if (this.unrendered_panels && this.unrendered_panels.length) {
35591             for (var i =0;i< this.unrendered_panels.length; i++) {
35592                 this.add(this.unrendered_panels[i]);
35593             }
35594             this.unrendered_panels = null;
35595             
35596         }
35597         
35598     },
35599     
35600     applyConfig : function(c)
35601     {
35602         /*
35603          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35604             var dh = Roo.DomHelper;
35605             if(c.titlebar !== false){
35606                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35607                 this.collapseBtn.on("click", this.collapse, this);
35608                 this.collapseBtn.enableDisplayMode();
35609                 /*
35610                 if(c.showPin === true || this.showPin){
35611                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35612                     this.stickBtn.enableDisplayMode();
35613                     this.stickBtn.on("click", this.expand, this);
35614                     this.stickBtn.hide();
35615                 }
35616                 
35617             }
35618             */
35619             /** This region's collapsed element
35620             * @type Roo.Element */
35621             /*
35622              *
35623             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35624                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35625             ]}, true);
35626             
35627             if(c.floatable !== false){
35628                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35629                this.collapsedEl.on("click", this.collapseClick, this);
35630             }
35631
35632             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35633                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35634                    id: "message", unselectable: "on", style:{"float":"left"}});
35635                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35636              }
35637             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35638             this.expandBtn.on("click", this.expand, this);
35639             
35640         }
35641         
35642         if(this.collapseBtn){
35643             this.collapseBtn.setVisible(c.collapsible == true);
35644         }
35645         
35646         this.cmargins = c.cmargins || this.cmargins ||
35647                          (this.position == "west" || this.position == "east" ?
35648                              {top: 0, left: 2, right:2, bottom: 0} :
35649                              {top: 2, left: 0, right:0, bottom: 2});
35650         */
35651         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35652         
35653         
35654         this.bottomTabs = c.tabPosition != "top";
35655         
35656         this.autoScroll = c.autoScroll || false;
35657         
35658         
35659        
35660         
35661         this.duration = c.duration || .30;
35662         this.slideDuration = c.slideDuration || .45;
35663         this.config = c;
35664        
35665     },
35666     /**
35667      * Returns true if this region is currently visible.
35668      * @return {Boolean}
35669      */
35670     isVisible : function(){
35671         return this.visible;
35672     },
35673
35674     /**
35675      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35676      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35677      */
35678     //setCollapsedTitle : function(title){
35679     //    title = title || "&#160;";
35680      //   if(this.collapsedTitleTextEl){
35681       //      this.collapsedTitleTextEl.innerHTML = title;
35682        // }
35683     //},
35684
35685     getBox : function(){
35686         var b;
35687       //  if(!this.collapsed){
35688             b = this.el.getBox(false, true);
35689        // }else{
35690           //  b = this.collapsedEl.getBox(false, true);
35691         //}
35692         return b;
35693     },
35694
35695     getMargins : function(){
35696         return this.margins;
35697         //return this.collapsed ? this.cmargins : this.margins;
35698     },
35699 /*
35700     highlight : function(){
35701         this.el.addClass("x-layout-panel-dragover");
35702     },
35703
35704     unhighlight : function(){
35705         this.el.removeClass("x-layout-panel-dragover");
35706     },
35707 */
35708     updateBox : function(box)
35709     {
35710         if (!this.bodyEl) {
35711             return; // not rendered yet..
35712         }
35713         
35714         this.box = box;
35715         if(!this.collapsed){
35716             this.el.dom.style.left = box.x + "px";
35717             this.el.dom.style.top = box.y + "px";
35718             this.updateBody(box.width, box.height);
35719         }else{
35720             this.collapsedEl.dom.style.left = box.x + "px";
35721             this.collapsedEl.dom.style.top = box.y + "px";
35722             this.collapsedEl.setSize(box.width, box.height);
35723         }
35724         if(this.tabs){
35725             this.tabs.autoSizeTabs();
35726         }
35727     },
35728
35729     updateBody : function(w, h)
35730     {
35731         if(w !== null){
35732             this.el.setWidth(w);
35733             w -= this.el.getBorderWidth("rl");
35734             if(this.config.adjustments){
35735                 w += this.config.adjustments[0];
35736             }
35737         }
35738         if(h !== null && h > 0){
35739             this.el.setHeight(h);
35740             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35741             h -= this.el.getBorderWidth("tb");
35742             if(this.config.adjustments){
35743                 h += this.config.adjustments[1];
35744             }
35745             this.bodyEl.setHeight(h);
35746             if(this.tabs){
35747                 h = this.tabs.syncHeight(h);
35748             }
35749         }
35750         if(this.panelSize){
35751             w = w !== null ? w : this.panelSize.width;
35752             h = h !== null ? h : this.panelSize.height;
35753         }
35754         if(this.activePanel){
35755             var el = this.activePanel.getEl();
35756             w = w !== null ? w : el.getWidth();
35757             h = h !== null ? h : el.getHeight();
35758             this.panelSize = {width: w, height: h};
35759             this.activePanel.setSize(w, h);
35760         }
35761         if(Roo.isIE && this.tabs){
35762             this.tabs.el.repaint();
35763         }
35764     },
35765
35766     /**
35767      * Returns the container element for this region.
35768      * @return {Roo.Element}
35769      */
35770     getEl : function(){
35771         return this.el;
35772     },
35773
35774     /**
35775      * Hides this region.
35776      */
35777     hide : function(){
35778         //if(!this.collapsed){
35779             this.el.dom.style.left = "-2000px";
35780             this.el.hide();
35781         //}else{
35782          //   this.collapsedEl.dom.style.left = "-2000px";
35783          //   this.collapsedEl.hide();
35784        // }
35785         this.visible = false;
35786         this.fireEvent("visibilitychange", this, false);
35787     },
35788
35789     /**
35790      * Shows this region if it was previously hidden.
35791      */
35792     show : function(){
35793         //if(!this.collapsed){
35794             this.el.show();
35795         //}else{
35796         //    this.collapsedEl.show();
35797        // }
35798         this.visible = true;
35799         this.fireEvent("visibilitychange", this, true);
35800     },
35801 /*
35802     closeClicked : function(){
35803         if(this.activePanel){
35804             this.remove(this.activePanel);
35805         }
35806     },
35807
35808     collapseClick : function(e){
35809         if(this.isSlid){
35810            e.stopPropagation();
35811            this.slideIn();
35812         }else{
35813            e.stopPropagation();
35814            this.slideOut();
35815         }
35816     },
35817 */
35818     /**
35819      * Collapses this region.
35820      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35821      */
35822     /*
35823     collapse : function(skipAnim, skipCheck = false){
35824         if(this.collapsed) {
35825             return;
35826         }
35827         
35828         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35829             
35830             this.collapsed = true;
35831             if(this.split){
35832                 this.split.el.hide();
35833             }
35834             if(this.config.animate && skipAnim !== true){
35835                 this.fireEvent("invalidated", this);
35836                 this.animateCollapse();
35837             }else{
35838                 this.el.setLocation(-20000,-20000);
35839                 this.el.hide();
35840                 this.collapsedEl.show();
35841                 this.fireEvent("collapsed", this);
35842                 this.fireEvent("invalidated", this);
35843             }
35844         }
35845         
35846     },
35847 */
35848     animateCollapse : function(){
35849         // overridden
35850     },
35851
35852     /**
35853      * Expands this region if it was previously collapsed.
35854      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35855      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35856      */
35857     /*
35858     expand : function(e, skipAnim){
35859         if(e) {
35860             e.stopPropagation();
35861         }
35862         if(!this.collapsed || this.el.hasActiveFx()) {
35863             return;
35864         }
35865         if(this.isSlid){
35866             this.afterSlideIn();
35867             skipAnim = true;
35868         }
35869         this.collapsed = false;
35870         if(this.config.animate && skipAnim !== true){
35871             this.animateExpand();
35872         }else{
35873             this.el.show();
35874             if(this.split){
35875                 this.split.el.show();
35876             }
35877             this.collapsedEl.setLocation(-2000,-2000);
35878             this.collapsedEl.hide();
35879             this.fireEvent("invalidated", this);
35880             this.fireEvent("expanded", this);
35881         }
35882     },
35883 */
35884     animateExpand : function(){
35885         // overridden
35886     },
35887
35888     initTabs : function()
35889     {
35890         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35891         
35892         var ts = new Roo.bootstrap.panel.Tabs({
35893                 el: this.bodyEl.dom,
35894                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35895                 disableTooltips: this.config.disableTabTips,
35896                 toolbar : this.config.toolbar
35897             });
35898         
35899         if(this.config.hideTabs){
35900             ts.stripWrap.setDisplayed(false);
35901         }
35902         this.tabs = ts;
35903         ts.resizeTabs = this.config.resizeTabs === true;
35904         ts.minTabWidth = this.config.minTabWidth || 40;
35905         ts.maxTabWidth = this.config.maxTabWidth || 250;
35906         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35907         ts.monitorResize = false;
35908         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35909         ts.bodyEl.addClass('roo-layout-tabs-body');
35910         this.panels.each(this.initPanelAsTab, this);
35911     },
35912
35913     initPanelAsTab : function(panel){
35914         var ti = this.tabs.addTab(
35915             panel.getEl().id,
35916             panel.getTitle(),
35917             null,
35918             this.config.closeOnTab && panel.isClosable(),
35919             panel.tpl
35920         );
35921         if(panel.tabTip !== undefined){
35922             ti.setTooltip(panel.tabTip);
35923         }
35924         ti.on("activate", function(){
35925               this.setActivePanel(panel);
35926         }, this);
35927         
35928         if(this.config.closeOnTab){
35929             ti.on("beforeclose", function(t, e){
35930                 e.cancel = true;
35931                 this.remove(panel);
35932             }, this);
35933         }
35934         
35935         panel.tabItem = ti;
35936         
35937         return ti;
35938     },
35939
35940     updatePanelTitle : function(panel, title)
35941     {
35942         if(this.activePanel == panel){
35943             this.updateTitle(title);
35944         }
35945         if(this.tabs){
35946             var ti = this.tabs.getTab(panel.getEl().id);
35947             ti.setText(title);
35948             if(panel.tabTip !== undefined){
35949                 ti.setTooltip(panel.tabTip);
35950             }
35951         }
35952     },
35953
35954     updateTitle : function(title){
35955         if(this.titleTextEl && !this.config.title){
35956             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35957         }
35958     },
35959
35960     setActivePanel : function(panel)
35961     {
35962         panel = this.getPanel(panel);
35963         if(this.activePanel && this.activePanel != panel){
35964             if(this.activePanel.setActiveState(false) === false){
35965                 return;
35966             }
35967         }
35968         this.activePanel = panel;
35969         panel.setActiveState(true);
35970         if(this.panelSize){
35971             panel.setSize(this.panelSize.width, this.panelSize.height);
35972         }
35973         if(this.closeBtn){
35974             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35975         }
35976         this.updateTitle(panel.getTitle());
35977         if(this.tabs){
35978             this.fireEvent("invalidated", this);
35979         }
35980         this.fireEvent("panelactivated", this, panel);
35981     },
35982
35983     /**
35984      * Shows the specified panel.
35985      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35986      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35987      */
35988     showPanel : function(panel)
35989     {
35990         panel = this.getPanel(panel);
35991         if(panel){
35992             if(this.tabs){
35993                 var tab = this.tabs.getTab(panel.getEl().id);
35994                 if(tab.isHidden()){
35995                     this.tabs.unhideTab(tab.id);
35996                 }
35997                 tab.activate();
35998             }else{
35999                 this.setActivePanel(panel);
36000             }
36001         }
36002         return panel;
36003     },
36004
36005     /**
36006      * Get the active panel for this region.
36007      * @return {Roo.ContentPanel} The active panel or null
36008      */
36009     getActivePanel : function(){
36010         return this.activePanel;
36011     },
36012
36013     validateVisibility : function(){
36014         if(this.panels.getCount() < 1){
36015             this.updateTitle("&#160;");
36016             this.closeBtn.hide();
36017             this.hide();
36018         }else{
36019             if(!this.isVisible()){
36020                 this.show();
36021             }
36022         }
36023     },
36024
36025     /**
36026      * Adds the passed ContentPanel(s) to this region.
36027      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36028      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36029      */
36030     add : function(panel)
36031     {
36032         if(arguments.length > 1){
36033             for(var i = 0, len = arguments.length; i < len; i++) {
36034                 this.add(arguments[i]);
36035             }
36036             return null;
36037         }
36038         
36039         // if we have not been rendered yet, then we can not really do much of this..
36040         if (!this.bodyEl) {
36041             this.unrendered_panels.push(panel);
36042             return panel;
36043         }
36044         
36045         
36046         
36047         
36048         if(this.hasPanel(panel)){
36049             this.showPanel(panel);
36050             return panel;
36051         }
36052         panel.setRegion(this);
36053         this.panels.add(panel);
36054        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36055             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36056             // and hide them... ???
36057             this.bodyEl.dom.appendChild(panel.getEl().dom);
36058             if(panel.background !== true){
36059                 this.setActivePanel(panel);
36060             }
36061             this.fireEvent("paneladded", this, panel);
36062             return panel;
36063         }
36064         */
36065         if(!this.tabs){
36066             this.initTabs();
36067         }else{
36068             this.initPanelAsTab(panel);
36069         }
36070         
36071         
36072         if(panel.background !== true){
36073             this.tabs.activate(panel.getEl().id);
36074         }
36075         this.fireEvent("paneladded", this, panel);
36076         return panel;
36077     },
36078
36079     /**
36080      * Hides the tab for the specified panel.
36081      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36082      */
36083     hidePanel : function(panel){
36084         if(this.tabs && (panel = this.getPanel(panel))){
36085             this.tabs.hideTab(panel.getEl().id);
36086         }
36087     },
36088
36089     /**
36090      * Unhides the tab for a previously hidden panel.
36091      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36092      */
36093     unhidePanel : function(panel){
36094         if(this.tabs && (panel = this.getPanel(panel))){
36095             this.tabs.unhideTab(panel.getEl().id);
36096         }
36097     },
36098
36099     clearPanels : function(){
36100         while(this.panels.getCount() > 0){
36101              this.remove(this.panels.first());
36102         }
36103     },
36104
36105     /**
36106      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36107      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36108      * @param {Boolean} preservePanel Overrides the config preservePanel option
36109      * @return {Roo.ContentPanel} The panel that was removed
36110      */
36111     remove : function(panel, preservePanel)
36112     {
36113         panel = this.getPanel(panel);
36114         if(!panel){
36115             return null;
36116         }
36117         var e = {};
36118         this.fireEvent("beforeremove", this, panel, e);
36119         if(e.cancel === true){
36120             return null;
36121         }
36122         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36123         var panelId = panel.getId();
36124         this.panels.removeKey(panelId);
36125         if(preservePanel){
36126             document.body.appendChild(panel.getEl().dom);
36127         }
36128         if(this.tabs){
36129             this.tabs.removeTab(panel.getEl().id);
36130         }else if (!preservePanel){
36131             this.bodyEl.dom.removeChild(panel.getEl().dom);
36132         }
36133         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36134             var p = this.panels.first();
36135             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36136             tempEl.appendChild(p.getEl().dom);
36137             this.bodyEl.update("");
36138             this.bodyEl.dom.appendChild(p.getEl().dom);
36139             tempEl = null;
36140             this.updateTitle(p.getTitle());
36141             this.tabs = null;
36142             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36143             this.setActivePanel(p);
36144         }
36145         panel.setRegion(null);
36146         if(this.activePanel == panel){
36147             this.activePanel = null;
36148         }
36149         if(this.config.autoDestroy !== false && preservePanel !== true){
36150             try{panel.destroy();}catch(e){}
36151         }
36152         this.fireEvent("panelremoved", this, panel);
36153         return panel;
36154     },
36155
36156     /**
36157      * Returns the TabPanel component used by this region
36158      * @return {Roo.TabPanel}
36159      */
36160     getTabs : function(){
36161         return this.tabs;
36162     },
36163
36164     createTool : function(parentEl, className){
36165         var btn = Roo.DomHelper.append(parentEl, {
36166             tag: "div",
36167             cls: "x-layout-tools-button",
36168             children: [ {
36169                 tag: "div",
36170                 cls: "roo-layout-tools-button-inner " + className,
36171                 html: "&#160;"
36172             }]
36173         }, true);
36174         btn.addClassOnOver("roo-layout-tools-button-over");
36175         return btn;
36176     }
36177 });/*
36178  * Based on:
36179  * Ext JS Library 1.1.1
36180  * Copyright(c) 2006-2007, Ext JS, LLC.
36181  *
36182  * Originally Released Under LGPL - original licence link has changed is not relivant.
36183  *
36184  * Fork - LGPL
36185  * <script type="text/javascript">
36186  */
36187  
36188
36189
36190 /**
36191  * @class Roo.SplitLayoutRegion
36192  * @extends Roo.LayoutRegion
36193  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36194  */
36195 Roo.bootstrap.layout.Split = function(config){
36196     this.cursor = config.cursor;
36197     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36198 };
36199
36200 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36201 {
36202     splitTip : "Drag to resize.",
36203     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36204     useSplitTips : false,
36205
36206     applyConfig : function(config){
36207         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36208     },
36209     
36210     onRender : function(ctr,pos) {
36211         
36212         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36213         if(!this.config.split){
36214             return;
36215         }
36216         if(!this.split){
36217             
36218             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36219                             tag: "div",
36220                             id: this.el.id + "-split",
36221                             cls: "roo-layout-split roo-layout-split-"+this.position,
36222                             html: "&#160;"
36223             });
36224             /** The SplitBar for this region 
36225             * @type Roo.SplitBar */
36226             // does not exist yet...
36227             Roo.log([this.position, this.orientation]);
36228             
36229             this.split = new Roo.bootstrap.SplitBar({
36230                 dragElement : splitEl,
36231                 resizingElement: this.el,
36232                 orientation : this.orientation
36233             });
36234             
36235             this.split.on("moved", this.onSplitMove, this);
36236             this.split.useShim = this.config.useShim === true;
36237             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36238             if(this.useSplitTips){
36239                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36240             }
36241             //if(config.collapsible){
36242             //    this.split.el.on("dblclick", this.collapse,  this);
36243             //}
36244         }
36245         if(typeof this.config.minSize != "undefined"){
36246             this.split.minSize = this.config.minSize;
36247         }
36248         if(typeof this.config.maxSize != "undefined"){
36249             this.split.maxSize = this.config.maxSize;
36250         }
36251         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36252             this.hideSplitter();
36253         }
36254         
36255     },
36256
36257     getHMaxSize : function(){
36258          var cmax = this.config.maxSize || 10000;
36259          var center = this.mgr.getRegion("center");
36260          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36261     },
36262
36263     getVMaxSize : function(){
36264          var cmax = this.config.maxSize || 10000;
36265          var center = this.mgr.getRegion("center");
36266          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36267     },
36268
36269     onSplitMove : function(split, newSize){
36270         this.fireEvent("resized", this, newSize);
36271     },
36272     
36273     /** 
36274      * Returns the {@link Roo.SplitBar} for this region.
36275      * @return {Roo.SplitBar}
36276      */
36277     getSplitBar : function(){
36278         return this.split;
36279     },
36280     
36281     hide : function(){
36282         this.hideSplitter();
36283         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36284     },
36285
36286     hideSplitter : function(){
36287         if(this.split){
36288             this.split.el.setLocation(-2000,-2000);
36289             this.split.el.hide();
36290         }
36291     },
36292
36293     show : function(){
36294         if(this.split){
36295             this.split.el.show();
36296         }
36297         Roo.bootstrap.layout.Split.superclass.show.call(this);
36298     },
36299     
36300     beforeSlide: function(){
36301         if(Roo.isGecko){// firefox overflow auto bug workaround
36302             this.bodyEl.clip();
36303             if(this.tabs) {
36304                 this.tabs.bodyEl.clip();
36305             }
36306             if(this.activePanel){
36307                 this.activePanel.getEl().clip();
36308                 
36309                 if(this.activePanel.beforeSlide){
36310                     this.activePanel.beforeSlide();
36311                 }
36312             }
36313         }
36314     },
36315     
36316     afterSlide : function(){
36317         if(Roo.isGecko){// firefox overflow auto bug workaround
36318             this.bodyEl.unclip();
36319             if(this.tabs) {
36320                 this.tabs.bodyEl.unclip();
36321             }
36322             if(this.activePanel){
36323                 this.activePanel.getEl().unclip();
36324                 if(this.activePanel.afterSlide){
36325                     this.activePanel.afterSlide();
36326                 }
36327             }
36328         }
36329     },
36330
36331     initAutoHide : function(){
36332         if(this.autoHide !== false){
36333             if(!this.autoHideHd){
36334                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36335                 this.autoHideHd = {
36336                     "mouseout": function(e){
36337                         if(!e.within(this.el, true)){
36338                             st.delay(500);
36339                         }
36340                     },
36341                     "mouseover" : function(e){
36342                         st.cancel();
36343                     },
36344                     scope : this
36345                 };
36346             }
36347             this.el.on(this.autoHideHd);
36348         }
36349     },
36350
36351     clearAutoHide : function(){
36352         if(this.autoHide !== false){
36353             this.el.un("mouseout", this.autoHideHd.mouseout);
36354             this.el.un("mouseover", this.autoHideHd.mouseover);
36355         }
36356     },
36357
36358     clearMonitor : function(){
36359         Roo.get(document).un("click", this.slideInIf, this);
36360     },
36361
36362     // these names are backwards but not changed for compat
36363     slideOut : function(){
36364         if(this.isSlid || this.el.hasActiveFx()){
36365             return;
36366         }
36367         this.isSlid = true;
36368         if(this.collapseBtn){
36369             this.collapseBtn.hide();
36370         }
36371         this.closeBtnState = this.closeBtn.getStyle('display');
36372         this.closeBtn.hide();
36373         if(this.stickBtn){
36374             this.stickBtn.show();
36375         }
36376         this.el.show();
36377         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36378         this.beforeSlide();
36379         this.el.setStyle("z-index", 10001);
36380         this.el.slideIn(this.getSlideAnchor(), {
36381             callback: function(){
36382                 this.afterSlide();
36383                 this.initAutoHide();
36384                 Roo.get(document).on("click", this.slideInIf, this);
36385                 this.fireEvent("slideshow", this);
36386             },
36387             scope: this,
36388             block: true
36389         });
36390     },
36391
36392     afterSlideIn : function(){
36393         this.clearAutoHide();
36394         this.isSlid = false;
36395         this.clearMonitor();
36396         this.el.setStyle("z-index", "");
36397         if(this.collapseBtn){
36398             this.collapseBtn.show();
36399         }
36400         this.closeBtn.setStyle('display', this.closeBtnState);
36401         if(this.stickBtn){
36402             this.stickBtn.hide();
36403         }
36404         this.fireEvent("slidehide", this);
36405     },
36406
36407     slideIn : function(cb){
36408         if(!this.isSlid || this.el.hasActiveFx()){
36409             Roo.callback(cb);
36410             return;
36411         }
36412         this.isSlid = false;
36413         this.beforeSlide();
36414         this.el.slideOut(this.getSlideAnchor(), {
36415             callback: function(){
36416                 this.el.setLeftTop(-10000, -10000);
36417                 this.afterSlide();
36418                 this.afterSlideIn();
36419                 Roo.callback(cb);
36420             },
36421             scope: this,
36422             block: true
36423         });
36424     },
36425     
36426     slideInIf : function(e){
36427         if(!e.within(this.el)){
36428             this.slideIn();
36429         }
36430     },
36431
36432     animateCollapse : function(){
36433         this.beforeSlide();
36434         this.el.setStyle("z-index", 20000);
36435         var anchor = this.getSlideAnchor();
36436         this.el.slideOut(anchor, {
36437             callback : function(){
36438                 this.el.setStyle("z-index", "");
36439                 this.collapsedEl.slideIn(anchor, {duration:.3});
36440                 this.afterSlide();
36441                 this.el.setLocation(-10000,-10000);
36442                 this.el.hide();
36443                 this.fireEvent("collapsed", this);
36444             },
36445             scope: this,
36446             block: true
36447         });
36448     },
36449
36450     animateExpand : function(){
36451         this.beforeSlide();
36452         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36453         this.el.setStyle("z-index", 20000);
36454         this.collapsedEl.hide({
36455             duration:.1
36456         });
36457         this.el.slideIn(this.getSlideAnchor(), {
36458             callback : function(){
36459                 this.el.setStyle("z-index", "");
36460                 this.afterSlide();
36461                 if(this.split){
36462                     this.split.el.show();
36463                 }
36464                 this.fireEvent("invalidated", this);
36465                 this.fireEvent("expanded", this);
36466             },
36467             scope: this,
36468             block: true
36469         });
36470     },
36471
36472     anchors : {
36473         "west" : "left",
36474         "east" : "right",
36475         "north" : "top",
36476         "south" : "bottom"
36477     },
36478
36479     sanchors : {
36480         "west" : "l",
36481         "east" : "r",
36482         "north" : "t",
36483         "south" : "b"
36484     },
36485
36486     canchors : {
36487         "west" : "tl-tr",
36488         "east" : "tr-tl",
36489         "north" : "tl-bl",
36490         "south" : "bl-tl"
36491     },
36492
36493     getAnchor : function(){
36494         return this.anchors[this.position];
36495     },
36496
36497     getCollapseAnchor : function(){
36498         return this.canchors[this.position];
36499     },
36500
36501     getSlideAnchor : function(){
36502         return this.sanchors[this.position];
36503     },
36504
36505     getAlignAdj : function(){
36506         var cm = this.cmargins;
36507         switch(this.position){
36508             case "west":
36509                 return [0, 0];
36510             break;
36511             case "east":
36512                 return [0, 0];
36513             break;
36514             case "north":
36515                 return [0, 0];
36516             break;
36517             case "south":
36518                 return [0, 0];
36519             break;
36520         }
36521     },
36522
36523     getExpandAdj : function(){
36524         var c = this.collapsedEl, cm = this.cmargins;
36525         switch(this.position){
36526             case "west":
36527                 return [-(cm.right+c.getWidth()+cm.left), 0];
36528             break;
36529             case "east":
36530                 return [cm.right+c.getWidth()+cm.left, 0];
36531             break;
36532             case "north":
36533                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36534             break;
36535             case "south":
36536                 return [0, cm.top+cm.bottom+c.getHeight()];
36537             break;
36538         }
36539     }
36540 });/*
36541  * Based on:
36542  * Ext JS Library 1.1.1
36543  * Copyright(c) 2006-2007, Ext JS, LLC.
36544  *
36545  * Originally Released Under LGPL - original licence link has changed is not relivant.
36546  *
36547  * Fork - LGPL
36548  * <script type="text/javascript">
36549  */
36550 /*
36551  * These classes are private internal classes
36552  */
36553 Roo.bootstrap.layout.Center = function(config){
36554     config.region = "center";
36555     Roo.bootstrap.layout.Region.call(this, config);
36556     this.visible = true;
36557     this.minWidth = config.minWidth || 20;
36558     this.minHeight = config.minHeight || 20;
36559 };
36560
36561 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36562     hide : function(){
36563         // center panel can't be hidden
36564     },
36565     
36566     show : function(){
36567         // center panel can't be hidden
36568     },
36569     
36570     getMinWidth: function(){
36571         return this.minWidth;
36572     },
36573     
36574     getMinHeight: function(){
36575         return this.minHeight;
36576     }
36577 });
36578
36579
36580
36581
36582  
36583
36584
36585
36586
36587
36588 Roo.bootstrap.layout.North = function(config)
36589 {
36590     config.region = 'north';
36591     config.cursor = 'n-resize';
36592     
36593     Roo.bootstrap.layout.Split.call(this, config);
36594     
36595     
36596     if(this.split){
36597         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36598         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36599         this.split.el.addClass("roo-layout-split-v");
36600     }
36601     var size = config.initialSize || config.height;
36602     if(typeof size != "undefined"){
36603         this.el.setHeight(size);
36604     }
36605 };
36606 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36607 {
36608     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36609     
36610     
36611     
36612     getBox : function(){
36613         if(this.collapsed){
36614             return this.collapsedEl.getBox();
36615         }
36616         var box = this.el.getBox();
36617         if(this.split){
36618             box.height += this.split.el.getHeight();
36619         }
36620         return box;
36621     },
36622     
36623     updateBox : function(box){
36624         if(this.split && !this.collapsed){
36625             box.height -= this.split.el.getHeight();
36626             this.split.el.setLeft(box.x);
36627             this.split.el.setTop(box.y+box.height);
36628             this.split.el.setWidth(box.width);
36629         }
36630         if(this.collapsed){
36631             this.updateBody(box.width, null);
36632         }
36633         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36634     }
36635 });
36636
36637
36638
36639
36640
36641 Roo.bootstrap.layout.South = function(config){
36642     config.region = 'south';
36643     config.cursor = 's-resize';
36644     Roo.bootstrap.layout.Split.call(this, config);
36645     if(this.split){
36646         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36647         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36648         this.split.el.addClass("roo-layout-split-v");
36649     }
36650     var size = config.initialSize || config.height;
36651     if(typeof size != "undefined"){
36652         this.el.setHeight(size);
36653     }
36654 };
36655
36656 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36657     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36658     getBox : function(){
36659         if(this.collapsed){
36660             return this.collapsedEl.getBox();
36661         }
36662         var box = this.el.getBox();
36663         if(this.split){
36664             var sh = this.split.el.getHeight();
36665             box.height += sh;
36666             box.y -= sh;
36667         }
36668         return box;
36669     },
36670     
36671     updateBox : function(box){
36672         if(this.split && !this.collapsed){
36673             var sh = this.split.el.getHeight();
36674             box.height -= sh;
36675             box.y += sh;
36676             this.split.el.setLeft(box.x);
36677             this.split.el.setTop(box.y-sh);
36678             this.split.el.setWidth(box.width);
36679         }
36680         if(this.collapsed){
36681             this.updateBody(box.width, null);
36682         }
36683         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36684     }
36685 });
36686
36687 Roo.bootstrap.layout.East = function(config){
36688     config.region = "east";
36689     config.cursor = "e-resize";
36690     Roo.bootstrap.layout.Split.call(this, config);
36691     if(this.split){
36692         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36693         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36694         this.split.el.addClass("roo-layout-split-h");
36695     }
36696     var size = config.initialSize || config.width;
36697     if(typeof size != "undefined"){
36698         this.el.setWidth(size);
36699     }
36700 };
36701 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36702     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36703     getBox : function(){
36704         if(this.collapsed){
36705             return this.collapsedEl.getBox();
36706         }
36707         var box = this.el.getBox();
36708         if(this.split){
36709             var sw = this.split.el.getWidth();
36710             box.width += sw;
36711             box.x -= sw;
36712         }
36713         return box;
36714     },
36715
36716     updateBox : function(box){
36717         if(this.split && !this.collapsed){
36718             var sw = this.split.el.getWidth();
36719             box.width -= sw;
36720             this.split.el.setLeft(box.x);
36721             this.split.el.setTop(box.y);
36722             this.split.el.setHeight(box.height);
36723             box.x += sw;
36724         }
36725         if(this.collapsed){
36726             this.updateBody(null, box.height);
36727         }
36728         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36729     }
36730 });
36731
36732 Roo.bootstrap.layout.West = function(config){
36733     config.region = "west";
36734     config.cursor = "w-resize";
36735     
36736     Roo.bootstrap.layout.Split.call(this, config);
36737     if(this.split){
36738         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36739         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36740         this.split.el.addClass("roo-layout-split-h");
36741     }
36742     
36743 };
36744 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36745     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36746     
36747     onRender: function(ctr, pos)
36748     {
36749         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36750         var size = this.config.initialSize || this.config.width;
36751         if(typeof size != "undefined"){
36752             this.el.setWidth(size);
36753         }
36754     },
36755     
36756     getBox : function(){
36757         if(this.collapsed){
36758             return this.collapsedEl.getBox();
36759         }
36760         var box = this.el.getBox();
36761         if(this.split){
36762             box.width += this.split.el.getWidth();
36763         }
36764         return box;
36765     },
36766     
36767     updateBox : function(box){
36768         if(this.split && !this.collapsed){
36769             var sw = this.split.el.getWidth();
36770             box.width -= sw;
36771             this.split.el.setLeft(box.x+box.width);
36772             this.split.el.setTop(box.y);
36773             this.split.el.setHeight(box.height);
36774         }
36775         if(this.collapsed){
36776             this.updateBody(null, box.height);
36777         }
36778         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36779     }
36780 });
36781 Roo.namespace("Roo.bootstrap.panel");/*
36782  * Based on:
36783  * Ext JS Library 1.1.1
36784  * Copyright(c) 2006-2007, Ext JS, LLC.
36785  *
36786  * Originally Released Under LGPL - original licence link has changed is not relivant.
36787  *
36788  * Fork - LGPL
36789  * <script type="text/javascript">
36790  */
36791 /**
36792  * @class Roo.ContentPanel
36793  * @extends Roo.util.Observable
36794  * A basic ContentPanel element.
36795  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36796  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36797  * @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
36798  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36799  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36800  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36801  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36802  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36803  * @cfg {String} title          The title for this panel
36804  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36805  * @cfg {String} url            Calls {@link #setUrl} with this value
36806  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36807  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36808  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36809  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36810  * @cfg {Boolean} badges render the badges
36811
36812  * @constructor
36813  * Create a new ContentPanel.
36814  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36815  * @param {String/Object} config A string to set only the title or a config object
36816  * @param {String} content (optional) Set the HTML content for this panel
36817  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36818  */
36819 Roo.bootstrap.panel.Content = function( config){
36820     
36821     this.tpl = config.tpl || false;
36822     
36823     var el = config.el;
36824     var content = config.content;
36825
36826     if(config.autoCreate){ // xtype is available if this is called from factory
36827         el = Roo.id();
36828     }
36829     this.el = Roo.get(el);
36830     if(!this.el && config && config.autoCreate){
36831         if(typeof config.autoCreate == "object"){
36832             if(!config.autoCreate.id){
36833                 config.autoCreate.id = config.id||el;
36834             }
36835             this.el = Roo.DomHelper.append(document.body,
36836                         config.autoCreate, true);
36837         }else{
36838             var elcfg =  {   tag: "div",
36839                             cls: "roo-layout-inactive-content",
36840                             id: config.id||el
36841                             };
36842             if (config.html) {
36843                 elcfg.html = config.html;
36844                 
36845             }
36846                         
36847             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36848         }
36849     } 
36850     this.closable = false;
36851     this.loaded = false;
36852     this.active = false;
36853    
36854       
36855     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36856         
36857         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36858         
36859         this.wrapEl = this.el; //this.el.wrap();
36860         var ti = [];
36861         if (config.toolbar.items) {
36862             ti = config.toolbar.items ;
36863             delete config.toolbar.items ;
36864         }
36865         
36866         var nitems = [];
36867         this.toolbar.render(this.wrapEl, 'before');
36868         for(var i =0;i < ti.length;i++) {
36869           //  Roo.log(['add child', items[i]]);
36870             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36871         }
36872         this.toolbar.items = nitems;
36873         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36874         delete config.toolbar;
36875         
36876     }
36877     /*
36878     // xtype created footer. - not sure if will work as we normally have to render first..
36879     if (this.footer && !this.footer.el && this.footer.xtype) {
36880         if (!this.wrapEl) {
36881             this.wrapEl = this.el.wrap();
36882         }
36883     
36884         this.footer.container = this.wrapEl.createChild();
36885          
36886         this.footer = Roo.factory(this.footer, Roo);
36887         
36888     }
36889     */
36890     
36891      if(typeof config == "string"){
36892         this.title = config;
36893     }else{
36894         Roo.apply(this, config);
36895     }
36896     
36897     if(this.resizeEl){
36898         this.resizeEl = Roo.get(this.resizeEl, true);
36899     }else{
36900         this.resizeEl = this.el;
36901     }
36902     // handle view.xtype
36903     
36904  
36905     
36906     
36907     this.addEvents({
36908         /**
36909          * @event activate
36910          * Fires when this panel is activated. 
36911          * @param {Roo.ContentPanel} this
36912          */
36913         "activate" : true,
36914         /**
36915          * @event deactivate
36916          * Fires when this panel is activated. 
36917          * @param {Roo.ContentPanel} this
36918          */
36919         "deactivate" : true,
36920
36921         /**
36922          * @event resize
36923          * Fires when this panel is resized if fitToFrame is true.
36924          * @param {Roo.ContentPanel} this
36925          * @param {Number} width The width after any component adjustments
36926          * @param {Number} height The height after any component adjustments
36927          */
36928         "resize" : true,
36929         
36930          /**
36931          * @event render
36932          * Fires when this tab is created
36933          * @param {Roo.ContentPanel} this
36934          */
36935         "render" : true
36936         
36937         
36938         
36939     });
36940     
36941
36942     
36943     
36944     if(this.autoScroll){
36945         this.resizeEl.setStyle("overflow", "auto");
36946     } else {
36947         // fix randome scrolling
36948         //this.el.on('scroll', function() {
36949         //    Roo.log('fix random scolling');
36950         //    this.scrollTo('top',0); 
36951         //});
36952     }
36953     content = content || this.content;
36954     if(content){
36955         this.setContent(content);
36956     }
36957     if(config && config.url){
36958         this.setUrl(this.url, this.params, this.loadOnce);
36959     }
36960     
36961     
36962     
36963     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36964     
36965     if (this.view && typeof(this.view.xtype) != 'undefined') {
36966         this.view.el = this.el.appendChild(document.createElement("div"));
36967         this.view = Roo.factory(this.view); 
36968         this.view.render  &&  this.view.render(false, '');  
36969     }
36970     
36971     
36972     this.fireEvent('render', this);
36973 };
36974
36975 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36976     
36977     tabTip : '',
36978     
36979     setRegion : function(region){
36980         this.region = region;
36981         this.setActiveClass(region && !this.background);
36982     },
36983     
36984     
36985     setActiveClass: function(state)
36986     {
36987         if(state){
36988            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36989            this.el.setStyle('position','relative');
36990         }else{
36991            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36992            this.el.setStyle('position', 'absolute');
36993         } 
36994     },
36995     
36996     /**
36997      * Returns the toolbar for this Panel if one was configured. 
36998      * @return {Roo.Toolbar} 
36999      */
37000     getToolbar : function(){
37001         return this.toolbar;
37002     },
37003     
37004     setActiveState : function(active)
37005     {
37006         this.active = active;
37007         this.setActiveClass(active);
37008         if(!active){
37009             if(this.fireEvent("deactivate", this) === false){
37010                 return false;
37011             }
37012             return true;
37013         }
37014         this.fireEvent("activate", this);
37015         return true;
37016     },
37017     /**
37018      * Updates this panel's element
37019      * @param {String} content The new content
37020      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37021     */
37022     setContent : function(content, loadScripts){
37023         this.el.update(content, loadScripts);
37024     },
37025
37026     ignoreResize : function(w, h){
37027         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37028             return true;
37029         }else{
37030             this.lastSize = {width: w, height: h};
37031             return false;
37032         }
37033     },
37034     /**
37035      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37036      * @return {Roo.UpdateManager} The UpdateManager
37037      */
37038     getUpdateManager : function(){
37039         return this.el.getUpdateManager();
37040     },
37041      /**
37042      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37043      * @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:
37044 <pre><code>
37045 panel.load({
37046     url: "your-url.php",
37047     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37048     callback: yourFunction,
37049     scope: yourObject, //(optional scope)
37050     discardUrl: false,
37051     nocache: false,
37052     text: "Loading...",
37053     timeout: 30,
37054     scripts: false
37055 });
37056 </code></pre>
37057      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37058      * 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.
37059      * @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}
37060      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37061      * @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.
37062      * @return {Roo.ContentPanel} this
37063      */
37064     load : function(){
37065         var um = this.el.getUpdateManager();
37066         um.update.apply(um, arguments);
37067         return this;
37068     },
37069
37070
37071     /**
37072      * 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.
37073      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37074      * @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)
37075      * @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)
37076      * @return {Roo.UpdateManager} The UpdateManager
37077      */
37078     setUrl : function(url, params, loadOnce){
37079         if(this.refreshDelegate){
37080             this.removeListener("activate", this.refreshDelegate);
37081         }
37082         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37083         this.on("activate", this.refreshDelegate);
37084         return this.el.getUpdateManager();
37085     },
37086     
37087     _handleRefresh : function(url, params, loadOnce){
37088         if(!loadOnce || !this.loaded){
37089             var updater = this.el.getUpdateManager();
37090             updater.update(url, params, this._setLoaded.createDelegate(this));
37091         }
37092     },
37093     
37094     _setLoaded : function(){
37095         this.loaded = true;
37096     }, 
37097     
37098     /**
37099      * Returns this panel's id
37100      * @return {String} 
37101      */
37102     getId : function(){
37103         return this.el.id;
37104     },
37105     
37106     /** 
37107      * Returns this panel's element - used by regiosn to add.
37108      * @return {Roo.Element} 
37109      */
37110     getEl : function(){
37111         return this.wrapEl || this.el;
37112     },
37113     
37114    
37115     
37116     adjustForComponents : function(width, height)
37117     {
37118         //Roo.log('adjustForComponents ');
37119         if(this.resizeEl != this.el){
37120             width -= this.el.getFrameWidth('lr');
37121             height -= this.el.getFrameWidth('tb');
37122         }
37123         if(this.toolbar){
37124             var te = this.toolbar.getEl();
37125             te.setWidth(width);
37126             height -= te.getHeight();
37127         }
37128         if(this.footer){
37129             var te = this.footer.getEl();
37130             te.setWidth(width);
37131             height -= te.getHeight();
37132         }
37133         
37134         
37135         if(this.adjustments){
37136             width += this.adjustments[0];
37137             height += this.adjustments[1];
37138         }
37139         return {"width": width, "height": height};
37140     },
37141     
37142     setSize : function(width, height){
37143         if(this.fitToFrame && !this.ignoreResize(width, height)){
37144             if(this.fitContainer && this.resizeEl != this.el){
37145                 this.el.setSize(width, height);
37146             }
37147             var size = this.adjustForComponents(width, height);
37148             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37149             this.fireEvent('resize', this, size.width, size.height);
37150         }
37151     },
37152     
37153     /**
37154      * Returns this panel's title
37155      * @return {String} 
37156      */
37157     getTitle : function(){
37158         
37159         if (typeof(this.title) != 'object') {
37160             return this.title;
37161         }
37162         
37163         var t = '';
37164         for (var k in this.title) {
37165             if (!this.title.hasOwnProperty(k)) {
37166                 continue;
37167             }
37168             
37169             if (k.indexOf('-') >= 0) {
37170                 var s = k.split('-');
37171                 for (var i = 0; i<s.length; i++) {
37172                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37173                 }
37174             } else {
37175                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37176             }
37177         }
37178         return t;
37179     },
37180     
37181     /**
37182      * Set this panel's title
37183      * @param {String} title
37184      */
37185     setTitle : function(title){
37186         this.title = title;
37187         if(this.region){
37188             this.region.updatePanelTitle(this, title);
37189         }
37190     },
37191     
37192     /**
37193      * Returns true is this panel was configured to be closable
37194      * @return {Boolean} 
37195      */
37196     isClosable : function(){
37197         return this.closable;
37198     },
37199     
37200     beforeSlide : function(){
37201         this.el.clip();
37202         this.resizeEl.clip();
37203     },
37204     
37205     afterSlide : function(){
37206         this.el.unclip();
37207         this.resizeEl.unclip();
37208     },
37209     
37210     /**
37211      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37212      *   Will fail silently if the {@link #setUrl} method has not been called.
37213      *   This does not activate the panel, just updates its content.
37214      */
37215     refresh : function(){
37216         if(this.refreshDelegate){
37217            this.loaded = false;
37218            this.refreshDelegate();
37219         }
37220     },
37221     
37222     /**
37223      * Destroys this panel
37224      */
37225     destroy : function(){
37226         this.el.removeAllListeners();
37227         var tempEl = document.createElement("span");
37228         tempEl.appendChild(this.el.dom);
37229         tempEl.innerHTML = "";
37230         this.el.remove();
37231         this.el = null;
37232     },
37233     
37234     /**
37235      * form - if the content panel contains a form - this is a reference to it.
37236      * @type {Roo.form.Form}
37237      */
37238     form : false,
37239     /**
37240      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37241      *    This contains a reference to it.
37242      * @type {Roo.View}
37243      */
37244     view : false,
37245     
37246       /**
37247      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37248      * <pre><code>
37249
37250 layout.addxtype({
37251        xtype : 'Form',
37252        items: [ .... ]
37253    }
37254 );
37255
37256 </code></pre>
37257      * @param {Object} cfg Xtype definition of item to add.
37258      */
37259     
37260     
37261     getChildContainer: function () {
37262         return this.getEl();
37263     }
37264     
37265     
37266     /*
37267         var  ret = new Roo.factory(cfg);
37268         return ret;
37269         
37270         
37271         // add form..
37272         if (cfg.xtype.match(/^Form$/)) {
37273             
37274             var el;
37275             //if (this.footer) {
37276             //    el = this.footer.container.insertSibling(false, 'before');
37277             //} else {
37278                 el = this.el.createChild();
37279             //}
37280
37281             this.form = new  Roo.form.Form(cfg);
37282             
37283             
37284             if ( this.form.allItems.length) {
37285                 this.form.render(el.dom);
37286             }
37287             return this.form;
37288         }
37289         // should only have one of theses..
37290         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37291             // views.. should not be just added - used named prop 'view''
37292             
37293             cfg.el = this.el.appendChild(document.createElement("div"));
37294             // factory?
37295             
37296             var ret = new Roo.factory(cfg);
37297              
37298              ret.render && ret.render(false, ''); // render blank..
37299             this.view = ret;
37300             return ret;
37301         }
37302         return false;
37303     }
37304     \*/
37305 });
37306  
37307 /**
37308  * @class Roo.bootstrap.panel.Grid
37309  * @extends Roo.bootstrap.panel.Content
37310  * @constructor
37311  * Create a new GridPanel.
37312  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37313  * @param {Object} config A the config object
37314   
37315  */
37316
37317
37318
37319 Roo.bootstrap.panel.Grid = function(config)
37320 {
37321     
37322       
37323     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37324         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37325
37326     config.el = this.wrapper;
37327     //this.el = this.wrapper;
37328     
37329       if (config.container) {
37330         // ctor'ed from a Border/panel.grid
37331         
37332         
37333         this.wrapper.setStyle("overflow", "hidden");
37334         this.wrapper.addClass('roo-grid-container');
37335
37336     }
37337     
37338     
37339     if(config.toolbar){
37340         var tool_el = this.wrapper.createChild();    
37341         this.toolbar = Roo.factory(config.toolbar);
37342         var ti = [];
37343         if (config.toolbar.items) {
37344             ti = config.toolbar.items ;
37345             delete config.toolbar.items ;
37346         }
37347         
37348         var nitems = [];
37349         this.toolbar.render(tool_el);
37350         for(var i =0;i < ti.length;i++) {
37351           //  Roo.log(['add child', items[i]]);
37352             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37353         }
37354         this.toolbar.items = nitems;
37355         
37356         delete config.toolbar;
37357     }
37358     
37359     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37360     config.grid.scrollBody = true;;
37361     config.grid.monitorWindowResize = false; // turn off autosizing
37362     config.grid.autoHeight = false;
37363     config.grid.autoWidth = false;
37364     
37365     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37366     
37367     if (config.background) {
37368         // render grid on panel activation (if panel background)
37369         this.on('activate', function(gp) {
37370             if (!gp.grid.rendered) {
37371                 gp.grid.render(this.wrapper);
37372                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37373             }
37374         });
37375             
37376     } else {
37377         this.grid.render(this.wrapper);
37378         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37379
37380     }
37381     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37382     // ??? needed ??? config.el = this.wrapper;
37383     
37384     
37385     
37386   
37387     // xtype created footer. - not sure if will work as we normally have to render first..
37388     if (this.footer && !this.footer.el && this.footer.xtype) {
37389         
37390         var ctr = this.grid.getView().getFooterPanel(true);
37391         this.footer.dataSource = this.grid.dataSource;
37392         this.footer = Roo.factory(this.footer, Roo);
37393         this.footer.render(ctr);
37394         
37395     }
37396     
37397     
37398     
37399     
37400      
37401 };
37402
37403 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37404     getId : function(){
37405         return this.grid.id;
37406     },
37407     
37408     /**
37409      * Returns the grid for this panel
37410      * @return {Roo.bootstrap.Table} 
37411      */
37412     getGrid : function(){
37413         return this.grid;    
37414     },
37415     
37416     setSize : function(width, height){
37417         if(!this.ignoreResize(width, height)){
37418             var grid = this.grid;
37419             var size = this.adjustForComponents(width, height);
37420             var gridel = grid.getGridEl();
37421             gridel.setSize(size.width, size.height);
37422             /*
37423             var thd = grid.getGridEl().select('thead',true).first();
37424             var tbd = grid.getGridEl().select('tbody', true).first();
37425             if (tbd) {
37426                 tbd.setSize(width, height - thd.getHeight());
37427             }
37428             */
37429             grid.autoSize();
37430         }
37431     },
37432      
37433     
37434     
37435     beforeSlide : function(){
37436         this.grid.getView().scroller.clip();
37437     },
37438     
37439     afterSlide : function(){
37440         this.grid.getView().scroller.unclip();
37441     },
37442     
37443     destroy : function(){
37444         this.grid.destroy();
37445         delete this.grid;
37446         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37447     }
37448 });
37449
37450 /**
37451  * @class Roo.bootstrap.panel.Nest
37452  * @extends Roo.bootstrap.panel.Content
37453  * @constructor
37454  * Create a new Panel, that can contain a layout.Border.
37455  * 
37456  * 
37457  * @param {Roo.BorderLayout} layout The layout for this panel
37458  * @param {String/Object} config A string to set only the title or a config object
37459  */
37460 Roo.bootstrap.panel.Nest = function(config)
37461 {
37462     // construct with only one argument..
37463     /* FIXME - implement nicer consturctors
37464     if (layout.layout) {
37465         config = layout;
37466         layout = config.layout;
37467         delete config.layout;
37468     }
37469     if (layout.xtype && !layout.getEl) {
37470         // then layout needs constructing..
37471         layout = Roo.factory(layout, Roo);
37472     }
37473     */
37474     
37475     config.el =  config.layout.getEl();
37476     
37477     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37478     
37479     config.layout.monitorWindowResize = false; // turn off autosizing
37480     this.layout = config.layout;
37481     this.layout.getEl().addClass("roo-layout-nested-layout");
37482     
37483     
37484     
37485     
37486 };
37487
37488 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37489
37490     setSize : function(width, height){
37491         if(!this.ignoreResize(width, height)){
37492             var size = this.adjustForComponents(width, height);
37493             var el = this.layout.getEl();
37494             if (size.height < 1) {
37495                 el.setWidth(size.width);   
37496             } else {
37497                 el.setSize(size.width, size.height);
37498             }
37499             var touch = el.dom.offsetWidth;
37500             this.layout.layout();
37501             // ie requires a double layout on the first pass
37502             if(Roo.isIE && !this.initialized){
37503                 this.initialized = true;
37504                 this.layout.layout();
37505             }
37506         }
37507     },
37508     
37509     // activate all subpanels if not currently active..
37510     
37511     setActiveState : function(active){
37512         this.active = active;
37513         this.setActiveClass(active);
37514         
37515         if(!active){
37516             this.fireEvent("deactivate", this);
37517             return;
37518         }
37519         
37520         this.fireEvent("activate", this);
37521         // not sure if this should happen before or after..
37522         if (!this.layout) {
37523             return; // should not happen..
37524         }
37525         var reg = false;
37526         for (var r in this.layout.regions) {
37527             reg = this.layout.getRegion(r);
37528             if (reg.getActivePanel()) {
37529                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37530                 reg.setActivePanel(reg.getActivePanel());
37531                 continue;
37532             }
37533             if (!reg.panels.length) {
37534                 continue;
37535             }
37536             reg.showPanel(reg.getPanel(0));
37537         }
37538         
37539         
37540         
37541         
37542     },
37543     
37544     /**
37545      * Returns the nested BorderLayout for this panel
37546      * @return {Roo.BorderLayout} 
37547      */
37548     getLayout : function(){
37549         return this.layout;
37550     },
37551     
37552      /**
37553      * Adds a xtype elements to the layout of the nested panel
37554      * <pre><code>
37555
37556 panel.addxtype({
37557        xtype : 'ContentPanel',
37558        region: 'west',
37559        items: [ .... ]
37560    }
37561 );
37562
37563 panel.addxtype({
37564         xtype : 'NestedLayoutPanel',
37565         region: 'west',
37566         layout: {
37567            center: { },
37568            west: { }   
37569         },
37570         items : [ ... list of content panels or nested layout panels.. ]
37571    }
37572 );
37573 </code></pre>
37574      * @param {Object} cfg Xtype definition of item to add.
37575      */
37576     addxtype : function(cfg) {
37577         return this.layout.addxtype(cfg);
37578     
37579     }
37580 });        /*
37581  * Based on:
37582  * Ext JS Library 1.1.1
37583  * Copyright(c) 2006-2007, Ext JS, LLC.
37584  *
37585  * Originally Released Under LGPL - original licence link has changed is not relivant.
37586  *
37587  * Fork - LGPL
37588  * <script type="text/javascript">
37589  */
37590 /**
37591  * @class Roo.TabPanel
37592  * @extends Roo.util.Observable
37593  * A lightweight tab container.
37594  * <br><br>
37595  * Usage:
37596  * <pre><code>
37597 // basic tabs 1, built from existing content
37598 var tabs = new Roo.TabPanel("tabs1");
37599 tabs.addTab("script", "View Script");
37600 tabs.addTab("markup", "View Markup");
37601 tabs.activate("script");
37602
37603 // more advanced tabs, built from javascript
37604 var jtabs = new Roo.TabPanel("jtabs");
37605 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37606
37607 // set up the UpdateManager
37608 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37609 var updater = tab2.getUpdateManager();
37610 updater.setDefaultUrl("ajax1.htm");
37611 tab2.on('activate', updater.refresh, updater, true);
37612
37613 // Use setUrl for Ajax loading
37614 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37615 tab3.setUrl("ajax2.htm", null, true);
37616
37617 // Disabled tab
37618 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37619 tab4.disable();
37620
37621 jtabs.activate("jtabs-1");
37622  * </code></pre>
37623  * @constructor
37624  * Create a new TabPanel.
37625  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37626  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37627  */
37628 Roo.bootstrap.panel.Tabs = function(config){
37629     /**
37630     * The container element for this TabPanel.
37631     * @type Roo.Element
37632     */
37633     this.el = Roo.get(config.el);
37634     delete config.el;
37635     if(config){
37636         if(typeof config == "boolean"){
37637             this.tabPosition = config ? "bottom" : "top";
37638         }else{
37639             Roo.apply(this, config);
37640         }
37641     }
37642     
37643     if(this.tabPosition == "bottom"){
37644         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37645         this.el.addClass("roo-tabs-bottom");
37646     }
37647     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37648     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37649     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37650     if(Roo.isIE){
37651         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37652     }
37653     if(this.tabPosition != "bottom"){
37654         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37655          * @type Roo.Element
37656          */
37657         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37658         this.el.addClass("roo-tabs-top");
37659     }
37660     this.items = [];
37661
37662     this.bodyEl.setStyle("position", "relative");
37663
37664     this.active = null;
37665     this.activateDelegate = this.activate.createDelegate(this);
37666
37667     this.addEvents({
37668         /**
37669          * @event tabchange
37670          * Fires when the active tab changes
37671          * @param {Roo.TabPanel} this
37672          * @param {Roo.TabPanelItem} activePanel The new active tab
37673          */
37674         "tabchange": true,
37675         /**
37676          * @event beforetabchange
37677          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37678          * @param {Roo.TabPanel} this
37679          * @param {Object} e Set cancel to true on this object to cancel the tab change
37680          * @param {Roo.TabPanelItem} tab The tab being changed to
37681          */
37682         "beforetabchange" : true
37683     });
37684
37685     Roo.EventManager.onWindowResize(this.onResize, this);
37686     this.cpad = this.el.getPadding("lr");
37687     this.hiddenCount = 0;
37688
37689
37690     // toolbar on the tabbar support...
37691     if (this.toolbar) {
37692         alert("no toolbar support yet");
37693         this.toolbar  = false;
37694         /*
37695         var tcfg = this.toolbar;
37696         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37697         this.toolbar = new Roo.Toolbar(tcfg);
37698         if (Roo.isSafari) {
37699             var tbl = tcfg.container.child('table', true);
37700             tbl.setAttribute('width', '100%');
37701         }
37702         */
37703         
37704     }
37705    
37706
37707
37708     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37709 };
37710
37711 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37712     /*
37713      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37714      */
37715     tabPosition : "top",
37716     /*
37717      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37718      */
37719     currentTabWidth : 0,
37720     /*
37721      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37722      */
37723     minTabWidth : 40,
37724     /*
37725      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37726      */
37727     maxTabWidth : 250,
37728     /*
37729      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37730      */
37731     preferredTabWidth : 175,
37732     /*
37733      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37734      */
37735     resizeTabs : false,
37736     /*
37737      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37738      */
37739     monitorResize : true,
37740     /*
37741      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37742      */
37743     toolbar : false,
37744
37745     /**
37746      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37747      * @param {String} id The id of the div to use <b>or create</b>
37748      * @param {String} text The text for the tab
37749      * @param {String} content (optional) Content to put in the TabPanelItem body
37750      * @param {Boolean} closable (optional) True to create a close icon on the tab
37751      * @return {Roo.TabPanelItem} The created TabPanelItem
37752      */
37753     addTab : function(id, text, content, closable, tpl)
37754     {
37755         var item = new Roo.bootstrap.panel.TabItem({
37756             panel: this,
37757             id : id,
37758             text : text,
37759             closable : closable,
37760             tpl : tpl
37761         });
37762         this.addTabItem(item);
37763         if(content){
37764             item.setContent(content);
37765         }
37766         return item;
37767     },
37768
37769     /**
37770      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37771      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37772      * @return {Roo.TabPanelItem}
37773      */
37774     getTab : function(id){
37775         return this.items[id];
37776     },
37777
37778     /**
37779      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37780      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37781      */
37782     hideTab : function(id){
37783         var t = this.items[id];
37784         if(!t.isHidden()){
37785            t.setHidden(true);
37786            this.hiddenCount++;
37787            this.autoSizeTabs();
37788         }
37789     },
37790
37791     /**
37792      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37793      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37794      */
37795     unhideTab : function(id){
37796         var t = this.items[id];
37797         if(t.isHidden()){
37798            t.setHidden(false);
37799            this.hiddenCount--;
37800            this.autoSizeTabs();
37801         }
37802     },
37803
37804     /**
37805      * Adds an existing {@link Roo.TabPanelItem}.
37806      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37807      */
37808     addTabItem : function(item){
37809         this.items[item.id] = item;
37810         this.items.push(item);
37811       //  if(this.resizeTabs){
37812     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37813   //         this.autoSizeTabs();
37814 //        }else{
37815 //            item.autoSize();
37816        // }
37817     },
37818
37819     /**
37820      * Removes a {@link Roo.TabPanelItem}.
37821      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37822      */
37823     removeTab : function(id){
37824         var items = this.items;
37825         var tab = items[id];
37826         if(!tab) { return; }
37827         var index = items.indexOf(tab);
37828         if(this.active == tab && items.length > 1){
37829             var newTab = this.getNextAvailable(index);
37830             if(newTab) {
37831                 newTab.activate();
37832             }
37833         }
37834         this.stripEl.dom.removeChild(tab.pnode.dom);
37835         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37836             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37837         }
37838         items.splice(index, 1);
37839         delete this.items[tab.id];
37840         tab.fireEvent("close", tab);
37841         tab.purgeListeners();
37842         this.autoSizeTabs();
37843     },
37844
37845     getNextAvailable : function(start){
37846         var items = this.items;
37847         var index = start;
37848         // look for a next tab that will slide over to
37849         // replace the one being removed
37850         while(index < items.length){
37851             var item = items[++index];
37852             if(item && !item.isHidden()){
37853                 return item;
37854             }
37855         }
37856         // if one isn't found select the previous tab (on the left)
37857         index = start;
37858         while(index >= 0){
37859             var item = items[--index];
37860             if(item && !item.isHidden()){
37861                 return item;
37862             }
37863         }
37864         return null;
37865     },
37866
37867     /**
37868      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37869      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37870      */
37871     disableTab : function(id){
37872         var tab = this.items[id];
37873         if(tab && this.active != tab){
37874             tab.disable();
37875         }
37876     },
37877
37878     /**
37879      * Enables a {@link Roo.TabPanelItem} that is disabled.
37880      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37881      */
37882     enableTab : function(id){
37883         var tab = this.items[id];
37884         tab.enable();
37885     },
37886
37887     /**
37888      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37889      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37890      * @return {Roo.TabPanelItem} The TabPanelItem.
37891      */
37892     activate : function(id){
37893         var tab = this.items[id];
37894         if(!tab){
37895             return null;
37896         }
37897         if(tab == this.active || tab.disabled){
37898             return tab;
37899         }
37900         var e = {};
37901         this.fireEvent("beforetabchange", this, e, tab);
37902         if(e.cancel !== true && !tab.disabled){
37903             if(this.active){
37904                 this.active.hide();
37905             }
37906             this.active = this.items[id];
37907             this.active.show();
37908             this.fireEvent("tabchange", this, this.active);
37909         }
37910         return tab;
37911     },
37912
37913     /**
37914      * Gets the active {@link Roo.TabPanelItem}.
37915      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37916      */
37917     getActiveTab : function(){
37918         return this.active;
37919     },
37920
37921     /**
37922      * Updates the tab body element to fit the height of the container element
37923      * for overflow scrolling
37924      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37925      */
37926     syncHeight : function(targetHeight){
37927         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37928         var bm = this.bodyEl.getMargins();
37929         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37930         this.bodyEl.setHeight(newHeight);
37931         return newHeight;
37932     },
37933
37934     onResize : function(){
37935         if(this.monitorResize){
37936             this.autoSizeTabs();
37937         }
37938     },
37939
37940     /**
37941      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37942      */
37943     beginUpdate : function(){
37944         this.updating = true;
37945     },
37946
37947     /**
37948      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37949      */
37950     endUpdate : function(){
37951         this.updating = false;
37952         this.autoSizeTabs();
37953     },
37954
37955     /**
37956      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37957      */
37958     autoSizeTabs : function(){
37959         var count = this.items.length;
37960         var vcount = count - this.hiddenCount;
37961         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37962             return;
37963         }
37964         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37965         var availWidth = Math.floor(w / vcount);
37966         var b = this.stripBody;
37967         if(b.getWidth() > w){
37968             var tabs = this.items;
37969             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37970             if(availWidth < this.minTabWidth){
37971                 /*if(!this.sleft){    // incomplete scrolling code
37972                     this.createScrollButtons();
37973                 }
37974                 this.showScroll();
37975                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37976             }
37977         }else{
37978             if(this.currentTabWidth < this.preferredTabWidth){
37979                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37980             }
37981         }
37982     },
37983
37984     /**
37985      * Returns the number of tabs in this TabPanel.
37986      * @return {Number}
37987      */
37988      getCount : function(){
37989          return this.items.length;
37990      },
37991
37992     /**
37993      * Resizes all the tabs to the passed width
37994      * @param {Number} The new width
37995      */
37996     setTabWidth : function(width){
37997         this.currentTabWidth = width;
37998         for(var i = 0, len = this.items.length; i < len; i++) {
37999                 if(!this.items[i].isHidden()) {
38000                 this.items[i].setWidth(width);
38001             }
38002         }
38003     },
38004
38005     /**
38006      * Destroys this TabPanel
38007      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38008      */
38009     destroy : function(removeEl){
38010         Roo.EventManager.removeResizeListener(this.onResize, this);
38011         for(var i = 0, len = this.items.length; i < len; i++){
38012             this.items[i].purgeListeners();
38013         }
38014         if(removeEl === true){
38015             this.el.update("");
38016             this.el.remove();
38017         }
38018     },
38019     
38020     createStrip : function(container)
38021     {
38022         var strip = document.createElement("nav");
38023         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38024         container.appendChild(strip);
38025         return strip;
38026     },
38027     
38028     createStripList : function(strip)
38029     {
38030         // div wrapper for retard IE
38031         // returns the "tr" element.
38032         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38033         //'<div class="x-tabs-strip-wrap">'+
38034           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38035           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38036         return strip.firstChild; //.firstChild.firstChild.firstChild;
38037     },
38038     createBody : function(container)
38039     {
38040         var body = document.createElement("div");
38041         Roo.id(body, "tab-body");
38042         //Roo.fly(body).addClass("x-tabs-body");
38043         Roo.fly(body).addClass("tab-content");
38044         container.appendChild(body);
38045         return body;
38046     },
38047     createItemBody :function(bodyEl, id){
38048         var body = Roo.getDom(id);
38049         if(!body){
38050             body = document.createElement("div");
38051             body.id = id;
38052         }
38053         //Roo.fly(body).addClass("x-tabs-item-body");
38054         Roo.fly(body).addClass("tab-pane");
38055          bodyEl.insertBefore(body, bodyEl.firstChild);
38056         return body;
38057     },
38058     /** @private */
38059     createStripElements :  function(stripEl, text, closable, tpl)
38060     {
38061         var td = document.createElement("li"); // was td..
38062         
38063         
38064         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38065         
38066         
38067         stripEl.appendChild(td);
38068         /*if(closable){
38069             td.className = "x-tabs-closable";
38070             if(!this.closeTpl){
38071                 this.closeTpl = new Roo.Template(
38072                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38073                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38074                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38075                 );
38076             }
38077             var el = this.closeTpl.overwrite(td, {"text": text});
38078             var close = el.getElementsByTagName("div")[0];
38079             var inner = el.getElementsByTagName("em")[0];
38080             return {"el": el, "close": close, "inner": inner};
38081         } else {
38082         */
38083         // not sure what this is..
38084 //            if(!this.tabTpl){
38085                 //this.tabTpl = new Roo.Template(
38086                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38087                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38088                 //);
38089 //                this.tabTpl = new Roo.Template(
38090 //                   '<a href="#">' +
38091 //                   '<span unselectable="on"' +
38092 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38093 //                            ' >{text}</span></a>'
38094 //                );
38095 //                
38096 //            }
38097
38098
38099             var template = tpl || this.tabTpl || false;
38100             
38101             if(!template){
38102                 
38103                 template = new Roo.Template(
38104                    '<a href="#">' +
38105                    '<span unselectable="on"' +
38106                             (this.disableTooltips ? '' : ' title="{text}"') +
38107                             ' >{text}</span></a>'
38108                 );
38109             }
38110             
38111             switch (typeof(template)) {
38112                 case 'object' :
38113                     break;
38114                 case 'string' :
38115                     template = new Roo.Template(template);
38116                     break;
38117                 default :
38118                     break;
38119             }
38120             
38121             var el = template.overwrite(td, {"text": text});
38122             
38123             var inner = el.getElementsByTagName("span")[0];
38124             
38125             return {"el": el, "inner": inner};
38126             
38127     }
38128         
38129     
38130 });
38131
38132 /**
38133  * @class Roo.TabPanelItem
38134  * @extends Roo.util.Observable
38135  * Represents an individual item (tab plus body) in a TabPanel.
38136  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38137  * @param {String} id The id of this TabPanelItem
38138  * @param {String} text The text for the tab of this TabPanelItem
38139  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38140  */
38141 Roo.bootstrap.panel.TabItem = function(config){
38142     /**
38143      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38144      * @type Roo.TabPanel
38145      */
38146     this.tabPanel = config.panel;
38147     /**
38148      * The id for this TabPanelItem
38149      * @type String
38150      */
38151     this.id = config.id;
38152     /** @private */
38153     this.disabled = false;
38154     /** @private */
38155     this.text = config.text;
38156     /** @private */
38157     this.loaded = false;
38158     this.closable = config.closable;
38159
38160     /**
38161      * The body element for this TabPanelItem.
38162      * @type Roo.Element
38163      */
38164     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38165     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38166     this.bodyEl.setStyle("display", "block");
38167     this.bodyEl.setStyle("zoom", "1");
38168     //this.hideAction();
38169
38170     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38171     /** @private */
38172     this.el = Roo.get(els.el);
38173     this.inner = Roo.get(els.inner, true);
38174     this.textEl = Roo.get(this.el.dom.firstChild, true);
38175     this.pnode = Roo.get(els.el.parentNode, true);
38176 //    this.el.on("mousedown", this.onTabMouseDown, this);
38177     this.el.on("click", this.onTabClick, this);
38178     /** @private */
38179     if(config.closable){
38180         var c = Roo.get(els.close, true);
38181         c.dom.title = this.closeText;
38182         c.addClassOnOver("close-over");
38183         c.on("click", this.closeClick, this);
38184      }
38185
38186     this.addEvents({
38187          /**
38188          * @event activate
38189          * Fires when this tab becomes the active tab.
38190          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38191          * @param {Roo.TabPanelItem} this
38192          */
38193         "activate": true,
38194         /**
38195          * @event beforeclose
38196          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38197          * @param {Roo.TabPanelItem} this
38198          * @param {Object} e Set cancel to true on this object to cancel the close.
38199          */
38200         "beforeclose": true,
38201         /**
38202          * @event close
38203          * Fires when this tab is closed.
38204          * @param {Roo.TabPanelItem} this
38205          */
38206          "close": true,
38207         /**
38208          * @event deactivate
38209          * Fires when this tab is no longer the active tab.
38210          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38211          * @param {Roo.TabPanelItem} this
38212          */
38213          "deactivate" : true
38214     });
38215     this.hidden = false;
38216
38217     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38218 };
38219
38220 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38221            {
38222     purgeListeners : function(){
38223        Roo.util.Observable.prototype.purgeListeners.call(this);
38224        this.el.removeAllListeners();
38225     },
38226     /**
38227      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38228      */
38229     show : function(){
38230         this.pnode.addClass("active");
38231         this.showAction();
38232         if(Roo.isOpera){
38233             this.tabPanel.stripWrap.repaint();
38234         }
38235         this.fireEvent("activate", this.tabPanel, this);
38236     },
38237
38238     /**
38239      * Returns true if this tab is the active tab.
38240      * @return {Boolean}
38241      */
38242     isActive : function(){
38243         return this.tabPanel.getActiveTab() == this;
38244     },
38245
38246     /**
38247      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38248      */
38249     hide : function(){
38250         this.pnode.removeClass("active");
38251         this.hideAction();
38252         this.fireEvent("deactivate", this.tabPanel, this);
38253     },
38254
38255     hideAction : function(){
38256         this.bodyEl.hide();
38257         this.bodyEl.setStyle("position", "absolute");
38258         this.bodyEl.setLeft("-20000px");
38259         this.bodyEl.setTop("-20000px");
38260     },
38261
38262     showAction : function(){
38263         this.bodyEl.setStyle("position", "relative");
38264         this.bodyEl.setTop("");
38265         this.bodyEl.setLeft("");
38266         this.bodyEl.show();
38267     },
38268
38269     /**
38270      * Set the tooltip for the tab.
38271      * @param {String} tooltip The tab's tooltip
38272      */
38273     setTooltip : function(text){
38274         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38275             this.textEl.dom.qtip = text;
38276             this.textEl.dom.removeAttribute('title');
38277         }else{
38278             this.textEl.dom.title = text;
38279         }
38280     },
38281
38282     onTabClick : function(e){
38283         e.preventDefault();
38284         this.tabPanel.activate(this.id);
38285     },
38286
38287     onTabMouseDown : function(e){
38288         e.preventDefault();
38289         this.tabPanel.activate(this.id);
38290     },
38291 /*
38292     getWidth : function(){
38293         return this.inner.getWidth();
38294     },
38295
38296     setWidth : function(width){
38297         var iwidth = width - this.pnode.getPadding("lr");
38298         this.inner.setWidth(iwidth);
38299         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38300         this.pnode.setWidth(width);
38301     },
38302 */
38303     /**
38304      * Show or hide the tab
38305      * @param {Boolean} hidden True to hide or false to show.
38306      */
38307     setHidden : function(hidden){
38308         this.hidden = hidden;
38309         this.pnode.setStyle("display", hidden ? "none" : "");
38310     },
38311
38312     /**
38313      * Returns true if this tab is "hidden"
38314      * @return {Boolean}
38315      */
38316     isHidden : function(){
38317         return this.hidden;
38318     },
38319
38320     /**
38321      * Returns the text for this tab
38322      * @return {String}
38323      */
38324     getText : function(){
38325         return this.text;
38326     },
38327     /*
38328     autoSize : function(){
38329         //this.el.beginMeasure();
38330         this.textEl.setWidth(1);
38331         /*
38332          *  #2804 [new] Tabs in Roojs
38333          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38334          */
38335         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38336         //this.el.endMeasure();
38337     //},
38338
38339     /**
38340      * Sets the text for the tab (Note: this also sets the tooltip text)
38341      * @param {String} text The tab's text and tooltip
38342      */
38343     setText : function(text){
38344         this.text = text;
38345         this.textEl.update(text);
38346         this.setTooltip(text);
38347         //if(!this.tabPanel.resizeTabs){
38348         //    this.autoSize();
38349         //}
38350     },
38351     /**
38352      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38353      */
38354     activate : function(){
38355         this.tabPanel.activate(this.id);
38356     },
38357
38358     /**
38359      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38360      */
38361     disable : function(){
38362         if(this.tabPanel.active != this){
38363             this.disabled = true;
38364             this.pnode.addClass("disabled");
38365         }
38366     },
38367
38368     /**
38369      * Enables this TabPanelItem if it was previously disabled.
38370      */
38371     enable : function(){
38372         this.disabled = false;
38373         this.pnode.removeClass("disabled");
38374     },
38375
38376     /**
38377      * Sets the content for this TabPanelItem.
38378      * @param {String} content The content
38379      * @param {Boolean} loadScripts true to look for and load scripts
38380      */
38381     setContent : function(content, loadScripts){
38382         this.bodyEl.update(content, loadScripts);
38383     },
38384
38385     /**
38386      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38387      * @return {Roo.UpdateManager} The UpdateManager
38388      */
38389     getUpdateManager : function(){
38390         return this.bodyEl.getUpdateManager();
38391     },
38392
38393     /**
38394      * Set a URL to be used to load the content for this TabPanelItem.
38395      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38396      * @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)
38397      * @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)
38398      * @return {Roo.UpdateManager} The UpdateManager
38399      */
38400     setUrl : function(url, params, loadOnce){
38401         if(this.refreshDelegate){
38402             this.un('activate', this.refreshDelegate);
38403         }
38404         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38405         this.on("activate", this.refreshDelegate);
38406         return this.bodyEl.getUpdateManager();
38407     },
38408
38409     /** @private */
38410     _handleRefresh : function(url, params, loadOnce){
38411         if(!loadOnce || !this.loaded){
38412             var updater = this.bodyEl.getUpdateManager();
38413             updater.update(url, params, this._setLoaded.createDelegate(this));
38414         }
38415     },
38416
38417     /**
38418      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38419      *   Will fail silently if the setUrl method has not been called.
38420      *   This does not activate the panel, just updates its content.
38421      */
38422     refresh : function(){
38423         if(this.refreshDelegate){
38424            this.loaded = false;
38425            this.refreshDelegate();
38426         }
38427     },
38428
38429     /** @private */
38430     _setLoaded : function(){
38431         this.loaded = true;
38432     },
38433
38434     /** @private */
38435     closeClick : function(e){
38436         var o = {};
38437         e.stopEvent();
38438         this.fireEvent("beforeclose", this, o);
38439         if(o.cancel !== true){
38440             this.tabPanel.removeTab(this.id);
38441         }
38442     },
38443     /**
38444      * The text displayed in the tooltip for the close icon.
38445      * @type String
38446      */
38447     closeText : "Close this tab"
38448 });
38449 /**
38450 *    This script refer to:
38451 *    Title: International Telephone Input
38452 *    Author: Jack O'Connor
38453 *    Code version:  v12.1.12
38454 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38455 **/
38456
38457 Roo.bootstrap.PhoneInputData = function() {
38458     var d = [
38459       [
38460         "Afghanistan (‫افغانستان‬‎)",
38461         "af",
38462         "93"
38463       ],
38464       [
38465         "Albania (Shqipëri)",
38466         "al",
38467         "355"
38468       ],
38469       [
38470         "Algeria (‫الجزائر‬‎)",
38471         "dz",
38472         "213"
38473       ],
38474       [
38475         "American Samoa",
38476         "as",
38477         "1684"
38478       ],
38479       [
38480         "Andorra",
38481         "ad",
38482         "376"
38483       ],
38484       [
38485         "Angola",
38486         "ao",
38487         "244"
38488       ],
38489       [
38490         "Anguilla",
38491         "ai",
38492         "1264"
38493       ],
38494       [
38495         "Antigua and Barbuda",
38496         "ag",
38497         "1268"
38498       ],
38499       [
38500         "Argentina",
38501         "ar",
38502         "54"
38503       ],
38504       [
38505         "Armenia (Հայաստան)",
38506         "am",
38507         "374"
38508       ],
38509       [
38510         "Aruba",
38511         "aw",
38512         "297"
38513       ],
38514       [
38515         "Australia",
38516         "au",
38517         "61",
38518         0
38519       ],
38520       [
38521         "Austria (Österreich)",
38522         "at",
38523         "43"
38524       ],
38525       [
38526         "Azerbaijan (Azərbaycan)",
38527         "az",
38528         "994"
38529       ],
38530       [
38531         "Bahamas",
38532         "bs",
38533         "1242"
38534       ],
38535       [
38536         "Bahrain (‫البحرين‬‎)",
38537         "bh",
38538         "973"
38539       ],
38540       [
38541         "Bangladesh (বাংলাদেশ)",
38542         "bd",
38543         "880"
38544       ],
38545       [
38546         "Barbados",
38547         "bb",
38548         "1246"
38549       ],
38550       [
38551         "Belarus (Беларусь)",
38552         "by",
38553         "375"
38554       ],
38555       [
38556         "Belgium (België)",
38557         "be",
38558         "32"
38559       ],
38560       [
38561         "Belize",
38562         "bz",
38563         "501"
38564       ],
38565       [
38566         "Benin (Bénin)",
38567         "bj",
38568         "229"
38569       ],
38570       [
38571         "Bermuda",
38572         "bm",
38573         "1441"
38574       ],
38575       [
38576         "Bhutan (འབྲུག)",
38577         "bt",
38578         "975"
38579       ],
38580       [
38581         "Bolivia",
38582         "bo",
38583         "591"
38584       ],
38585       [
38586         "Bosnia and Herzegovina (Босна и Херцеговина)",
38587         "ba",
38588         "387"
38589       ],
38590       [
38591         "Botswana",
38592         "bw",
38593         "267"
38594       ],
38595       [
38596         "Brazil (Brasil)",
38597         "br",
38598         "55"
38599       ],
38600       [
38601         "British Indian Ocean Territory",
38602         "io",
38603         "246"
38604       ],
38605       [
38606         "British Virgin Islands",
38607         "vg",
38608         "1284"
38609       ],
38610       [
38611         "Brunei",
38612         "bn",
38613         "673"
38614       ],
38615       [
38616         "Bulgaria (България)",
38617         "bg",
38618         "359"
38619       ],
38620       [
38621         "Burkina Faso",
38622         "bf",
38623         "226"
38624       ],
38625       [
38626         "Burundi (Uburundi)",
38627         "bi",
38628         "257"
38629       ],
38630       [
38631         "Cambodia (កម្ពុជា)",
38632         "kh",
38633         "855"
38634       ],
38635       [
38636         "Cameroon (Cameroun)",
38637         "cm",
38638         "237"
38639       ],
38640       [
38641         "Canada",
38642         "ca",
38643         "1",
38644         1,
38645         ["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"]
38646       ],
38647       [
38648         "Cape Verde (Kabu Verdi)",
38649         "cv",
38650         "238"
38651       ],
38652       [
38653         "Caribbean Netherlands",
38654         "bq",
38655         "599",
38656         1
38657       ],
38658       [
38659         "Cayman Islands",
38660         "ky",
38661         "1345"
38662       ],
38663       [
38664         "Central African Republic (République centrafricaine)",
38665         "cf",
38666         "236"
38667       ],
38668       [
38669         "Chad (Tchad)",
38670         "td",
38671         "235"
38672       ],
38673       [
38674         "Chile",
38675         "cl",
38676         "56"
38677       ],
38678       [
38679         "China (中国)",
38680         "cn",
38681         "86"
38682       ],
38683       [
38684         "Christmas Island",
38685         "cx",
38686         "61",
38687         2
38688       ],
38689       [
38690         "Cocos (Keeling) Islands",
38691         "cc",
38692         "61",
38693         1
38694       ],
38695       [
38696         "Colombia",
38697         "co",
38698         "57"
38699       ],
38700       [
38701         "Comoros (‫جزر القمر‬‎)",
38702         "km",
38703         "269"
38704       ],
38705       [
38706         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38707         "cd",
38708         "243"
38709       ],
38710       [
38711         "Congo (Republic) (Congo-Brazzaville)",
38712         "cg",
38713         "242"
38714       ],
38715       [
38716         "Cook Islands",
38717         "ck",
38718         "682"
38719       ],
38720       [
38721         "Costa Rica",
38722         "cr",
38723         "506"
38724       ],
38725       [
38726         "Côte d’Ivoire",
38727         "ci",
38728         "225"
38729       ],
38730       [
38731         "Croatia (Hrvatska)",
38732         "hr",
38733         "385"
38734       ],
38735       [
38736         "Cuba",
38737         "cu",
38738         "53"
38739       ],
38740       [
38741         "Curaçao",
38742         "cw",
38743         "599",
38744         0
38745       ],
38746       [
38747         "Cyprus (Κύπρος)",
38748         "cy",
38749         "357"
38750       ],
38751       [
38752         "Czech Republic (Česká republika)",
38753         "cz",
38754         "420"
38755       ],
38756       [
38757         "Denmark (Danmark)",
38758         "dk",
38759         "45"
38760       ],
38761       [
38762         "Djibouti",
38763         "dj",
38764         "253"
38765       ],
38766       [
38767         "Dominica",
38768         "dm",
38769         "1767"
38770       ],
38771       [
38772         "Dominican Republic (República Dominicana)",
38773         "do",
38774         "1",
38775         2,
38776         ["809", "829", "849"]
38777       ],
38778       [
38779         "Ecuador",
38780         "ec",
38781         "593"
38782       ],
38783       [
38784         "Egypt (‫مصر‬‎)",
38785         "eg",
38786         "20"
38787       ],
38788       [
38789         "El Salvador",
38790         "sv",
38791         "503"
38792       ],
38793       [
38794         "Equatorial Guinea (Guinea Ecuatorial)",
38795         "gq",
38796         "240"
38797       ],
38798       [
38799         "Eritrea",
38800         "er",
38801         "291"
38802       ],
38803       [
38804         "Estonia (Eesti)",
38805         "ee",
38806         "372"
38807       ],
38808       [
38809         "Ethiopia",
38810         "et",
38811         "251"
38812       ],
38813       [
38814         "Falkland Islands (Islas Malvinas)",
38815         "fk",
38816         "500"
38817       ],
38818       [
38819         "Faroe Islands (Føroyar)",
38820         "fo",
38821         "298"
38822       ],
38823       [
38824         "Fiji",
38825         "fj",
38826         "679"
38827       ],
38828       [
38829         "Finland (Suomi)",
38830         "fi",
38831         "358",
38832         0
38833       ],
38834       [
38835         "France",
38836         "fr",
38837         "33"
38838       ],
38839       [
38840         "French Guiana (Guyane française)",
38841         "gf",
38842         "594"
38843       ],
38844       [
38845         "French Polynesia (Polynésie française)",
38846         "pf",
38847         "689"
38848       ],
38849       [
38850         "Gabon",
38851         "ga",
38852         "241"
38853       ],
38854       [
38855         "Gambia",
38856         "gm",
38857         "220"
38858       ],
38859       [
38860         "Georgia (საქართველო)",
38861         "ge",
38862         "995"
38863       ],
38864       [
38865         "Germany (Deutschland)",
38866         "de",
38867         "49"
38868       ],
38869       [
38870         "Ghana (Gaana)",
38871         "gh",
38872         "233"
38873       ],
38874       [
38875         "Gibraltar",
38876         "gi",
38877         "350"
38878       ],
38879       [
38880         "Greece (Ελλάδα)",
38881         "gr",
38882         "30"
38883       ],
38884       [
38885         "Greenland (Kalaallit Nunaat)",
38886         "gl",
38887         "299"
38888       ],
38889       [
38890         "Grenada",
38891         "gd",
38892         "1473"
38893       ],
38894       [
38895         "Guadeloupe",
38896         "gp",
38897         "590",
38898         0
38899       ],
38900       [
38901         "Guam",
38902         "gu",
38903         "1671"
38904       ],
38905       [
38906         "Guatemala",
38907         "gt",
38908         "502"
38909       ],
38910       [
38911         "Guernsey",
38912         "gg",
38913         "44",
38914         1
38915       ],
38916       [
38917         "Guinea (Guinée)",
38918         "gn",
38919         "224"
38920       ],
38921       [
38922         "Guinea-Bissau (Guiné Bissau)",
38923         "gw",
38924         "245"
38925       ],
38926       [
38927         "Guyana",
38928         "gy",
38929         "592"
38930       ],
38931       [
38932         "Haiti",
38933         "ht",
38934         "509"
38935       ],
38936       [
38937         "Honduras",
38938         "hn",
38939         "504"
38940       ],
38941       [
38942         "Hong Kong (香港)",
38943         "hk",
38944         "852"
38945       ],
38946       [
38947         "Hungary (Magyarország)",
38948         "hu",
38949         "36"
38950       ],
38951       [
38952         "Iceland (Ísland)",
38953         "is",
38954         "354"
38955       ],
38956       [
38957         "India (भारत)",
38958         "in",
38959         "91"
38960       ],
38961       [
38962         "Indonesia",
38963         "id",
38964         "62"
38965       ],
38966       [
38967         "Iran (‫ایران‬‎)",
38968         "ir",
38969         "98"
38970       ],
38971       [
38972         "Iraq (‫العراق‬‎)",
38973         "iq",
38974         "964"
38975       ],
38976       [
38977         "Ireland",
38978         "ie",
38979         "353"
38980       ],
38981       [
38982         "Isle of Man",
38983         "im",
38984         "44",
38985         2
38986       ],
38987       [
38988         "Israel (‫ישראל‬‎)",
38989         "il",
38990         "972"
38991       ],
38992       [
38993         "Italy (Italia)",
38994         "it",
38995         "39",
38996         0
38997       ],
38998       [
38999         "Jamaica",
39000         "jm",
39001         "1876"
39002       ],
39003       [
39004         "Japan (日本)",
39005         "jp",
39006         "81"
39007       ],
39008       [
39009         "Jersey",
39010         "je",
39011         "44",
39012         3
39013       ],
39014       [
39015         "Jordan (‫الأردن‬‎)",
39016         "jo",
39017         "962"
39018       ],
39019       [
39020         "Kazakhstan (Казахстан)",
39021         "kz",
39022         "7",
39023         1
39024       ],
39025       [
39026         "Kenya",
39027         "ke",
39028         "254"
39029       ],
39030       [
39031         "Kiribati",
39032         "ki",
39033         "686"
39034       ],
39035       [
39036         "Kosovo",
39037         "xk",
39038         "383"
39039       ],
39040       [
39041         "Kuwait (‫الكويت‬‎)",
39042         "kw",
39043         "965"
39044       ],
39045       [
39046         "Kyrgyzstan (Кыргызстан)",
39047         "kg",
39048         "996"
39049       ],
39050       [
39051         "Laos (ລາວ)",
39052         "la",
39053         "856"
39054       ],
39055       [
39056         "Latvia (Latvija)",
39057         "lv",
39058         "371"
39059       ],
39060       [
39061         "Lebanon (‫لبنان‬‎)",
39062         "lb",
39063         "961"
39064       ],
39065       [
39066         "Lesotho",
39067         "ls",
39068         "266"
39069       ],
39070       [
39071         "Liberia",
39072         "lr",
39073         "231"
39074       ],
39075       [
39076         "Libya (‫ليبيا‬‎)",
39077         "ly",
39078         "218"
39079       ],
39080       [
39081         "Liechtenstein",
39082         "li",
39083         "423"
39084       ],
39085       [
39086         "Lithuania (Lietuva)",
39087         "lt",
39088         "370"
39089       ],
39090       [
39091         "Luxembourg",
39092         "lu",
39093         "352"
39094       ],
39095       [
39096         "Macau (澳門)",
39097         "mo",
39098         "853"
39099       ],
39100       [
39101         "Macedonia (FYROM) (Македонија)",
39102         "mk",
39103         "389"
39104       ],
39105       [
39106         "Madagascar (Madagasikara)",
39107         "mg",
39108         "261"
39109       ],
39110       [
39111         "Malawi",
39112         "mw",
39113         "265"
39114       ],
39115       [
39116         "Malaysia",
39117         "my",
39118         "60"
39119       ],
39120       [
39121         "Maldives",
39122         "mv",
39123         "960"
39124       ],
39125       [
39126         "Mali",
39127         "ml",
39128         "223"
39129       ],
39130       [
39131         "Malta",
39132         "mt",
39133         "356"
39134       ],
39135       [
39136         "Marshall Islands",
39137         "mh",
39138         "692"
39139       ],
39140       [
39141         "Martinique",
39142         "mq",
39143         "596"
39144       ],
39145       [
39146         "Mauritania (‫موريتانيا‬‎)",
39147         "mr",
39148         "222"
39149       ],
39150       [
39151         "Mauritius (Moris)",
39152         "mu",
39153         "230"
39154       ],
39155       [
39156         "Mayotte",
39157         "yt",
39158         "262",
39159         1
39160       ],
39161       [
39162         "Mexico (México)",
39163         "mx",
39164         "52"
39165       ],
39166       [
39167         "Micronesia",
39168         "fm",
39169         "691"
39170       ],
39171       [
39172         "Moldova (Republica Moldova)",
39173         "md",
39174         "373"
39175       ],
39176       [
39177         "Monaco",
39178         "mc",
39179         "377"
39180       ],
39181       [
39182         "Mongolia (Монгол)",
39183         "mn",
39184         "976"
39185       ],
39186       [
39187         "Montenegro (Crna Gora)",
39188         "me",
39189         "382"
39190       ],
39191       [
39192         "Montserrat",
39193         "ms",
39194         "1664"
39195       ],
39196       [
39197         "Morocco (‫المغرب‬‎)",
39198         "ma",
39199         "212",
39200         0
39201       ],
39202       [
39203         "Mozambique (Moçambique)",
39204         "mz",
39205         "258"
39206       ],
39207       [
39208         "Myanmar (Burma) (မြန်မာ)",
39209         "mm",
39210         "95"
39211       ],
39212       [
39213         "Namibia (Namibië)",
39214         "na",
39215         "264"
39216       ],
39217       [
39218         "Nauru",
39219         "nr",
39220         "674"
39221       ],
39222       [
39223         "Nepal (नेपाल)",
39224         "np",
39225         "977"
39226       ],
39227       [
39228         "Netherlands (Nederland)",
39229         "nl",
39230         "31"
39231       ],
39232       [
39233         "New Caledonia (Nouvelle-Calédonie)",
39234         "nc",
39235         "687"
39236       ],
39237       [
39238         "New Zealand",
39239         "nz",
39240         "64"
39241       ],
39242       [
39243         "Nicaragua",
39244         "ni",
39245         "505"
39246       ],
39247       [
39248         "Niger (Nijar)",
39249         "ne",
39250         "227"
39251       ],
39252       [
39253         "Nigeria",
39254         "ng",
39255         "234"
39256       ],
39257       [
39258         "Niue",
39259         "nu",
39260         "683"
39261       ],
39262       [
39263         "Norfolk Island",
39264         "nf",
39265         "672"
39266       ],
39267       [
39268         "North Korea (조선 민주주의 인민 공화국)",
39269         "kp",
39270         "850"
39271       ],
39272       [
39273         "Northern Mariana Islands",
39274         "mp",
39275         "1670"
39276       ],
39277       [
39278         "Norway (Norge)",
39279         "no",
39280         "47",
39281         0
39282       ],
39283       [
39284         "Oman (‫عُمان‬‎)",
39285         "om",
39286         "968"
39287       ],
39288       [
39289         "Pakistan (‫پاکستان‬‎)",
39290         "pk",
39291         "92"
39292       ],
39293       [
39294         "Palau",
39295         "pw",
39296         "680"
39297       ],
39298       [
39299         "Palestine (‫فلسطين‬‎)",
39300         "ps",
39301         "970"
39302       ],
39303       [
39304         "Panama (Panamá)",
39305         "pa",
39306         "507"
39307       ],
39308       [
39309         "Papua New Guinea",
39310         "pg",
39311         "675"
39312       ],
39313       [
39314         "Paraguay",
39315         "py",
39316         "595"
39317       ],
39318       [
39319         "Peru (Perú)",
39320         "pe",
39321         "51"
39322       ],
39323       [
39324         "Philippines",
39325         "ph",
39326         "63"
39327       ],
39328       [
39329         "Poland (Polska)",
39330         "pl",
39331         "48"
39332       ],
39333       [
39334         "Portugal",
39335         "pt",
39336         "351"
39337       ],
39338       [
39339         "Puerto Rico",
39340         "pr",
39341         "1",
39342         3,
39343         ["787", "939"]
39344       ],
39345       [
39346         "Qatar (‫قطر‬‎)",
39347         "qa",
39348         "974"
39349       ],
39350       [
39351         "Réunion (La Réunion)",
39352         "re",
39353         "262",
39354         0
39355       ],
39356       [
39357         "Romania (România)",
39358         "ro",
39359         "40"
39360       ],
39361       [
39362         "Russia (Россия)",
39363         "ru",
39364         "7",
39365         0
39366       ],
39367       [
39368         "Rwanda",
39369         "rw",
39370         "250"
39371       ],
39372       [
39373         "Saint Barthélemy",
39374         "bl",
39375         "590",
39376         1
39377       ],
39378       [
39379         "Saint Helena",
39380         "sh",
39381         "290"
39382       ],
39383       [
39384         "Saint Kitts and Nevis",
39385         "kn",
39386         "1869"
39387       ],
39388       [
39389         "Saint Lucia",
39390         "lc",
39391         "1758"
39392       ],
39393       [
39394         "Saint Martin (Saint-Martin (partie française))",
39395         "mf",
39396         "590",
39397         2
39398       ],
39399       [
39400         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39401         "pm",
39402         "508"
39403       ],
39404       [
39405         "Saint Vincent and the Grenadines",
39406         "vc",
39407         "1784"
39408       ],
39409       [
39410         "Samoa",
39411         "ws",
39412         "685"
39413       ],
39414       [
39415         "San Marino",
39416         "sm",
39417         "378"
39418       ],
39419       [
39420         "São Tomé and Príncipe (São Tomé e Príncipe)",
39421         "st",
39422         "239"
39423       ],
39424       [
39425         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39426         "sa",
39427         "966"
39428       ],
39429       [
39430         "Senegal (Sénégal)",
39431         "sn",
39432         "221"
39433       ],
39434       [
39435         "Serbia (Србија)",
39436         "rs",
39437         "381"
39438       ],
39439       [
39440         "Seychelles",
39441         "sc",
39442         "248"
39443       ],
39444       [
39445         "Sierra Leone",
39446         "sl",
39447         "232"
39448       ],
39449       [
39450         "Singapore",
39451         "sg",
39452         "65"
39453       ],
39454       [
39455         "Sint Maarten",
39456         "sx",
39457         "1721"
39458       ],
39459       [
39460         "Slovakia (Slovensko)",
39461         "sk",
39462         "421"
39463       ],
39464       [
39465         "Slovenia (Slovenija)",
39466         "si",
39467         "386"
39468       ],
39469       [
39470         "Solomon Islands",
39471         "sb",
39472         "677"
39473       ],
39474       [
39475         "Somalia (Soomaaliya)",
39476         "so",
39477         "252"
39478       ],
39479       [
39480         "South Africa",
39481         "za",
39482         "27"
39483       ],
39484       [
39485         "South Korea (대한민국)",
39486         "kr",
39487         "82"
39488       ],
39489       [
39490         "South Sudan (‫جنوب السودان‬‎)",
39491         "ss",
39492         "211"
39493       ],
39494       [
39495         "Spain (España)",
39496         "es",
39497         "34"
39498       ],
39499       [
39500         "Sri Lanka (ශ්‍රී ලංකාව)",
39501         "lk",
39502         "94"
39503       ],
39504       [
39505         "Sudan (‫السودان‬‎)",
39506         "sd",
39507         "249"
39508       ],
39509       [
39510         "Suriname",
39511         "sr",
39512         "597"
39513       ],
39514       [
39515         "Svalbard and Jan Mayen",
39516         "sj",
39517         "47",
39518         1
39519       ],
39520       [
39521         "Swaziland",
39522         "sz",
39523         "268"
39524       ],
39525       [
39526         "Sweden (Sverige)",
39527         "se",
39528         "46"
39529       ],
39530       [
39531         "Switzerland (Schweiz)",
39532         "ch",
39533         "41"
39534       ],
39535       [
39536         "Syria (‫سوريا‬‎)",
39537         "sy",
39538         "963"
39539       ],
39540       [
39541         "Taiwan (台灣)",
39542         "tw",
39543         "886"
39544       ],
39545       [
39546         "Tajikistan",
39547         "tj",
39548         "992"
39549       ],
39550       [
39551         "Tanzania",
39552         "tz",
39553         "255"
39554       ],
39555       [
39556         "Thailand (ไทย)",
39557         "th",
39558         "66"
39559       ],
39560       [
39561         "Timor-Leste",
39562         "tl",
39563         "670"
39564       ],
39565       [
39566         "Togo",
39567         "tg",
39568         "228"
39569       ],
39570       [
39571         "Tokelau",
39572         "tk",
39573         "690"
39574       ],
39575       [
39576         "Tonga",
39577         "to",
39578         "676"
39579       ],
39580       [
39581         "Trinidad and Tobago",
39582         "tt",
39583         "1868"
39584       ],
39585       [
39586         "Tunisia (‫تونس‬‎)",
39587         "tn",
39588         "216"
39589       ],
39590       [
39591         "Turkey (Türkiye)",
39592         "tr",
39593         "90"
39594       ],
39595       [
39596         "Turkmenistan",
39597         "tm",
39598         "993"
39599       ],
39600       [
39601         "Turks and Caicos Islands",
39602         "tc",
39603         "1649"
39604       ],
39605       [
39606         "Tuvalu",
39607         "tv",
39608         "688"
39609       ],
39610       [
39611         "U.S. Virgin Islands",
39612         "vi",
39613         "1340"
39614       ],
39615       [
39616         "Uganda",
39617         "ug",
39618         "256"
39619       ],
39620       [
39621         "Ukraine (Україна)",
39622         "ua",
39623         "380"
39624       ],
39625       [
39626         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39627         "ae",
39628         "971"
39629       ],
39630       [
39631         "United Kingdom",
39632         "gb",
39633         "44",
39634         0
39635       ],
39636       [
39637         "United States",
39638         "us",
39639         "1",
39640         0
39641       ],
39642       [
39643         "Uruguay",
39644         "uy",
39645         "598"
39646       ],
39647       [
39648         "Uzbekistan (Oʻzbekiston)",
39649         "uz",
39650         "998"
39651       ],
39652       [
39653         "Vanuatu",
39654         "vu",
39655         "678"
39656       ],
39657       [
39658         "Vatican City (Città del Vaticano)",
39659         "va",
39660         "39",
39661         1
39662       ],
39663       [
39664         "Venezuela",
39665         "ve",
39666         "58"
39667       ],
39668       [
39669         "Vietnam (Việt Nam)",
39670         "vn",
39671         "84"
39672       ],
39673       [
39674         "Wallis and Futuna (Wallis-et-Futuna)",
39675         "wf",
39676         "681"
39677       ],
39678       [
39679         "Western Sahara (‫الصحراء الغربية‬‎)",
39680         "eh",
39681         "212",
39682         1
39683       ],
39684       [
39685         "Yemen (‫اليمن‬‎)",
39686         "ye",
39687         "967"
39688       ],
39689       [
39690         "Zambia",
39691         "zm",
39692         "260"
39693       ],
39694       [
39695         "Zimbabwe",
39696         "zw",
39697         "263"
39698       ],
39699       [
39700         "Åland Islands",
39701         "ax",
39702         "358",
39703         1
39704       ]
39705   ];
39706   
39707   return d;
39708 }/**
39709 *    This script refer to:
39710 *    Title: International Telephone Input
39711 *    Author: Jack O'Connor
39712 *    Code version:  v12.1.12
39713 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39714 **/
39715
39716 /**
39717  * @class Roo.bootstrap.PhoneInput
39718  * @extends Roo.bootstrap.TriggerField
39719  * An input with International dial-code selection
39720  
39721  * @cfg {String} defaultDialCode default '+852'
39722  * @cfg {Array} preferedCountries default []
39723   
39724  * @constructor
39725  * Create a new PhoneInput.
39726  * @param {Object} config Configuration options
39727  */
39728
39729 Roo.bootstrap.PhoneInput = function(config) {
39730     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39731 };
39732
39733 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39734         
39735         listWidth: undefined,
39736         
39737         selectedClass: 'active',
39738         
39739         invalidClass : "has-warning",
39740         
39741         validClass: 'has-success',
39742         
39743         allowed: '0123456789',
39744         
39745         /**
39746          * @cfg {String} defaultDialCode The default dial code when initializing the input
39747          */
39748         defaultDialCode: '+852',
39749         
39750         /**
39751          * @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
39752          */
39753         preferedCountries: false,
39754         
39755         getAutoCreate : function()
39756         {
39757             var data = Roo.bootstrap.PhoneInputData();
39758             var align = this.labelAlign || this.parentLabelAlign();
39759             var id = Roo.id();
39760             
39761             this.allCountries = [];
39762             this.dialCodeMapping = [];
39763             
39764             for (var i = 0; i < data.length; i++) {
39765               var c = data[i];
39766               this.allCountries[i] = {
39767                 name: c[0],
39768                 iso2: c[1],
39769                 dialCode: c[2],
39770                 priority: c[3] || 0,
39771                 areaCodes: c[4] || null
39772               };
39773               this.dialCodeMapping[c[2]] = {
39774                   name: c[0],
39775                   iso2: c[1],
39776                   priority: c[3] || 0,
39777                   areaCodes: c[4] || null
39778               };
39779             }
39780             
39781             var cfg = {
39782                 cls: 'form-group',
39783                 cn: []
39784             };
39785             
39786             var input =  {
39787                 tag: 'input',
39788                 id : id,
39789                 cls : 'form-control tel-input',
39790                 autocomplete: 'new-password'
39791             };
39792             
39793             var hiddenInput = {
39794                 tag: 'input',
39795                 type: 'hidden',
39796                 cls: 'hidden-tel-input'
39797             };
39798             
39799             if (this.name) {
39800                 hiddenInput.name = this.name;
39801             }
39802             
39803             if (this.disabled) {
39804                 input.disabled = true;
39805             }
39806             
39807             var flag_container = {
39808                 tag: 'div',
39809                 cls: 'flag-box',
39810                 cn: [
39811                     {
39812                         tag: 'div',
39813                         cls: 'flag'
39814                     },
39815                     {
39816                         tag: 'div',
39817                         cls: 'caret'
39818                     }
39819                 ]
39820             };
39821             
39822             var box = {
39823                 tag: 'div',
39824                 cls: this.hasFeedback ? 'has-feedback' : '',
39825                 cn: [
39826                     hiddenInput,
39827                     input,
39828                     {
39829                         tag: 'input',
39830                         cls: 'dial-code-holder',
39831                         disabled: true
39832                     }
39833                 ]
39834             };
39835             
39836             var container = {
39837                 cls: 'roo-select2-container input-group',
39838                 cn: [
39839                     flag_container,
39840                     box
39841                 ]
39842             };
39843             
39844             if (this.fieldLabel.length) {
39845                 var indicator = {
39846                     tag: 'i',
39847                     tooltip: 'This field is required'
39848                 };
39849                 
39850                 var label = {
39851                     tag: 'label',
39852                     'for':  id,
39853                     cls: 'control-label',
39854                     cn: []
39855                 };
39856                 
39857                 var label_text = {
39858                     tag: 'span',
39859                     html: this.fieldLabel
39860                 };
39861                 
39862                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39863                 label.cn = [
39864                     indicator,
39865                     label_text
39866                 ];
39867                 
39868                 if(this.indicatorpos == 'right') {
39869                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39870                     label.cn = [
39871                         label_text,
39872                         indicator
39873                     ];
39874                 }
39875                 
39876                 if(align == 'left') {
39877                     container = {
39878                         tag: 'div',
39879                         cn: [
39880                             container
39881                         ]
39882                     };
39883                     
39884                     if(this.labelWidth > 12){
39885                         label.style = "width: " + this.labelWidth + 'px';
39886                     }
39887                     if(this.labelWidth < 13 && this.labelmd == 0){
39888                         this.labelmd = this.labelWidth;
39889                     }
39890                     if(this.labellg > 0){
39891                         label.cls += ' col-lg-' + this.labellg;
39892                         input.cls += ' col-lg-' + (12 - this.labellg);
39893                     }
39894                     if(this.labelmd > 0){
39895                         label.cls += ' col-md-' + this.labelmd;
39896                         container.cls += ' col-md-' + (12 - this.labelmd);
39897                     }
39898                     if(this.labelsm > 0){
39899                         label.cls += ' col-sm-' + this.labelsm;
39900                         container.cls += ' col-sm-' + (12 - this.labelsm);
39901                     }
39902                     if(this.labelxs > 0){
39903                         label.cls += ' col-xs-' + this.labelxs;
39904                         container.cls += ' col-xs-' + (12 - this.labelxs);
39905                     }
39906                 }
39907             }
39908             
39909             cfg.cn = [
39910                 label,
39911                 container
39912             ];
39913             
39914             var settings = this;
39915             
39916             ['xs','sm','md','lg'].map(function(size){
39917                 if (settings[size]) {
39918                     cfg.cls += ' col-' + size + '-' + settings[size];
39919                 }
39920             });
39921             
39922             this.store = new Roo.data.Store({
39923                 proxy : new Roo.data.MemoryProxy({}),
39924                 reader : new Roo.data.JsonReader({
39925                     fields : [
39926                         {
39927                             'name' : 'name',
39928                             'type' : 'string'
39929                         },
39930                         {
39931                             'name' : 'iso2',
39932                             'type' : 'string'
39933                         },
39934                         {
39935                             'name' : 'dialCode',
39936                             'type' : 'string'
39937                         },
39938                         {
39939                             'name' : 'priority',
39940                             'type' : 'string'
39941                         },
39942                         {
39943                             'name' : 'areaCodes',
39944                             'type' : 'string'
39945                         }
39946                     ]
39947                 })
39948             });
39949             
39950             if(!this.preferedCountries) {
39951                 this.preferedCountries = [
39952                     'hk',
39953                     'gb',
39954                     'us'
39955                 ];
39956             }
39957             
39958             var p = this.preferedCountries.reverse();
39959             
39960             if(p) {
39961                 for (var i = 0; i < p.length; i++) {
39962                     for (var j = 0; j < this.allCountries.length; j++) {
39963                         if(this.allCountries[j].iso2 == p[i]) {
39964                             var t = this.allCountries[j];
39965                             this.allCountries.splice(j,1);
39966                             this.allCountries.unshift(t);
39967                         }
39968                     } 
39969                 }
39970             }
39971             
39972             this.store.proxy.data = {
39973                 success: true,
39974                 data: this.allCountries
39975             };
39976             
39977             return cfg;
39978         },
39979         
39980         initEvents : function()
39981         {
39982             this.createList();
39983             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39984             
39985             this.indicator = this.indicatorEl();
39986             this.flag = this.flagEl();
39987             this.dialCodeHolder = this.dialCodeHolderEl();
39988             
39989             this.trigger = this.el.select('div.flag-box',true).first();
39990             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39991             
39992             var _this = this;
39993             
39994             (function(){
39995                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39996                 _this.list.setWidth(lw);
39997             }).defer(100);
39998             
39999             this.list.on('mouseover', this.onViewOver, this);
40000             this.list.on('mousemove', this.onViewMove, this);
40001             this.inputEl().on("keyup", this.onKeyUp, this);
40002             
40003             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40004
40005             this.view = new Roo.View(this.list, this.tpl, {
40006                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40007             });
40008             
40009             this.view.on('click', this.onViewClick, this);
40010             this.setValue(this.defaultDialCode);
40011         },
40012         
40013         onTriggerClick : function(e)
40014         {
40015             Roo.log('trigger click');
40016             if(this.disabled){
40017                 return;
40018             }
40019             
40020             if(this.isExpanded()){
40021                 this.collapse();
40022                 this.hasFocus = false;
40023             }else {
40024                 this.store.load({});
40025                 this.hasFocus = true;
40026                 this.expand();
40027             }
40028         },
40029         
40030         isExpanded : function()
40031         {
40032             return this.list.isVisible();
40033         },
40034         
40035         collapse : function()
40036         {
40037             if(!this.isExpanded()){
40038                 return;
40039             }
40040             this.list.hide();
40041             Roo.get(document).un('mousedown', this.collapseIf, this);
40042             Roo.get(document).un('mousewheel', this.collapseIf, this);
40043             this.fireEvent('collapse', this);
40044             this.validate();
40045         },
40046         
40047         expand : function()
40048         {
40049             Roo.log('expand');
40050
40051             if(this.isExpanded() || !this.hasFocus){
40052                 return;
40053             }
40054             
40055             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40056             this.list.setWidth(lw);
40057             
40058             this.list.show();
40059             this.restrictHeight();
40060             
40061             Roo.get(document).on('mousedown', this.collapseIf, this);
40062             Roo.get(document).on('mousewheel', this.collapseIf, this);
40063             
40064             this.fireEvent('expand', this);
40065         },
40066         
40067         restrictHeight : function()
40068         {
40069             this.list.alignTo(this.inputEl(), this.listAlign);
40070             this.list.alignTo(this.inputEl(), this.listAlign);
40071         },
40072         
40073         onViewOver : function(e, t)
40074         {
40075             if(this.inKeyMode){
40076                 return;
40077             }
40078             var item = this.view.findItemFromChild(t);
40079             
40080             if(item){
40081                 var index = this.view.indexOf(item);
40082                 this.select(index, false);
40083             }
40084         },
40085
40086         // private
40087         onViewClick : function(view, doFocus, el, e)
40088         {
40089             var index = this.view.getSelectedIndexes()[0];
40090             
40091             var r = this.store.getAt(index);
40092             
40093             if(r){
40094                 this.onSelect(r, index);
40095             }
40096             if(doFocus !== false && !this.blockFocus){
40097                 this.inputEl().focus();
40098             }
40099         },
40100         
40101         onViewMove : function(e, t)
40102         {
40103             this.inKeyMode = false;
40104         },
40105         
40106         select : function(index, scrollIntoView)
40107         {
40108             this.selectedIndex = index;
40109             this.view.select(index);
40110             if(scrollIntoView !== false){
40111                 var el = this.view.getNode(index);
40112                 if(el){
40113                     this.list.scrollChildIntoView(el, false);
40114                 }
40115             }
40116         },
40117         
40118         createList : function()
40119         {
40120             this.list = Roo.get(document.body).createChild({
40121                 tag: 'ul',
40122                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40123                 style: 'display:none'
40124             });
40125             
40126             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40127         },
40128         
40129         collapseIf : function(e)
40130         {
40131             var in_combo  = e.within(this.el);
40132             var in_list =  e.within(this.list);
40133             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40134             
40135             if (in_combo || in_list || is_list) {
40136                 return;
40137             }
40138             this.collapse();
40139         },
40140         
40141         onSelect : function(record, index)
40142         {
40143             if(this.fireEvent('beforeselect', this, record, index) !== false){
40144                 
40145                 this.setFlagClass(record.data.iso2);
40146                 this.setDialCode(record.data.dialCode);
40147                 this.hasFocus = false;
40148                 this.collapse();
40149                 this.fireEvent('select', this, record, index);
40150             }
40151         },
40152         
40153         flagEl : function()
40154         {
40155             var flag = this.el.select('div.flag',true).first();
40156             if(!flag){
40157                 return false;
40158             }
40159             return flag;
40160         },
40161         
40162         dialCodeHolderEl : function()
40163         {
40164             var d = this.el.select('input.dial-code-holder',true).first();
40165             if(!d){
40166                 return false;
40167             }
40168             return d;
40169         },
40170         
40171         setDialCode : function(v)
40172         {
40173             this.dialCodeHolder.dom.value = '+'+v;
40174         },
40175         
40176         setFlagClass : function(n)
40177         {
40178             this.flag.dom.className = 'flag '+n;
40179         },
40180         
40181         getValue : function()
40182         {
40183             var v = this.inputEl().getValue();
40184             if(this.dialCodeHolder) {
40185                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40186             }
40187             return v;
40188         },
40189         
40190         setValue : function(v)
40191         {
40192             var d = this.getDialCode(v);
40193             
40194             //invalid dial code
40195             if(v.length == 0 || !d || d.length == 0) {
40196                 if(this.rendered){
40197                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40198                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40199                 }
40200                 return;
40201             }
40202             
40203             //valid dial code
40204             this.setFlagClass(this.dialCodeMapping[d].iso2);
40205             this.setDialCode(d);
40206             this.inputEl().dom.value = v.replace('+'+d,'');
40207             this.hiddenEl().dom.value = this.getValue();
40208             
40209             this.validate();
40210         },
40211         
40212         getDialCode : function(v)
40213         {
40214             v = v ||  '';
40215             
40216             if (v.length == 0) {
40217                 return this.dialCodeHolder.dom.value;
40218             }
40219             
40220             var dialCode = "";
40221             if (v.charAt(0) != "+") {
40222                 return false;
40223             }
40224             var numericChars = "";
40225             for (var i = 1; i < v.length; i++) {
40226               var c = v.charAt(i);
40227               if (!isNaN(c)) {
40228                 numericChars += c;
40229                 if (this.dialCodeMapping[numericChars]) {
40230                   dialCode = v.substr(1, i);
40231                 }
40232                 if (numericChars.length == 4) {
40233                   break;
40234                 }
40235               }
40236             }
40237             return dialCode;
40238         },
40239         
40240         reset : function()
40241         {
40242             this.setValue(this.defaultDialCode);
40243             this.validate();
40244         },
40245         
40246         hiddenEl : function()
40247         {
40248             return this.el.select('input.hidden-tel-input',true).first();
40249         },
40250         
40251         onKeyUp : function(e){
40252             
40253             var k = e.getKey();
40254             var c = e.getCharCode();
40255             
40256             if(
40257                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40258                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40259             ){
40260                 e.stopEvent();
40261             }
40262             
40263             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40264             //     return;
40265             // }
40266             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40267                 e.stopEvent();
40268             }
40269             
40270             this.setValue(this.getValue());
40271         }
40272         
40273 });
40274 /**
40275  * @class Roo.bootstrap.MoneyField
40276  * @extends Roo.bootstrap.ComboBox
40277  * Bootstrap MoneyField class
40278  * 
40279  * @constructor
40280  * Create a new MoneyField.
40281  * @param {Object} config Configuration options
40282  */
40283
40284 Roo.bootstrap.MoneyField = function(config) {
40285     
40286     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40287     
40288 };
40289
40290 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40291     
40292     /**
40293      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40294      */
40295     allowDecimals : true,
40296     /**
40297      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40298      */
40299     decimalSeparator : ".",
40300     /**
40301      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40302      */
40303     decimalPrecision : 0,
40304     /**
40305      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40306      */
40307     allowNegative : true,
40308     /**
40309      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40310      */
40311     allowZero: true,
40312     /**
40313      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40314      */
40315     minValue : Number.NEGATIVE_INFINITY,
40316     /**
40317      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40318      */
40319     maxValue : Number.MAX_VALUE,
40320     /**
40321      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40322      */
40323     minText : "The minimum value for this field is {0}",
40324     /**
40325      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40326      */
40327     maxText : "The maximum value for this field is {0}",
40328     /**
40329      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40330      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40331      */
40332     nanText : "{0} is not a valid number",
40333     /**
40334      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40335      */
40336     castInt : true,
40337     /**
40338      * @cfg {String} defaults currency of the MoneyField
40339      * value should be in lkey
40340      */
40341     defaultCurrency : false,
40342     /**
40343      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40344      */
40345     thousandsDelimiter : false,
40346     
40347     
40348     inputlg : 9,
40349     inputmd : 9,
40350     inputsm : 9,
40351     inputxs : 6,
40352     
40353     store : false,
40354     
40355     getAutoCreate : function()
40356     {
40357         var align = this.labelAlign || this.parentLabelAlign();
40358         
40359         var id = Roo.id();
40360
40361         var cfg = {
40362             cls: 'form-group',
40363             cn: []
40364         };
40365
40366         var input =  {
40367             tag: 'input',
40368             id : id,
40369             cls : 'form-control roo-money-amount-input',
40370             autocomplete: 'new-password'
40371         };
40372         
40373         var hiddenInput = {
40374             tag: 'input',
40375             type: 'hidden',
40376             id: Roo.id(),
40377             cls: 'hidden-number-input'
40378         };
40379         
40380         if (this.name) {
40381             hiddenInput.name = this.name;
40382         }
40383
40384         if (this.disabled) {
40385             input.disabled = true;
40386         }
40387
40388         var clg = 12 - this.inputlg;
40389         var cmd = 12 - this.inputmd;
40390         var csm = 12 - this.inputsm;
40391         var cxs = 12 - this.inputxs;
40392         
40393         var container = {
40394             tag : 'div',
40395             cls : 'row roo-money-field',
40396             cn : [
40397                 {
40398                     tag : 'div',
40399                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40400                     cn : [
40401                         {
40402                             tag : 'div',
40403                             cls: 'roo-select2-container input-group',
40404                             cn: [
40405                                 {
40406                                     tag : 'input',
40407                                     cls : 'form-control roo-money-currency-input',
40408                                     autocomplete: 'new-password',
40409                                     readOnly : 1,
40410                                     name : this.currencyName
40411                                 },
40412                                 {
40413                                     tag :'span',
40414                                     cls : 'input-group-addon',
40415                                     cn : [
40416                                         {
40417                                             tag: 'span',
40418                                             cls: 'caret'
40419                                         }
40420                                     ]
40421                                 }
40422                             ]
40423                         }
40424                     ]
40425                 },
40426                 {
40427                     tag : 'div',
40428                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40429                     cn : [
40430                         {
40431                             tag: 'div',
40432                             cls: this.hasFeedback ? 'has-feedback' : '',
40433                             cn: [
40434                                 input
40435                             ]
40436                         }
40437                     ]
40438                 }
40439             ]
40440             
40441         };
40442         
40443         if (this.fieldLabel.length) {
40444             var indicator = {
40445                 tag: 'i',
40446                 tooltip: 'This field is required'
40447             };
40448
40449             var label = {
40450                 tag: 'label',
40451                 'for':  id,
40452                 cls: 'control-label',
40453                 cn: []
40454             };
40455
40456             var label_text = {
40457                 tag: 'span',
40458                 html: this.fieldLabel
40459             };
40460
40461             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40462             label.cn = [
40463                 indicator,
40464                 label_text
40465             ];
40466
40467             if(this.indicatorpos == 'right') {
40468                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40469                 label.cn = [
40470                     label_text,
40471                     indicator
40472                 ];
40473             }
40474
40475             if(align == 'left') {
40476                 container = {
40477                     tag: 'div',
40478                     cn: [
40479                         container
40480                     ]
40481                 };
40482
40483                 if(this.labelWidth > 12){
40484                     label.style = "width: " + this.labelWidth + 'px';
40485                 }
40486                 if(this.labelWidth < 13 && this.labelmd == 0){
40487                     this.labelmd = this.labelWidth;
40488                 }
40489                 if(this.labellg > 0){
40490                     label.cls += ' col-lg-' + this.labellg;
40491                     input.cls += ' col-lg-' + (12 - this.labellg);
40492                 }
40493                 if(this.labelmd > 0){
40494                     label.cls += ' col-md-' + this.labelmd;
40495                     container.cls += ' col-md-' + (12 - this.labelmd);
40496                 }
40497                 if(this.labelsm > 0){
40498                     label.cls += ' col-sm-' + this.labelsm;
40499                     container.cls += ' col-sm-' + (12 - this.labelsm);
40500                 }
40501                 if(this.labelxs > 0){
40502                     label.cls += ' col-xs-' + this.labelxs;
40503                     container.cls += ' col-xs-' + (12 - this.labelxs);
40504                 }
40505             }
40506         }
40507
40508         cfg.cn = [
40509             label,
40510             container,
40511             hiddenInput
40512         ];
40513         
40514         var settings = this;
40515
40516         ['xs','sm','md','lg'].map(function(size){
40517             if (settings[size]) {
40518                 cfg.cls += ' col-' + size + '-' + settings[size];
40519             }
40520         });
40521         
40522         return cfg;
40523     },
40524     
40525     initEvents : function()
40526     {
40527         this.indicator = this.indicatorEl();
40528         
40529         this.initCurrencyEvent();
40530         
40531         this.initNumberEvent();
40532     },
40533     
40534     initCurrencyEvent : function()
40535     {
40536         if (!this.store) {
40537             throw "can not find store for combo";
40538         }
40539         
40540         this.store = Roo.factory(this.store, Roo.data);
40541         this.store.parent = this;
40542         
40543         this.createList();
40544         
40545         this.triggerEl = this.el.select('.input-group-addon', true).first();
40546         
40547         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40548         
40549         var _this = this;
40550         
40551         (function(){
40552             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40553             _this.list.setWidth(lw);
40554         }).defer(100);
40555         
40556         this.list.on('mouseover', this.onViewOver, this);
40557         this.list.on('mousemove', this.onViewMove, this);
40558         this.list.on('scroll', this.onViewScroll, this);
40559         
40560         if(!this.tpl){
40561             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40562         }
40563         
40564         this.view = new Roo.View(this.list, this.tpl, {
40565             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40566         });
40567         
40568         this.view.on('click', this.onViewClick, this);
40569         
40570         this.store.on('beforeload', this.onBeforeLoad, this);
40571         this.store.on('load', this.onLoad, this);
40572         this.store.on('loadexception', this.onLoadException, this);
40573         
40574         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40575             "up" : function(e){
40576                 this.inKeyMode = true;
40577                 this.selectPrev();
40578             },
40579
40580             "down" : function(e){
40581                 if(!this.isExpanded()){
40582                     this.onTriggerClick();
40583                 }else{
40584                     this.inKeyMode = true;
40585                     this.selectNext();
40586                 }
40587             },
40588
40589             "enter" : function(e){
40590                 this.collapse();
40591                 
40592                 if(this.fireEvent("specialkey", this, e)){
40593                     this.onViewClick(false);
40594                 }
40595                 
40596                 return true;
40597             },
40598
40599             "esc" : function(e){
40600                 this.collapse();
40601             },
40602
40603             "tab" : function(e){
40604                 this.collapse();
40605                 
40606                 if(this.fireEvent("specialkey", this, e)){
40607                     this.onViewClick(false);
40608                 }
40609                 
40610                 return true;
40611             },
40612
40613             scope : this,
40614
40615             doRelay : function(foo, bar, hname){
40616                 if(hname == 'down' || this.scope.isExpanded()){
40617                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40618                 }
40619                 return true;
40620             },
40621
40622             forceKeyDown: true
40623         });
40624         
40625         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40626         
40627     },
40628     
40629     initNumberEvent : function(e)
40630     {
40631         this.inputEl().on("keydown" , this.fireKey,  this);
40632         this.inputEl().on("focus", this.onFocus,  this);
40633         this.inputEl().on("blur", this.onBlur,  this);
40634         
40635         this.inputEl().relayEvent('keyup', this);
40636         
40637         if(this.indicator){
40638             this.indicator.addClass('invisible');
40639         }
40640  
40641         this.originalValue = this.getValue();
40642         
40643         if(this.validationEvent == 'keyup'){
40644             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40645             this.inputEl().on('keyup', this.filterValidation, this);
40646         }
40647         else if(this.validationEvent !== false){
40648             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40649         }
40650         
40651         if(this.selectOnFocus){
40652             this.on("focus", this.preFocus, this);
40653             
40654         }
40655         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40656             this.inputEl().on("keypress", this.filterKeys, this);
40657         } else {
40658             this.inputEl().relayEvent('keypress', this);
40659         }
40660         
40661         var allowed = "0123456789";
40662         
40663         if(this.allowDecimals){
40664             allowed += this.decimalSeparator;
40665         }
40666         
40667         if(this.allowNegative){
40668             allowed += "-";
40669         }
40670         
40671         if(this.thousandsDelimiter) {
40672             allowed += ",";
40673         }
40674         
40675         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40676         
40677         var keyPress = function(e){
40678             
40679             var k = e.getKey();
40680             
40681             var c = e.getCharCode();
40682             
40683             if(
40684                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40685                     allowed.indexOf(String.fromCharCode(c)) === -1
40686             ){
40687                 e.stopEvent();
40688                 return;
40689             }
40690             
40691             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40692                 return;
40693             }
40694             
40695             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40696                 e.stopEvent();
40697             }
40698         };
40699         
40700         this.inputEl().on("keypress", keyPress, this);
40701         
40702     },
40703     
40704     onTriggerClick : function(e)
40705     {   
40706         if(this.disabled){
40707             return;
40708         }
40709         
40710         this.page = 0;
40711         this.loadNext = false;
40712         
40713         if(this.isExpanded()){
40714             this.collapse();
40715             return;
40716         }
40717         
40718         this.hasFocus = true;
40719         
40720         if(this.triggerAction == 'all') {
40721             this.doQuery(this.allQuery, true);
40722             return;
40723         }
40724         
40725         this.doQuery(this.getRawValue());
40726     },
40727     
40728     getCurrency : function()
40729     {   
40730         var v = this.currencyEl().getValue();
40731         
40732         return v;
40733     },
40734     
40735     restrictHeight : function()
40736     {
40737         this.list.alignTo(this.currencyEl(), this.listAlign);
40738         this.list.alignTo(this.currencyEl(), this.listAlign);
40739     },
40740     
40741     onViewClick : function(view, doFocus, el, e)
40742     {
40743         var index = this.view.getSelectedIndexes()[0];
40744         
40745         var r = this.store.getAt(index);
40746         
40747         if(r){
40748             this.onSelect(r, index);
40749         }
40750     },
40751     
40752     onSelect : function(record, index){
40753         
40754         if(this.fireEvent('beforeselect', this, record, index) !== false){
40755         
40756             this.setFromCurrencyData(index > -1 ? record.data : false);
40757             
40758             this.collapse();
40759             
40760             this.fireEvent('select', this, record, index);
40761         }
40762     },
40763     
40764     setFromCurrencyData : function(o)
40765     {
40766         var currency = '';
40767         
40768         this.lastCurrency = o;
40769         
40770         if (this.currencyField) {
40771             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40772         } else {
40773             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40774         }
40775         
40776         this.lastSelectionText = currency;
40777         
40778         //setting default currency
40779         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40780             this.setCurrency(this.defaultCurrency);
40781             return;
40782         }
40783         
40784         this.setCurrency(currency);
40785     },
40786     
40787     setFromData : function(o)
40788     {
40789         var c = {};
40790         
40791         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40792         
40793         this.setFromCurrencyData(c);
40794         
40795         var value = '';
40796         
40797         if (this.name) {
40798             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40799         } else {
40800             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40801         }
40802         
40803         this.setValue(value);
40804         
40805     },
40806     
40807     setCurrency : function(v)
40808     {   
40809         this.currencyValue = v;
40810         
40811         if(this.rendered){
40812             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40813             this.validate();
40814         }
40815     },
40816     
40817     setValue : function(v)
40818     {
40819         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40820         
40821         this.value = v;
40822         
40823         if(this.rendered){
40824             
40825             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40826             
40827             this.inputEl().dom.value = (v == '') ? '' :
40828                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40829             
40830             if(!this.allowZero && v === '0') {
40831                 this.hiddenEl().dom.value = '';
40832                 this.inputEl().dom.value = '';
40833             }
40834             
40835             this.validate();
40836         }
40837     },
40838     
40839     getRawValue : function()
40840     {
40841         var v = this.inputEl().getValue();
40842         
40843         return v;
40844     },
40845     
40846     getValue : function()
40847     {
40848         return this.fixPrecision(this.parseValue(this.getRawValue()));
40849     },
40850     
40851     parseValue : function(value)
40852     {
40853         if(this.thousandsDelimiter) {
40854             value += "";
40855             r = new RegExp(",", "g");
40856             value = value.replace(r, "");
40857         }
40858         
40859         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40860         return isNaN(value) ? '' : value;
40861         
40862     },
40863     
40864     fixPrecision : function(value)
40865     {
40866         if(this.thousandsDelimiter) {
40867             value += "";
40868             r = new RegExp(",", "g");
40869             value = value.replace(r, "");
40870         }
40871         
40872         var nan = isNaN(value);
40873         
40874         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40875             return nan ? '' : value;
40876         }
40877         return parseFloat(value).toFixed(this.decimalPrecision);
40878     },
40879     
40880     decimalPrecisionFcn : function(v)
40881     {
40882         return Math.floor(v);
40883     },
40884     
40885     validateValue : function(value)
40886     {
40887         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40888             return false;
40889         }
40890         
40891         var num = this.parseValue(value);
40892         
40893         if(isNaN(num)){
40894             this.markInvalid(String.format(this.nanText, value));
40895             return false;
40896         }
40897         
40898         if(num < this.minValue){
40899             this.markInvalid(String.format(this.minText, this.minValue));
40900             return false;
40901         }
40902         
40903         if(num > this.maxValue){
40904             this.markInvalid(String.format(this.maxText, this.maxValue));
40905             return false;
40906         }
40907         
40908         return true;
40909     },
40910     
40911     validate : function()
40912     {
40913         if(this.disabled || this.allowBlank){
40914             this.markValid();
40915             return true;
40916         }
40917         
40918         var currency = this.getCurrency();
40919         
40920         if(this.validateValue(this.getRawValue()) && currency.length){
40921             this.markValid();
40922             return true;
40923         }
40924         
40925         this.markInvalid();
40926         return false;
40927     },
40928     
40929     getName: function()
40930     {
40931         return this.name;
40932     },
40933     
40934     beforeBlur : function()
40935     {
40936         if(!this.castInt){
40937             return;
40938         }
40939         
40940         var v = this.parseValue(this.getRawValue());
40941         
40942         if(v || v == 0){
40943             this.setValue(v);
40944         }
40945     },
40946     
40947     onBlur : function()
40948     {
40949         this.beforeBlur();
40950         
40951         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40952             //this.el.removeClass(this.focusClass);
40953         }
40954         
40955         this.hasFocus = false;
40956         
40957         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40958             this.validate();
40959         }
40960         
40961         var v = this.getValue();
40962         
40963         if(String(v) !== String(this.startValue)){
40964             this.fireEvent('change', this, v, this.startValue);
40965         }
40966         
40967         this.fireEvent("blur", this);
40968     },
40969     
40970     inputEl : function()
40971     {
40972         return this.el.select('.roo-money-amount-input', true).first();
40973     },
40974     
40975     currencyEl : function()
40976     {
40977         return this.el.select('.roo-money-currency-input', true).first();
40978     },
40979     
40980     hiddenEl : function()
40981     {
40982         return this.el.select('input.hidden-number-input',true).first();
40983     }
40984     
40985 });