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
2650     onRender : function(ct, position)
2651     {
2652         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2653
2654         if(!this.el){
2655             var cfg = Roo.apply({},  this.getAutoCreate());
2656             cfg.id = Roo.id();
2657             //if(!cfg.name){
2658             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2659             //}
2660             //if (!cfg.name.length) {
2661             //    delete cfg.name;
2662            // }
2663             if (this.cls) {
2664                 cfg.cls += ' ' + this.cls;
2665             }
2666             if (this.style) {
2667                 cfg.style = this.style;
2668             }
2669             this.el = Roo.get(document.body).createChild(cfg, position);
2670         }
2671         //var type = this.el.dom.type;
2672
2673
2674         if(this.tabIndex !== undefined){
2675             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2676         }
2677
2678         this.dialogEl = this.el.select('.modal-dialog',true).first();
2679         this.bodyEl = this.el.select('.modal-body',true).first();
2680         this.closeEl = this.el.select('.modal-header .close', true).first();
2681         this.headerEl = this.el.select('.modal-header',true).first();
2682         this.titleEl = this.el.select('.modal-title',true).first();
2683         this.footerEl = this.el.select('.modal-footer',true).first();
2684
2685         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2686         
2687         //this.el.addClass("x-dlg-modal");
2688
2689         if (this.buttons.length) {
2690             Roo.each(this.buttons, function(bb) {
2691                 var b = Roo.apply({}, bb);
2692                 b.xns = b.xns || Roo.bootstrap;
2693                 b.xtype = b.xtype || 'Button';
2694                 if (typeof(b.listeners) == 'undefined') {
2695                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2696                 }
2697
2698                 var btn = Roo.factory(b);
2699
2700                 btn.render(this.el.select('.modal-footer div').first());
2701
2702             },this);
2703         }
2704         // render the children.
2705         var nitems = [];
2706
2707         if(typeof(this.items) != 'undefined'){
2708             var items = this.items;
2709             delete this.items;
2710
2711             for(var i =0;i < items.length;i++) {
2712                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2713             }
2714         }
2715
2716         this.items = nitems;
2717
2718         // where are these used - they used to be body/close/footer
2719
2720
2721         this.initEvents();
2722         //this.el.addClass([this.fieldClass, this.cls]);
2723
2724     },
2725
2726     getAutoCreate : function()
2727     {
2728         var bdy = {
2729                 cls : 'modal-body',
2730                 html : this.html || ''
2731         };
2732
2733         var title = {
2734             tag: 'h4',
2735             cls : 'modal-title',
2736             html : this.title
2737         };
2738
2739         if(this.specificTitle){
2740             title = this.title;
2741
2742         };
2743
2744         var header = [];
2745         if (this.allow_close) {
2746             header.push({
2747                 tag: 'button',
2748                 cls : 'close',
2749                 html : '&times'
2750             });
2751         }
2752
2753         header.push(title);
2754
2755         var size = '';
2756
2757         if(this.size.length){
2758             size = 'modal-' + this.size;
2759         }
2760
2761         var modal = {
2762             cls: "modal",
2763              cn : [
2764                 {
2765                     cls: "modal-dialog " + size,
2766                     cn : [
2767                         {
2768                             cls : "modal-content",
2769                             cn : [
2770                                 {
2771                                     cls : 'modal-header',
2772                                     cn : header
2773                                 },
2774                                 bdy,
2775                                 {
2776                                     cls : 'modal-footer',
2777                                     cn : [
2778                                         {
2779                                             tag: 'div',
2780                                             cls: 'btn-' + this.buttonPosition
2781                                         }
2782                                     ]
2783
2784                                 }
2785
2786
2787                             ]
2788
2789                         }
2790                     ]
2791
2792                 }
2793             ]
2794         };
2795
2796         if(this.animate){
2797             modal.cls += ' fade';
2798         }
2799
2800         return modal;
2801
2802     },
2803     getChildContainer : function() {
2804
2805          return this.bodyEl;
2806
2807     },
2808     getButtonContainer : function() {
2809          return this.el.select('.modal-footer div',true).first();
2810
2811     },
2812     initEvents : function()
2813     {
2814         if (this.allow_close) {
2815             this.closeEl.on('click', this.hide, this);
2816         }
2817         Roo.EventManager.onWindowResize(this.resize, this, true);
2818
2819
2820     },
2821
2822     resize : function()
2823     {
2824         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2825         
2826         if (this.fitwindow) {
2827             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2828             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2829             this.setSize(w,h);
2830         }
2831         
2832         if(!this.fitwindow && this.max_width !== 0){
2833             
2834             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2835             
2836             Roo.log(this.height);
2837             
2838             if(this.height) {
2839                 this.setSize(w, this.height);
2840                 return;
2841             }
2842             
2843             var view_height = Roo.lib.Dom.getViewportHeight(true) - 60;
2844             
2845             var body_childs = this.bodyEl.dom.childNodes;
2846             var body_height = 0;
2847             for(var i = 0; i < body_childs.length; i++) {
2848                 body_height += body_childs[i].offsetHeight;
2849             }
2850             
2851             // this.setSize(w, this.height || view_height);
2852             
2853             
2854             
2855             // 
2856             // if(
2857             //     (
2858             //         this.headerEl.getHeight() + 
2859             //         this.bodyEl.getHeight() + 
2860             //         this.footerEl.getHeight()
2861             //     ) > view_height) {
2862             // } {
2863             //     this.setSize(w,view_height);
2864             // }
2865         }
2866         
2867     },
2868
2869     setSize : function(w,h)
2870     {
2871         if (!w && !h) {
2872             return;
2873         }
2874         this.resizeTo(w,h);
2875     },
2876
2877     show : function() {
2878
2879         if (!this.rendered) {
2880             this.render();
2881         }
2882
2883         //this.el.setStyle('display', 'block');
2884         this.el.removeClass('hideing');        
2885         this.el.addClass('show');
2886  
2887         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2888             var _this = this;
2889             (function(){
2890                 this.el.addClass('in');
2891             }).defer(50, this);
2892         }else{
2893             this.el.addClass('in');
2894         }
2895
2896         // not sure how we can show data in here..
2897         //if (this.tmpl) {
2898         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2899         //}
2900
2901         Roo.get(document.body).addClass("x-body-masked");
2902         
2903         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2904         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2905         this.maskEl.addClass('show');
2906         
2907         this.resize();
2908         
2909         this.fireEvent('show', this);
2910
2911         // set zindex here - otherwise it appears to be ignored...
2912         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2913
2914         (function () {
2915             this.items.forEach( function(e) {
2916                 e.layout ? e.layout() : false;
2917
2918             });
2919         }).defer(100,this);
2920
2921     },
2922     hide : function()
2923     {
2924         if(this.fireEvent("beforehide", this) !== false){
2925             this.maskEl.removeClass('show');
2926             Roo.get(document.body).removeClass("x-body-masked");
2927             this.el.removeClass('in');
2928             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2929
2930             if(this.animate){ // why
2931                 this.el.addClass('hideing');
2932                 (function(){
2933                     if (!this.el.hasClass('hideing')) {
2934                         return; // it's been shown again...
2935                     }
2936                     this.el.removeClass('show');
2937                     this.el.removeClass('hideing');
2938                 }).defer(150,this);
2939                 
2940             }else{
2941                  this.el.removeClass('show');
2942             }
2943             this.fireEvent('hide', this);
2944         }
2945     },
2946     isVisible : function()
2947     {
2948         
2949         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2950         
2951     },
2952
2953     addButton : function(str, cb)
2954     {
2955
2956
2957         var b = Roo.apply({}, { html : str } );
2958         b.xns = b.xns || Roo.bootstrap;
2959         b.xtype = b.xtype || 'Button';
2960         if (typeof(b.listeners) == 'undefined') {
2961             b.listeners = { click : cb.createDelegate(this)  };
2962         }
2963
2964         var btn = Roo.factory(b);
2965
2966         btn.render(this.el.select('.modal-footer div').first());
2967
2968         return btn;
2969
2970     },
2971
2972     setDefaultButton : function(btn)
2973     {
2974         //this.el.select('.modal-footer').()
2975     },
2976     diff : false,
2977
2978     resizeTo: function(w,h)
2979     {
2980         // skip.. ?? why??
2981
2982         this.dialogEl.setWidth(w);
2983         if (this.diff === false) {
2984             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2985         }
2986
2987         this.bodyEl.setHeight(h-this.diff);
2988
2989         this.fireEvent('resize', this);
2990
2991     },
2992     setContentSize  : function(w, h)
2993     {
2994
2995     },
2996     onButtonClick: function(btn,e)
2997     {
2998         //Roo.log([a,b,c]);
2999         this.fireEvent('btnclick', btn.name, e);
3000     },
3001      /**
3002      * Set the title of the Dialog
3003      * @param {String} str new Title
3004      */
3005     setTitle: function(str) {
3006         this.titleEl.dom.innerHTML = str;
3007     },
3008     /**
3009      * Set the body of the Dialog
3010      * @param {String} str new Title
3011      */
3012     setBody: function(str) {
3013         this.bodyEl.dom.innerHTML = str;
3014     },
3015     /**
3016      * Set the body of the Dialog using the template
3017      * @param {Obj} data - apply this data to the template and replace the body contents.
3018      */
3019     applyBody: function(obj)
3020     {
3021         if (!this.tmpl) {
3022             Roo.log("Error - using apply Body without a template");
3023             //code
3024         }
3025         this.tmpl.overwrite(this.bodyEl, obj);
3026     }
3027
3028 });
3029
3030
3031 Roo.apply(Roo.bootstrap.Modal,  {
3032     /**
3033          * Button config that displays a single OK button
3034          * @type Object
3035          */
3036         OK :  [{
3037             name : 'ok',
3038             weight : 'primary',
3039             html : 'OK'
3040         }],
3041         /**
3042          * Button config that displays Yes and No buttons
3043          * @type Object
3044          */
3045         YESNO : [
3046             {
3047                 name  : 'no',
3048                 html : 'No'
3049             },
3050             {
3051                 name  :'yes',
3052                 weight : 'primary',
3053                 html : 'Yes'
3054             }
3055         ],
3056
3057         /**
3058          * Button config that displays OK and Cancel buttons
3059          * @type Object
3060          */
3061         OKCANCEL : [
3062             {
3063                name : 'cancel',
3064                 html : 'Cancel'
3065             },
3066             {
3067                 name : 'ok',
3068                 weight : 'primary',
3069                 html : 'OK'
3070             }
3071         ],
3072         /**
3073          * Button config that displays Yes, No and Cancel buttons
3074          * @type Object
3075          */
3076         YESNOCANCEL : [
3077             {
3078                 name : 'yes',
3079                 weight : 'primary',
3080                 html : 'Yes'
3081             },
3082             {
3083                 name : 'no',
3084                 html : 'No'
3085             },
3086             {
3087                 name : 'cancel',
3088                 html : 'Cancel'
3089             }
3090         ],
3091         
3092         zIndex : 10001
3093 });
3094 /*
3095  * - LGPL
3096  *
3097  * messagebox - can be used as a replace
3098  * 
3099  */
3100 /**
3101  * @class Roo.MessageBox
3102  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3103  * Example usage:
3104  *<pre><code>
3105 // Basic alert:
3106 Roo.Msg.alert('Status', 'Changes saved successfully.');
3107
3108 // Prompt for user data:
3109 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3110     if (btn == 'ok'){
3111         // process text value...
3112     }
3113 });
3114
3115 // Show a dialog using config options:
3116 Roo.Msg.show({
3117    title:'Save Changes?',
3118    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3119    buttons: Roo.Msg.YESNOCANCEL,
3120    fn: processResult,
3121    animEl: 'elId'
3122 });
3123 </code></pre>
3124  * @singleton
3125  */
3126 Roo.bootstrap.MessageBox = function(){
3127     var dlg, opt, mask, waitTimer;
3128     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3129     var buttons, activeTextEl, bwidth;
3130
3131     
3132     // private
3133     var handleButton = function(button){
3134         dlg.hide();
3135         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3136     };
3137
3138     // private
3139     var handleHide = function(){
3140         if(opt && opt.cls){
3141             dlg.el.removeClass(opt.cls);
3142         }
3143         //if(waitTimer){
3144         //    Roo.TaskMgr.stop(waitTimer);
3145         //    waitTimer = null;
3146         //}
3147     };
3148
3149     // private
3150     var updateButtons = function(b){
3151         var width = 0;
3152         if(!b){
3153             buttons["ok"].hide();
3154             buttons["cancel"].hide();
3155             buttons["yes"].hide();
3156             buttons["no"].hide();
3157             //dlg.footer.dom.style.display = 'none';
3158             return width;
3159         }
3160         dlg.footerEl.dom.style.display = '';
3161         for(var k in buttons){
3162             if(typeof buttons[k] != "function"){
3163                 if(b[k]){
3164                     buttons[k].show();
3165                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3166                     width += buttons[k].el.getWidth()+15;
3167                 }else{
3168                     buttons[k].hide();
3169                 }
3170             }
3171         }
3172         return width;
3173     };
3174
3175     // private
3176     var handleEsc = function(d, k, e){
3177         if(opt && opt.closable !== false){
3178             dlg.hide();
3179         }
3180         if(e){
3181             e.stopEvent();
3182         }
3183     };
3184
3185     return {
3186         /**
3187          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3188          * @return {Roo.BasicDialog} The BasicDialog element
3189          */
3190         getDialog : function(){
3191            if(!dlg){
3192                 dlg = new Roo.bootstrap.Modal( {
3193                     //draggable: true,
3194                     //resizable:false,
3195                     //constraintoviewport:false,
3196                     //fixedcenter:true,
3197                     //collapsible : false,
3198                     //shim:true,
3199                     //modal: true,
3200                 //    width: 'auto',
3201                   //  height:100,
3202                     //buttonAlign:"center",
3203                     closeClick : function(){
3204                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3205                             handleButton("no");
3206                         }else{
3207                             handleButton("cancel");
3208                         }
3209                     }
3210                 });
3211                 dlg.render();
3212                 dlg.on("hide", handleHide);
3213                 mask = dlg.mask;
3214                 //dlg.addKeyListener(27, handleEsc);
3215                 buttons = {};
3216                 this.buttons = buttons;
3217                 var bt = this.buttonText;
3218                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3219                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3220                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3221                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3222                 //Roo.log(buttons);
3223                 bodyEl = dlg.bodyEl.createChild({
3224
3225                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3226                         '<textarea class="roo-mb-textarea"></textarea>' +
3227                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3228                 });
3229                 msgEl = bodyEl.dom.firstChild;
3230                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3231                 textboxEl.enableDisplayMode();
3232                 textboxEl.addKeyListener([10,13], function(){
3233                     if(dlg.isVisible() && opt && opt.buttons){
3234                         if(opt.buttons.ok){
3235                             handleButton("ok");
3236                         }else if(opt.buttons.yes){
3237                             handleButton("yes");
3238                         }
3239                     }
3240                 });
3241                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3242                 textareaEl.enableDisplayMode();
3243                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3244                 progressEl.enableDisplayMode();
3245                 
3246                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3247                 var pf = progressEl.dom.firstChild;
3248                 if (pf) {
3249                     pp = Roo.get(pf.firstChild);
3250                     pp.setHeight(pf.offsetHeight);
3251                 }
3252                 
3253             }
3254             return dlg;
3255         },
3256
3257         /**
3258          * Updates the message box body text
3259          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3260          * the XHTML-compliant non-breaking space character '&amp;#160;')
3261          * @return {Roo.MessageBox} This message box
3262          */
3263         updateText : function(text)
3264         {
3265             if(!dlg.isVisible() && !opt.width){
3266                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3267                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3268             }
3269             msgEl.innerHTML = text || '&#160;';
3270       
3271             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3272             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3273             var w = Math.max(
3274                     Math.min(opt.width || cw , this.maxWidth), 
3275                     Math.max(opt.minWidth || this.minWidth, bwidth)
3276             );
3277             if(opt.prompt){
3278                 activeTextEl.setWidth(w);
3279             }
3280             if(dlg.isVisible()){
3281                 dlg.fixedcenter = false;
3282             }
3283             // to big, make it scroll. = But as usual stupid IE does not support
3284             // !important..
3285             
3286             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3287                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3288                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3289             } else {
3290                 bodyEl.dom.style.height = '';
3291                 bodyEl.dom.style.overflowY = '';
3292             }
3293             if (cw > w) {
3294                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3295             } else {
3296                 bodyEl.dom.style.overflowX = '';
3297             }
3298             
3299             dlg.setContentSize(w, bodyEl.getHeight());
3300             if(dlg.isVisible()){
3301                 dlg.fixedcenter = true;
3302             }
3303             return this;
3304         },
3305
3306         /**
3307          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3308          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3309          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3310          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3311          * @return {Roo.MessageBox} This message box
3312          */
3313         updateProgress : function(value, text){
3314             if(text){
3315                 this.updateText(text);
3316             }
3317             
3318             if (pp) { // weird bug on my firefox - for some reason this is not defined
3319                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3320                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3321             }
3322             return this;
3323         },        
3324
3325         /**
3326          * Returns true if the message box is currently displayed
3327          * @return {Boolean} True if the message box is visible, else false
3328          */
3329         isVisible : function(){
3330             return dlg && dlg.isVisible();  
3331         },
3332
3333         /**
3334          * Hides the message box if it is displayed
3335          */
3336         hide : function(){
3337             if(this.isVisible()){
3338                 dlg.hide();
3339             }  
3340         },
3341
3342         /**
3343          * Displays a new message box, or reinitializes an existing message box, based on the config options
3344          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3345          * The following config object properties are supported:
3346          * <pre>
3347 Property    Type             Description
3348 ----------  ---------------  ------------------------------------------------------------------------------------
3349 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3350                                    closes (defaults to undefined)
3351 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3352                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3353 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3354                                    progress and wait dialogs will ignore this property and always hide the
3355                                    close button as they can only be closed programmatically.
3356 cls               String           A custom CSS class to apply to the message box element
3357 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3358                                    displayed (defaults to 75)
3359 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3360                                    function will be btn (the name of the button that was clicked, if applicable,
3361                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3362                                    Progress and wait dialogs will ignore this option since they do not respond to
3363                                    user actions and can only be closed programmatically, so any required function
3364                                    should be called by the same code after it closes the dialog.
3365 icon              String           A CSS class that provides a background image to be used as an icon for
3366                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3367 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3368 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3369 modal             Boolean          False to allow user interaction with the page while the message box is
3370                                    displayed (defaults to true)
3371 msg               String           A string that will replace the existing message box body text (defaults
3372                                    to the XHTML-compliant non-breaking space character '&#160;')
3373 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3374 progress          Boolean          True to display a progress bar (defaults to false)
3375 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3376 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3377 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3378 title             String           The title text
3379 value             String           The string value to set into the active textbox element if displayed
3380 wait              Boolean          True to display a progress bar (defaults to false)
3381 width             Number           The width of the dialog in pixels
3382 </pre>
3383          *
3384          * Example usage:
3385          * <pre><code>
3386 Roo.Msg.show({
3387    title: 'Address',
3388    msg: 'Please enter your address:',
3389    width: 300,
3390    buttons: Roo.MessageBox.OKCANCEL,
3391    multiline: true,
3392    fn: saveAddress,
3393    animEl: 'addAddressBtn'
3394 });
3395 </code></pre>
3396          * @param {Object} config Configuration options
3397          * @return {Roo.MessageBox} This message box
3398          */
3399         show : function(options)
3400         {
3401             
3402             // this causes nightmares if you show one dialog after another
3403             // especially on callbacks..
3404              
3405             if(this.isVisible()){
3406                 
3407                 this.hide();
3408                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3409                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3410                 Roo.log("New Dialog Message:" +  options.msg )
3411                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3412                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3413                 
3414             }
3415             var d = this.getDialog();
3416             opt = options;
3417             d.setTitle(opt.title || "&#160;");
3418             d.closeEl.setDisplayed(opt.closable !== false);
3419             activeTextEl = textboxEl;
3420             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3421             if(opt.prompt){
3422                 if(opt.multiline){
3423                     textboxEl.hide();
3424                     textareaEl.show();
3425                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3426                         opt.multiline : this.defaultTextHeight);
3427                     activeTextEl = textareaEl;
3428                 }else{
3429                     textboxEl.show();
3430                     textareaEl.hide();
3431                 }
3432             }else{
3433                 textboxEl.hide();
3434                 textareaEl.hide();
3435             }
3436             progressEl.setDisplayed(opt.progress === true);
3437             this.updateProgress(0);
3438             activeTextEl.dom.value = opt.value || "";
3439             if(opt.prompt){
3440                 dlg.setDefaultButton(activeTextEl);
3441             }else{
3442                 var bs = opt.buttons;
3443                 var db = null;
3444                 if(bs && bs.ok){
3445                     db = buttons["ok"];
3446                 }else if(bs && bs.yes){
3447                     db = buttons["yes"];
3448                 }
3449                 dlg.setDefaultButton(db);
3450             }
3451             bwidth = updateButtons(opt.buttons);
3452             this.updateText(opt.msg);
3453             if(opt.cls){
3454                 d.el.addClass(opt.cls);
3455             }
3456             d.proxyDrag = opt.proxyDrag === true;
3457             d.modal = opt.modal !== false;
3458             d.mask = opt.modal !== false ? mask : false;
3459             if(!d.isVisible()){
3460                 // force it to the end of the z-index stack so it gets a cursor in FF
3461                 document.body.appendChild(dlg.el.dom);
3462                 d.animateTarget = null;
3463                 d.show(options.animEl);
3464             }
3465             return this;
3466         },
3467
3468         /**
3469          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3470          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3471          * and closing the message box when the process is complete.
3472          * @param {String} title The title bar text
3473          * @param {String} msg The message box body text
3474          * @return {Roo.MessageBox} This message box
3475          */
3476         progress : function(title, msg){
3477             this.show({
3478                 title : title,
3479                 msg : msg,
3480                 buttons: false,
3481                 progress:true,
3482                 closable:false,
3483                 minWidth: this.minProgressWidth,
3484                 modal : true
3485             });
3486             return this;
3487         },
3488
3489         /**
3490          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3491          * If a callback function is passed it will be called after the user clicks the button, and the
3492          * id of the button that was clicked will be passed as the only parameter to the callback
3493          * (could also be the top-right close button).
3494          * @param {String} title The title bar text
3495          * @param {String} msg The message box body text
3496          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3497          * @param {Object} scope (optional) The scope of the callback function
3498          * @return {Roo.MessageBox} This message box
3499          */
3500         alert : function(title, msg, fn, scope)
3501         {
3502             this.show({
3503                 title : title,
3504                 msg : msg,
3505                 buttons: this.OK,
3506                 fn: fn,
3507                 closable : false,
3508                 scope : scope,
3509                 modal : true
3510             });
3511             return this;
3512         },
3513
3514         /**
3515          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3516          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3517          * You are responsible for closing the message box when the process is complete.
3518          * @param {String} msg The message box body text
3519          * @param {String} title (optional) The title bar text
3520          * @return {Roo.MessageBox} This message box
3521          */
3522         wait : function(msg, title){
3523             this.show({
3524                 title : title,
3525                 msg : msg,
3526                 buttons: false,
3527                 closable:false,
3528                 progress:true,
3529                 modal:true,
3530                 width:300,
3531                 wait:true
3532             });
3533             waitTimer = Roo.TaskMgr.start({
3534                 run: function(i){
3535                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3536                 },
3537                 interval: 1000
3538             });
3539             return this;
3540         },
3541
3542         /**
3543          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3544          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3545          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3546          * @param {String} title The title bar text
3547          * @param {String} msg The message box body text
3548          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3549          * @param {Object} scope (optional) The scope of the callback function
3550          * @return {Roo.MessageBox} This message box
3551          */
3552         confirm : function(title, msg, fn, scope){
3553             this.show({
3554                 title : title,
3555                 msg : msg,
3556                 buttons: this.YESNO,
3557                 fn: fn,
3558                 scope : scope,
3559                 modal : true
3560             });
3561             return this;
3562         },
3563
3564         /**
3565          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3566          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3567          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3568          * (could also be the top-right close button) and the text that was entered will be passed as the two
3569          * parameters to the callback.
3570          * @param {String} title The title bar text
3571          * @param {String} msg The message box body text
3572          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3573          * @param {Object} scope (optional) The scope of the callback function
3574          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3575          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3576          * @return {Roo.MessageBox} This message box
3577          */
3578         prompt : function(title, msg, fn, scope, multiline){
3579             this.show({
3580                 title : title,
3581                 msg : msg,
3582                 buttons: this.OKCANCEL,
3583                 fn: fn,
3584                 minWidth:250,
3585                 scope : scope,
3586                 prompt:true,
3587                 multiline: multiline,
3588                 modal : true
3589             });
3590             return this;
3591         },
3592
3593         /**
3594          * Button config that displays a single OK button
3595          * @type Object
3596          */
3597         OK : {ok:true},
3598         /**
3599          * Button config that displays Yes and No buttons
3600          * @type Object
3601          */
3602         YESNO : {yes:true, no:true},
3603         /**
3604          * Button config that displays OK and Cancel buttons
3605          * @type Object
3606          */
3607         OKCANCEL : {ok:true, cancel:true},
3608         /**
3609          * Button config that displays Yes, No and Cancel buttons
3610          * @type Object
3611          */
3612         YESNOCANCEL : {yes:true, no:true, cancel:true},
3613
3614         /**
3615          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3616          * @type Number
3617          */
3618         defaultTextHeight : 75,
3619         /**
3620          * The maximum width in pixels of the message box (defaults to 600)
3621          * @type Number
3622          */
3623         maxWidth : 600,
3624         /**
3625          * The minimum width in pixels of the message box (defaults to 100)
3626          * @type Number
3627          */
3628         minWidth : 100,
3629         /**
3630          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3631          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3632          * @type Number
3633          */
3634         minProgressWidth : 250,
3635         /**
3636          * An object containing the default button text strings that can be overriden for localized language support.
3637          * Supported properties are: ok, cancel, yes and no.
3638          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3639          * @type Object
3640          */
3641         buttonText : {
3642             ok : "OK",
3643             cancel : "Cancel",
3644             yes : "Yes",
3645             no : "No"
3646         }
3647     };
3648 }();
3649
3650 /**
3651  * Shorthand for {@link Roo.MessageBox}
3652  */
3653 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3654 Roo.Msg = Roo.Msg || Roo.MessageBox;
3655 /*
3656  * - LGPL
3657  *
3658  * navbar
3659  * 
3660  */
3661
3662 /**
3663  * @class Roo.bootstrap.Navbar
3664  * @extends Roo.bootstrap.Component
3665  * Bootstrap Navbar class
3666
3667  * @constructor
3668  * Create a new Navbar
3669  * @param {Object} config The config object
3670  */
3671
3672
3673 Roo.bootstrap.Navbar = function(config){
3674     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3675     this.addEvents({
3676         // raw events
3677         /**
3678          * @event beforetoggle
3679          * Fire before toggle the menu
3680          * @param {Roo.EventObject} e
3681          */
3682         "beforetoggle" : true
3683     });
3684 };
3685
3686 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3687     
3688     
3689    
3690     // private
3691     navItems : false,
3692     loadMask : false,
3693     
3694     
3695     getAutoCreate : function(){
3696         
3697         
3698         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3699         
3700     },
3701     
3702     initEvents :function ()
3703     {
3704         //Roo.log(this.el.select('.navbar-toggle',true));
3705         this.el.select('.navbar-toggle',true).on('click', function() {
3706             if(this.fireEvent('beforetoggle', this) !== false){
3707                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3708             }
3709             
3710         }, this);
3711         
3712         var mark = {
3713             tag: "div",
3714             cls:"x-dlg-mask"
3715         };
3716         
3717         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3718         
3719         var size = this.el.getSize();
3720         this.maskEl.setSize(size.width, size.height);
3721         this.maskEl.enableDisplayMode("block");
3722         this.maskEl.hide();
3723         
3724         if(this.loadMask){
3725             this.maskEl.show();
3726         }
3727     },
3728     
3729     
3730     getChildContainer : function()
3731     {
3732         if (this.el.select('.collapse').getCount()) {
3733             return this.el.select('.collapse',true).first();
3734         }
3735         
3736         return this.el;
3737     },
3738     
3739     mask : function()
3740     {
3741         this.maskEl.show();
3742     },
3743     
3744     unmask : function()
3745     {
3746         this.maskEl.hide();
3747     } 
3748     
3749     
3750     
3751     
3752 });
3753
3754
3755
3756  
3757
3758  /*
3759  * - LGPL
3760  *
3761  * navbar
3762  * 
3763  */
3764
3765 /**
3766  * @class Roo.bootstrap.NavSimplebar
3767  * @extends Roo.bootstrap.Navbar
3768  * Bootstrap Sidebar class
3769  *
3770  * @cfg {Boolean} inverse is inverted color
3771  * 
3772  * @cfg {String} type (nav | pills | tabs)
3773  * @cfg {Boolean} arrangement stacked | justified
3774  * @cfg {String} align (left | right) alignment
3775  * 
3776  * @cfg {Boolean} main (true|false) main nav bar? default false
3777  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3778  * 
3779  * @cfg {String} tag (header|footer|nav|div) default is nav 
3780
3781  * 
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new Sidebar
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.NavSimplebar = function(config){
3791     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3792 };
3793
3794 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3795     
3796     inverse: false,
3797     
3798     type: false,
3799     arrangement: '',
3800     align : false,
3801     
3802     
3803     
3804     main : false,
3805     
3806     
3807     tag : false,
3808     
3809     
3810     getAutoCreate : function(){
3811         
3812         
3813         var cfg = {
3814             tag : this.tag || 'div',
3815             cls : 'navbar'
3816         };
3817           
3818         
3819         cfg.cn = [
3820             {
3821                 cls: 'nav',
3822                 tag : 'ul'
3823             }
3824         ];
3825         
3826          
3827         this.type = this.type || 'nav';
3828         if (['tabs','pills'].indexOf(this.type)!==-1) {
3829             cfg.cn[0].cls += ' nav-' + this.type
3830         
3831         
3832         } else {
3833             if (this.type!=='nav') {
3834                 Roo.log('nav type must be nav/tabs/pills')
3835             }
3836             cfg.cn[0].cls += ' navbar-nav'
3837         }
3838         
3839         
3840         
3841         
3842         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3843             cfg.cn[0].cls += ' nav-' + this.arrangement;
3844         }
3845         
3846         
3847         if (this.align === 'right') {
3848             cfg.cn[0].cls += ' navbar-right';
3849         }
3850         
3851         if (this.inverse) {
3852             cfg.cls += ' navbar-inverse';
3853             
3854         }
3855         
3856         
3857         return cfg;
3858     
3859         
3860     }
3861     
3862     
3863     
3864 });
3865
3866
3867
3868  
3869
3870  
3871        /*
3872  * - LGPL
3873  *
3874  * navbar
3875  * 
3876  */
3877
3878 /**
3879  * @class Roo.bootstrap.NavHeaderbar
3880  * @extends Roo.bootstrap.NavSimplebar
3881  * Bootstrap Sidebar class
3882  *
3883  * @cfg {String} brand what is brand
3884  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3885  * @cfg {String} brand_href href of the brand
3886  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3887  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3888  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3889  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3890  * 
3891  * @constructor
3892  * Create a new Sidebar
3893  * @param {Object} config The config object
3894  */
3895
3896
3897 Roo.bootstrap.NavHeaderbar = function(config){
3898     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3899       
3900 };
3901
3902 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3903     
3904     position: '',
3905     brand: '',
3906     brand_href: false,
3907     srButton : true,
3908     autohide : false,
3909     desktopCenter : false,
3910    
3911     
3912     getAutoCreate : function(){
3913         
3914         var   cfg = {
3915             tag: this.nav || 'nav',
3916             cls: 'navbar',
3917             role: 'navigation',
3918             cn: []
3919         };
3920         
3921         var cn = cfg.cn;
3922         if (this.desktopCenter) {
3923             cn.push({cls : 'container', cn : []});
3924             cn = cn[0].cn;
3925         }
3926         
3927         if(this.srButton){
3928             cn.push({
3929                 tag: 'div',
3930                 cls: 'navbar-header',
3931                 cn: [
3932                     {
3933                         tag: 'button',
3934                         type: 'button',
3935                         cls: 'navbar-toggle',
3936                         'data-toggle': 'collapse',
3937                         cn: [
3938                             {
3939                                 tag: 'span',
3940                                 cls: 'sr-only',
3941                                 html: 'Toggle navigation'
3942                             },
3943                             {
3944                                 tag: 'span',
3945                                 cls: 'icon-bar'
3946                             },
3947                             {
3948                                 tag: 'span',
3949                                 cls: 'icon-bar'
3950                             },
3951                             {
3952                                 tag: 'span',
3953                                 cls: 'icon-bar'
3954                             }
3955                         ]
3956                     }
3957                 ]
3958             });
3959         }
3960         
3961         cn.push({
3962             tag: 'div',
3963             cls: 'collapse navbar-collapse',
3964             cn : []
3965         });
3966         
3967         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3968         
3969         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3970             cfg.cls += ' navbar-' + this.position;
3971             
3972             // tag can override this..
3973             
3974             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3975         }
3976         
3977         if (this.brand !== '') {
3978             cn[0].cn.push({
3979                 tag: 'a',
3980                 href: this.brand_href ? this.brand_href : '#',
3981                 cls: 'navbar-brand',
3982                 cn: [
3983                 this.brand
3984                 ]
3985             });
3986         }
3987         
3988         if(this.main){
3989             cfg.cls += ' main-nav';
3990         }
3991         
3992         
3993         return cfg;
3994
3995         
3996     },
3997     getHeaderChildContainer : function()
3998     {
3999         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4000             return this.el.select('.navbar-header',true).first();
4001         }
4002         
4003         return this.getChildContainer();
4004     },
4005     
4006     
4007     initEvents : function()
4008     {
4009         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4010         
4011         if (this.autohide) {
4012             
4013             var prevScroll = 0;
4014             var ft = this.el;
4015             
4016             Roo.get(document).on('scroll',function(e) {
4017                 var ns = Roo.get(document).getScroll().top;
4018                 var os = prevScroll;
4019                 prevScroll = ns;
4020                 
4021                 if(ns > os){
4022                     ft.removeClass('slideDown');
4023                     ft.addClass('slideUp');
4024                     return;
4025                 }
4026                 ft.removeClass('slideUp');
4027                 ft.addClass('slideDown');
4028                  
4029               
4030           },this);
4031         }
4032     }    
4033     
4034 });
4035
4036
4037
4038  
4039
4040  /*
4041  * - LGPL
4042  *
4043  * navbar
4044  * 
4045  */
4046
4047 /**
4048  * @class Roo.bootstrap.NavSidebar
4049  * @extends Roo.bootstrap.Navbar
4050  * Bootstrap Sidebar class
4051  * 
4052  * @constructor
4053  * Create a new Sidebar
4054  * @param {Object} config The config object
4055  */
4056
4057
4058 Roo.bootstrap.NavSidebar = function(config){
4059     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4060 };
4061
4062 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4063     
4064     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4065     
4066     getAutoCreate : function(){
4067         
4068         
4069         return  {
4070             tag: 'div',
4071             cls: 'sidebar sidebar-nav'
4072         };
4073     
4074         
4075     }
4076     
4077     
4078     
4079 });
4080
4081
4082
4083  
4084
4085  /*
4086  * - LGPL
4087  *
4088  * nav group
4089  * 
4090  */
4091
4092 /**
4093  * @class Roo.bootstrap.NavGroup
4094  * @extends Roo.bootstrap.Component
4095  * Bootstrap NavGroup class
4096  * @cfg {String} align (left|right)
4097  * @cfg {Boolean} inverse
4098  * @cfg {String} type (nav|pills|tab) default nav
4099  * @cfg {String} navId - reference Id for navbar.
4100
4101  * 
4102  * @constructor
4103  * Create a new nav group
4104  * @param {Object} config The config object
4105  */
4106
4107 Roo.bootstrap.NavGroup = function(config){
4108     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4109     this.navItems = [];
4110    
4111     Roo.bootstrap.NavGroup.register(this);
4112      this.addEvents({
4113         /**
4114              * @event changed
4115              * Fires when the active item changes
4116              * @param {Roo.bootstrap.NavGroup} this
4117              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4118              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4119          */
4120         'changed': true
4121      });
4122     
4123 };
4124
4125 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4126     
4127     align: '',
4128     inverse: false,
4129     form: false,
4130     type: 'nav',
4131     navId : '',
4132     // private
4133     
4134     navItems : false, 
4135     
4136     getAutoCreate : function()
4137     {
4138         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4139         
4140         cfg = {
4141             tag : 'ul',
4142             cls: 'nav' 
4143         };
4144         
4145         if (['tabs','pills'].indexOf(this.type)!==-1) {
4146             cfg.cls += ' nav-' + this.type
4147         } else {
4148             if (this.type!=='nav') {
4149                 Roo.log('nav type must be nav/tabs/pills')
4150             }
4151             cfg.cls += ' navbar-nav'
4152         }
4153         
4154         if (this.parent() && this.parent().sidebar) {
4155             cfg = {
4156                 tag: 'ul',
4157                 cls: 'dashboard-menu sidebar-menu'
4158             };
4159             
4160             return cfg;
4161         }
4162         
4163         if (this.form === true) {
4164             cfg = {
4165                 tag: 'form',
4166                 cls: 'navbar-form'
4167             };
4168             
4169             if (this.align === 'right') {
4170                 cfg.cls += ' navbar-right';
4171             } else {
4172                 cfg.cls += ' navbar-left';
4173             }
4174         }
4175         
4176         if (this.align === 'right') {
4177             cfg.cls += ' navbar-right';
4178         }
4179         
4180         if (this.inverse) {
4181             cfg.cls += ' navbar-inverse';
4182             
4183         }
4184         
4185         
4186         return cfg;
4187     },
4188     /**
4189     * sets the active Navigation item
4190     * @param {Roo.bootstrap.NavItem} the new current navitem
4191     */
4192     setActiveItem : function(item)
4193     {
4194         var prev = false;
4195         Roo.each(this.navItems, function(v){
4196             if (v == item) {
4197                 return ;
4198             }
4199             if (v.isActive()) {
4200                 v.setActive(false, true);
4201                 prev = v;
4202                 
4203             }
4204             
4205         });
4206
4207         item.setActive(true, true);
4208         this.fireEvent('changed', this, item, prev);
4209         
4210         
4211     },
4212     /**
4213     * gets the active Navigation item
4214     * @return {Roo.bootstrap.NavItem} the current navitem
4215     */
4216     getActive : function()
4217     {
4218         
4219         var prev = false;
4220         Roo.each(this.navItems, function(v){
4221             
4222             if (v.isActive()) {
4223                 prev = v;
4224                 
4225             }
4226             
4227         });
4228         return prev;
4229     },
4230     
4231     indexOfNav : function()
4232     {
4233         
4234         var prev = false;
4235         Roo.each(this.navItems, function(v,i){
4236             
4237             if (v.isActive()) {
4238                 prev = i;
4239                 
4240             }
4241             
4242         });
4243         return prev;
4244     },
4245     /**
4246     * adds a Navigation item
4247     * @param {Roo.bootstrap.NavItem} the navitem to add
4248     */
4249     addItem : function(cfg)
4250     {
4251         var cn = new Roo.bootstrap.NavItem(cfg);
4252         this.register(cn);
4253         cn.parentId = this.id;
4254         cn.onRender(this.el, null);
4255         return cn;
4256     },
4257     /**
4258     * register a Navigation item
4259     * @param {Roo.bootstrap.NavItem} the navitem to add
4260     */
4261     register : function(item)
4262     {
4263         this.navItems.push( item);
4264         item.navId = this.navId;
4265     
4266     },
4267     
4268     /**
4269     * clear all the Navigation item
4270     */
4271    
4272     clearAll : function()
4273     {
4274         this.navItems = [];
4275         this.el.dom.innerHTML = '';
4276     },
4277     
4278     getNavItem: function(tabId)
4279     {
4280         var ret = false;
4281         Roo.each(this.navItems, function(e) {
4282             if (e.tabId == tabId) {
4283                ret =  e;
4284                return false;
4285             }
4286             return true;
4287             
4288         });
4289         return ret;
4290     },
4291     
4292     setActiveNext : function()
4293     {
4294         var i = this.indexOfNav(this.getActive());
4295         if (i > this.navItems.length) {
4296             return;
4297         }
4298         this.setActiveItem(this.navItems[i+1]);
4299     },
4300     setActivePrev : function()
4301     {
4302         var i = this.indexOfNav(this.getActive());
4303         if (i  < 1) {
4304             return;
4305         }
4306         this.setActiveItem(this.navItems[i-1]);
4307     },
4308     clearWasActive : function(except) {
4309         Roo.each(this.navItems, function(e) {
4310             if (e.tabId != except.tabId && e.was_active) {
4311                e.was_active = false;
4312                return false;
4313             }
4314             return true;
4315             
4316         });
4317     },
4318     getWasActive : function ()
4319     {
4320         var r = false;
4321         Roo.each(this.navItems, function(e) {
4322             if (e.was_active) {
4323                r = e;
4324                return false;
4325             }
4326             return true;
4327             
4328         });
4329         return r;
4330     }
4331     
4332     
4333 });
4334
4335  
4336 Roo.apply(Roo.bootstrap.NavGroup, {
4337     
4338     groups: {},
4339      /**
4340     * register a Navigation Group
4341     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4342     */
4343     register : function(navgrp)
4344     {
4345         this.groups[navgrp.navId] = navgrp;
4346         
4347     },
4348     /**
4349     * fetch a Navigation Group based on the navigation ID
4350     * @param {string} the navgroup to add
4351     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4352     */
4353     get: function(navId) {
4354         if (typeof(this.groups[navId]) == 'undefined') {
4355             return false;
4356             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4357         }
4358         return this.groups[navId] ;
4359     }
4360     
4361     
4362     
4363 });
4364
4365  /*
4366  * - LGPL
4367  *
4368  * row
4369  * 
4370  */
4371
4372 /**
4373  * @class Roo.bootstrap.NavItem
4374  * @extends Roo.bootstrap.Component
4375  * Bootstrap Navbar.NavItem class
4376  * @cfg {String} href  link to
4377  * @cfg {String} html content of button
4378  * @cfg {String} badge text inside badge
4379  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4380  * @cfg {String} glyphicon name of glyphicon
4381  * @cfg {String} icon name of font awesome icon
4382  * @cfg {Boolean} active Is item active
4383  * @cfg {Boolean} disabled Is item disabled
4384  
4385  * @cfg {Boolean} preventDefault (true | false) default false
4386  * @cfg {String} tabId the tab that this item activates.
4387  * @cfg {String} tagtype (a|span) render as a href or span?
4388  * @cfg {Boolean} animateRef (true|false) link to element default false  
4389   
4390  * @constructor
4391  * Create a new Navbar Item
4392  * @param {Object} config The config object
4393  */
4394 Roo.bootstrap.NavItem = function(config){
4395     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4396     this.addEvents({
4397         // raw events
4398         /**
4399          * @event click
4400          * The raw click event for the entire grid.
4401          * @param {Roo.EventObject} e
4402          */
4403         "click" : true,
4404          /**
4405             * @event changed
4406             * Fires when the active item active state changes
4407             * @param {Roo.bootstrap.NavItem} this
4408             * @param {boolean} state the new state
4409              
4410          */
4411         'changed': true,
4412         /**
4413             * @event scrollto
4414             * Fires when scroll to element
4415             * @param {Roo.bootstrap.NavItem} this
4416             * @param {Object} options
4417             * @param {Roo.EventObject} e
4418              
4419          */
4420         'scrollto': true
4421     });
4422    
4423 };
4424
4425 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4426     
4427     href: false,
4428     html: '',
4429     badge: '',
4430     icon: false,
4431     glyphicon: false,
4432     active: false,
4433     preventDefault : false,
4434     tabId : false,
4435     tagtype : 'a',
4436     disabled : false,
4437     animateRef : false,
4438     was_active : false,
4439     
4440     getAutoCreate : function(){
4441          
4442         var cfg = {
4443             tag: 'li',
4444             cls: 'nav-item'
4445             
4446         };
4447         
4448         if (this.active) {
4449             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4450         }
4451         if (this.disabled) {
4452             cfg.cls += ' disabled';
4453         }
4454         
4455         if (this.href || this.html || this.glyphicon || this.icon) {
4456             cfg.cn = [
4457                 {
4458                     tag: this.tagtype,
4459                     href : this.href || "#",
4460                     html: this.html || ''
4461                 }
4462             ];
4463             
4464             if (this.icon) {
4465                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4466             }
4467
4468             if(this.glyphicon) {
4469                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4470             }
4471             
4472             if (this.menu) {
4473                 
4474                 cfg.cn[0].html += " <span class='caret'></span>";
4475              
4476             }
4477             
4478             if (this.badge !== '') {
4479                  
4480                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4481             }
4482         }
4483         
4484         
4485         
4486         return cfg;
4487     },
4488     initEvents: function() 
4489     {
4490         if (typeof (this.menu) != 'undefined') {
4491             this.menu.parentType = this.xtype;
4492             this.menu.triggerEl = this.el;
4493             this.menu = this.addxtype(Roo.apply({}, this.menu));
4494         }
4495         
4496         this.el.select('a',true).on('click', this.onClick, this);
4497         
4498         if(this.tagtype == 'span'){
4499             this.el.select('span',true).on('click', this.onClick, this);
4500         }
4501        
4502         // at this point parent should be available..
4503         this.parent().register(this);
4504     },
4505     
4506     onClick : function(e)
4507     {
4508         if (e.getTarget('.dropdown-menu-item')) {
4509             // did you click on a menu itemm.... - then don't trigger onclick..
4510             return;
4511         }
4512         
4513         if(
4514                 this.preventDefault || 
4515                 this.href == '#' 
4516         ){
4517             Roo.log("NavItem - prevent Default?");
4518             e.preventDefault();
4519         }
4520         
4521         if (this.disabled) {
4522             return;
4523         }
4524         
4525         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4526         if (tg && tg.transition) {
4527             Roo.log("waiting for the transitionend");
4528             return;
4529         }
4530         
4531         
4532         
4533         //Roo.log("fire event clicked");
4534         if(this.fireEvent('click', this, e) === false){
4535             return;
4536         };
4537         
4538         if(this.tagtype == 'span'){
4539             return;
4540         }
4541         
4542         //Roo.log(this.href);
4543         var ael = this.el.select('a',true).first();
4544         //Roo.log(ael);
4545         
4546         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4547             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4548             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4549                 return; // ignore... - it's a 'hash' to another page.
4550             }
4551             Roo.log("NavItem - prevent Default?");
4552             e.preventDefault();
4553             this.scrollToElement(e);
4554         }
4555         
4556         
4557         var p =  this.parent();
4558    
4559         if (['tabs','pills'].indexOf(p.type)!==-1) {
4560             if (typeof(p.setActiveItem) !== 'undefined') {
4561                 p.setActiveItem(this);
4562             }
4563         }
4564         
4565         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4566         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4567             // remove the collapsed menu expand...
4568             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4569         }
4570     },
4571     
4572     isActive: function () {
4573         return this.active
4574     },
4575     setActive : function(state, fire, is_was_active)
4576     {
4577         if (this.active && !state && this.navId) {
4578             this.was_active = true;
4579             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4580             if (nv) {
4581                 nv.clearWasActive(this);
4582             }
4583             
4584         }
4585         this.active = state;
4586         
4587         if (!state ) {
4588             this.el.removeClass('active');
4589         } else if (!this.el.hasClass('active')) {
4590             this.el.addClass('active');
4591         }
4592         if (fire) {
4593             this.fireEvent('changed', this, state);
4594         }
4595         
4596         // show a panel if it's registered and related..
4597         
4598         if (!this.navId || !this.tabId || !state || is_was_active) {
4599             return;
4600         }
4601         
4602         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4603         if (!tg) {
4604             return;
4605         }
4606         var pan = tg.getPanelByName(this.tabId);
4607         if (!pan) {
4608             return;
4609         }
4610         // if we can not flip to new panel - go back to old nav highlight..
4611         if (false == tg.showPanel(pan)) {
4612             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4613             if (nv) {
4614                 var onav = nv.getWasActive();
4615                 if (onav) {
4616                     onav.setActive(true, false, true);
4617                 }
4618             }
4619             
4620         }
4621         
4622         
4623         
4624     },
4625      // this should not be here...
4626     setDisabled : function(state)
4627     {
4628         this.disabled = state;
4629         if (!state ) {
4630             this.el.removeClass('disabled');
4631         } else if (!this.el.hasClass('disabled')) {
4632             this.el.addClass('disabled');
4633         }
4634         
4635     },
4636     
4637     /**
4638      * Fetch the element to display the tooltip on.
4639      * @return {Roo.Element} defaults to this.el
4640      */
4641     tooltipEl : function()
4642     {
4643         return this.el.select('' + this.tagtype + '', true).first();
4644     },
4645     
4646     scrollToElement : function(e)
4647     {
4648         var c = document.body;
4649         
4650         /*
4651          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4652          */
4653         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4654             c = document.documentElement;
4655         }
4656         
4657         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4658         
4659         if(!target){
4660             return;
4661         }
4662
4663         var o = target.calcOffsetsTo(c);
4664         
4665         var options = {
4666             target : target,
4667             value : o[1]
4668         };
4669         
4670         this.fireEvent('scrollto', this, options, e);
4671         
4672         Roo.get(c).scrollTo('top', options.value, true);
4673         
4674         return;
4675     }
4676 });
4677  
4678
4679  /*
4680  * - LGPL
4681  *
4682  * sidebar item
4683  *
4684  *  li
4685  *    <span> icon </span>
4686  *    <span> text </span>
4687  *    <span>badge </span>
4688  */
4689
4690 /**
4691  * @class Roo.bootstrap.NavSidebarItem
4692  * @extends Roo.bootstrap.NavItem
4693  * Bootstrap Navbar.NavSidebarItem class
4694  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4695  * {Boolean} open is the menu open
4696  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4697  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4698  * {String} buttonSize (sm|md|lg)the extra classes for the button
4699  * {Boolean} showArrow show arrow next to the text (default true)
4700  * @constructor
4701  * Create a new Navbar Button
4702  * @param {Object} config The config object
4703  */
4704 Roo.bootstrap.NavSidebarItem = function(config){
4705     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4706     this.addEvents({
4707         // raw events
4708         /**
4709          * @event click
4710          * The raw click event for the entire grid.
4711          * @param {Roo.EventObject} e
4712          */
4713         "click" : true,
4714          /**
4715             * @event changed
4716             * Fires when the active item active state changes
4717             * @param {Roo.bootstrap.NavSidebarItem} this
4718             * @param {boolean} state the new state
4719              
4720          */
4721         'changed': true
4722     });
4723    
4724 };
4725
4726 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4727     
4728     badgeWeight : 'default',
4729     
4730     open: false,
4731     
4732     buttonView : false,
4733     
4734     buttonWeight : 'default',
4735     
4736     buttonSize : 'md',
4737     
4738     showArrow : true,
4739     
4740     getAutoCreate : function(){
4741         
4742         
4743         var a = {
4744                 tag: 'a',
4745                 href : this.href || '#',
4746                 cls: '',
4747                 html : '',
4748                 cn : []
4749         };
4750         
4751         if(this.buttonView){
4752             a = {
4753                 tag: 'button',
4754                 href : this.href || '#',
4755                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4756                 html : this.html,
4757                 cn : []
4758             };
4759         }
4760         
4761         var cfg = {
4762             tag: 'li',
4763             cls: '',
4764             cn: [ a ]
4765         };
4766         
4767         if (this.active) {
4768             cfg.cls += ' active';
4769         }
4770         
4771         if (this.disabled) {
4772             cfg.cls += ' disabled';
4773         }
4774         if (this.open) {
4775             cfg.cls += ' open x-open';
4776         }
4777         // left icon..
4778         if (this.glyphicon || this.icon) {
4779             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4780             a.cn.push({ tag : 'i', cls : c }) ;
4781         }
4782         
4783         if(!this.buttonView){
4784             var span = {
4785                 tag: 'span',
4786                 html : this.html || ''
4787             };
4788
4789             a.cn.push(span);
4790             
4791         }
4792         
4793         if (this.badge !== '') {
4794             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4795         }
4796         
4797         if (this.menu) {
4798             
4799             if(this.showArrow){
4800                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4801             }
4802             
4803             a.cls += ' dropdown-toggle treeview' ;
4804         }
4805         
4806         return cfg;
4807     },
4808     
4809     initEvents : function()
4810     { 
4811         if (typeof (this.menu) != 'undefined') {
4812             this.menu.parentType = this.xtype;
4813             this.menu.triggerEl = this.el;
4814             this.menu = this.addxtype(Roo.apply({}, this.menu));
4815         }
4816         
4817         this.el.on('click', this.onClick, this);
4818         
4819         if(this.badge !== ''){
4820             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4821         }
4822         
4823     },
4824     
4825     onClick : function(e)
4826     {
4827         if(this.disabled){
4828             e.preventDefault();
4829             return;
4830         }
4831         
4832         if(this.preventDefault){
4833             e.preventDefault();
4834         }
4835         
4836         this.fireEvent('click', this);
4837     },
4838     
4839     disable : function()
4840     {
4841         this.setDisabled(true);
4842     },
4843     
4844     enable : function()
4845     {
4846         this.setDisabled(false);
4847     },
4848     
4849     setDisabled : function(state)
4850     {
4851         if(this.disabled == state){
4852             return;
4853         }
4854         
4855         this.disabled = state;
4856         
4857         if (state) {
4858             this.el.addClass('disabled');
4859             return;
4860         }
4861         
4862         this.el.removeClass('disabled');
4863         
4864         return;
4865     },
4866     
4867     setActive : function(state)
4868     {
4869         if(this.active == state){
4870             return;
4871         }
4872         
4873         this.active = state;
4874         
4875         if (state) {
4876             this.el.addClass('active');
4877             return;
4878         }
4879         
4880         this.el.removeClass('active');
4881         
4882         return;
4883     },
4884     
4885     isActive: function () 
4886     {
4887         return this.active;
4888     },
4889     
4890     setBadge : function(str)
4891     {
4892         if(!this.badgeEl){
4893             return;
4894         }
4895         
4896         this.badgeEl.dom.innerHTML = str;
4897     }
4898     
4899    
4900      
4901  
4902 });
4903  
4904
4905  /*
4906  * - LGPL
4907  *
4908  * row
4909  * 
4910  */
4911
4912 /**
4913  * @class Roo.bootstrap.Row
4914  * @extends Roo.bootstrap.Component
4915  * Bootstrap Row class (contains columns...)
4916  * 
4917  * @constructor
4918  * Create a new Row
4919  * @param {Object} config The config object
4920  */
4921
4922 Roo.bootstrap.Row = function(config){
4923     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4924 };
4925
4926 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4927     
4928     getAutoCreate : function(){
4929        return {
4930             cls: 'row clearfix'
4931        };
4932     }
4933     
4934     
4935 });
4936
4937  
4938
4939  /*
4940  * - LGPL
4941  *
4942  * element
4943  * 
4944  */
4945
4946 /**
4947  * @class Roo.bootstrap.Element
4948  * @extends Roo.bootstrap.Component
4949  * Bootstrap Element class
4950  * @cfg {String} html contents of the element
4951  * @cfg {String} tag tag of the element
4952  * @cfg {String} cls class of the element
4953  * @cfg {Boolean} preventDefault (true|false) default false
4954  * @cfg {Boolean} clickable (true|false) default false
4955  * 
4956  * @constructor
4957  * Create a new Element
4958  * @param {Object} config The config object
4959  */
4960
4961 Roo.bootstrap.Element = function(config){
4962     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4963     
4964     this.addEvents({
4965         // raw events
4966         /**
4967          * @event click
4968          * When a element is chick
4969          * @param {Roo.bootstrap.Element} this
4970          * @param {Roo.EventObject} e
4971          */
4972         "click" : true
4973     });
4974 };
4975
4976 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4977     
4978     tag: 'div',
4979     cls: '',
4980     html: '',
4981     preventDefault: false, 
4982     clickable: false,
4983     
4984     getAutoCreate : function(){
4985         
4986         var cfg = {
4987             tag: this.tag,
4988             // cls: this.cls, double assign in parent class Component.js :: onRender
4989             html: this.html
4990         };
4991         
4992         return cfg;
4993     },
4994     
4995     initEvents: function() 
4996     {
4997         Roo.bootstrap.Element.superclass.initEvents.call(this);
4998         
4999         if(this.clickable){
5000             this.el.on('click', this.onClick, this);
5001         }
5002         
5003     },
5004     
5005     onClick : function(e)
5006     {
5007         if(this.preventDefault){
5008             e.preventDefault();
5009         }
5010         
5011         this.fireEvent('click', this, e);
5012     },
5013     
5014     getValue : function()
5015     {
5016         return this.el.dom.innerHTML;
5017     },
5018     
5019     setValue : function(value)
5020     {
5021         this.el.dom.innerHTML = value;
5022     }
5023    
5024 });
5025
5026  
5027
5028  /*
5029  * - LGPL
5030  *
5031  * pagination
5032  * 
5033  */
5034
5035 /**
5036  * @class Roo.bootstrap.Pagination
5037  * @extends Roo.bootstrap.Component
5038  * Bootstrap Pagination class
5039  * @cfg {String} size xs | sm | md | lg
5040  * @cfg {Boolean} inverse false | true
5041  * 
5042  * @constructor
5043  * Create a new Pagination
5044  * @param {Object} config The config object
5045  */
5046
5047 Roo.bootstrap.Pagination = function(config){
5048     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5049 };
5050
5051 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5052     
5053     cls: false,
5054     size: false,
5055     inverse: false,
5056     
5057     getAutoCreate : function(){
5058         var cfg = {
5059             tag: 'ul',
5060                 cls: 'pagination'
5061         };
5062         if (this.inverse) {
5063             cfg.cls += ' inverse';
5064         }
5065         if (this.html) {
5066             cfg.html=this.html;
5067         }
5068         if (this.cls) {
5069             cfg.cls += " " + this.cls;
5070         }
5071         return cfg;
5072     }
5073    
5074 });
5075
5076  
5077
5078  /*
5079  * - LGPL
5080  *
5081  * Pagination item
5082  * 
5083  */
5084
5085
5086 /**
5087  * @class Roo.bootstrap.PaginationItem
5088  * @extends Roo.bootstrap.Component
5089  * Bootstrap PaginationItem class
5090  * @cfg {String} html text
5091  * @cfg {String} href the link
5092  * @cfg {Boolean} preventDefault (true | false) default true
5093  * @cfg {Boolean} active (true | false) default false
5094  * @cfg {Boolean} disabled default false
5095  * 
5096  * 
5097  * @constructor
5098  * Create a new PaginationItem
5099  * @param {Object} config The config object
5100  */
5101
5102
5103 Roo.bootstrap.PaginationItem = function(config){
5104     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5105     this.addEvents({
5106         // raw events
5107         /**
5108          * @event click
5109          * The raw click event for the entire grid.
5110          * @param {Roo.EventObject} e
5111          */
5112         "click" : true
5113     });
5114 };
5115
5116 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5117     
5118     href : false,
5119     html : false,
5120     preventDefault: true,
5121     active : false,
5122     cls : false,
5123     disabled: false,
5124     
5125     getAutoCreate : function(){
5126         var cfg= {
5127             tag: 'li',
5128             cn: [
5129                 {
5130                     tag : 'a',
5131                     href : this.href ? this.href : '#',
5132                     html : this.html ? this.html : ''
5133                 }
5134             ]
5135         };
5136         
5137         if(this.cls){
5138             cfg.cls = this.cls;
5139         }
5140         
5141         if(this.disabled){
5142             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5143         }
5144         
5145         if(this.active){
5146             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5147         }
5148         
5149         return cfg;
5150     },
5151     
5152     initEvents: function() {
5153         
5154         this.el.on('click', this.onClick, this);
5155         
5156     },
5157     onClick : function(e)
5158     {
5159         Roo.log('PaginationItem on click ');
5160         if(this.preventDefault){
5161             e.preventDefault();
5162         }
5163         
5164         if(this.disabled){
5165             return;
5166         }
5167         
5168         this.fireEvent('click', this, e);
5169     }
5170    
5171 });
5172
5173  
5174
5175  /*
5176  * - LGPL
5177  *
5178  * slider
5179  * 
5180  */
5181
5182
5183 /**
5184  * @class Roo.bootstrap.Slider
5185  * @extends Roo.bootstrap.Component
5186  * Bootstrap Slider class
5187  *    
5188  * @constructor
5189  * Create a new Slider
5190  * @param {Object} config The config object
5191  */
5192
5193 Roo.bootstrap.Slider = function(config){
5194     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5195 };
5196
5197 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5198     
5199     getAutoCreate : function(){
5200         
5201         var cfg = {
5202             tag: 'div',
5203             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5204             cn: [
5205                 {
5206                     tag: 'a',
5207                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5208                 }
5209             ]
5210         };
5211         
5212         return cfg;
5213     }
5214    
5215 });
5216
5217  /*
5218  * Based on:
5219  * Ext JS Library 1.1.1
5220  * Copyright(c) 2006-2007, Ext JS, LLC.
5221  *
5222  * Originally Released Under LGPL - original licence link has changed is not relivant.
5223  *
5224  * Fork - LGPL
5225  * <script type="text/javascript">
5226  */
5227  
5228
5229 /**
5230  * @class Roo.grid.ColumnModel
5231  * @extends Roo.util.Observable
5232  * This is the default implementation of a ColumnModel used by the Grid. It defines
5233  * the columns in the grid.
5234  * <br>Usage:<br>
5235  <pre><code>
5236  var colModel = new Roo.grid.ColumnModel([
5237         {header: "Ticker", width: 60, sortable: true, locked: true},
5238         {header: "Company Name", width: 150, sortable: true},
5239         {header: "Market Cap.", width: 100, sortable: true},
5240         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5241         {header: "Employees", width: 100, sortable: true, resizable: false}
5242  ]);
5243  </code></pre>
5244  * <p>
5245  
5246  * The config options listed for this class are options which may appear in each
5247  * individual column definition.
5248  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5249  * @constructor
5250  * @param {Object} config An Array of column config objects. See this class's
5251  * config objects for details.
5252 */
5253 Roo.grid.ColumnModel = function(config){
5254         /**
5255      * The config passed into the constructor
5256      */
5257     this.config = config;
5258     this.lookup = {};
5259
5260     // if no id, create one
5261     // if the column does not have a dataIndex mapping,
5262     // map it to the order it is in the config
5263     for(var i = 0, len = config.length; i < len; i++){
5264         var c = config[i];
5265         if(typeof c.dataIndex == "undefined"){
5266             c.dataIndex = i;
5267         }
5268         if(typeof c.renderer == "string"){
5269             c.renderer = Roo.util.Format[c.renderer];
5270         }
5271         if(typeof c.id == "undefined"){
5272             c.id = Roo.id();
5273         }
5274         if(c.editor && c.editor.xtype){
5275             c.editor  = Roo.factory(c.editor, Roo.grid);
5276         }
5277         if(c.editor && c.editor.isFormField){
5278             c.editor = new Roo.grid.GridEditor(c.editor);
5279         }
5280         this.lookup[c.id] = c;
5281     }
5282
5283     /**
5284      * The width of columns which have no width specified (defaults to 100)
5285      * @type Number
5286      */
5287     this.defaultWidth = 100;
5288
5289     /**
5290      * Default sortable of columns which have no sortable specified (defaults to false)
5291      * @type Boolean
5292      */
5293     this.defaultSortable = false;
5294
5295     this.addEvents({
5296         /**
5297              * @event widthchange
5298              * Fires when the width of a column changes.
5299              * @param {ColumnModel} this
5300              * @param {Number} columnIndex The column index
5301              * @param {Number} newWidth The new width
5302              */
5303             "widthchange": true,
5304         /**
5305              * @event headerchange
5306              * Fires when the text of a header changes.
5307              * @param {ColumnModel} this
5308              * @param {Number} columnIndex The column index
5309              * @param {Number} newText The new header text
5310              */
5311             "headerchange": true,
5312         /**
5313              * @event hiddenchange
5314              * Fires when a column is hidden or "unhidden".
5315              * @param {ColumnModel} this
5316              * @param {Number} columnIndex The column index
5317              * @param {Boolean} hidden true if hidden, false otherwise
5318              */
5319             "hiddenchange": true,
5320             /**
5321          * @event columnmoved
5322          * Fires when a column is moved.
5323          * @param {ColumnModel} this
5324          * @param {Number} oldIndex
5325          * @param {Number} newIndex
5326          */
5327         "columnmoved" : true,
5328         /**
5329          * @event columlockchange
5330          * Fires when a column's locked state is changed
5331          * @param {ColumnModel} this
5332          * @param {Number} colIndex
5333          * @param {Boolean} locked true if locked
5334          */
5335         "columnlockchange" : true
5336     });
5337     Roo.grid.ColumnModel.superclass.constructor.call(this);
5338 };
5339 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5340     /**
5341      * @cfg {String} header The header text to display in the Grid view.
5342      */
5343     /**
5344      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5345      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5346      * specified, the column's index is used as an index into the Record's data Array.
5347      */
5348     /**
5349      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5350      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5351      */
5352     /**
5353      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5354      * Defaults to the value of the {@link #defaultSortable} property.
5355      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5356      */
5357     /**
5358      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5359      */
5360     /**
5361      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5362      */
5363     /**
5364      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5365      */
5366     /**
5367      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5368      */
5369     /**
5370      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5371      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5372      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5373      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5374      */
5375        /**
5376      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5377      */
5378     /**
5379      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5380      */
5381     /**
5382      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5383      */
5384     /**
5385      * @cfg {String} cursor (Optional)
5386      */
5387     /**
5388      * @cfg {String} tooltip (Optional)
5389      */
5390     /**
5391      * @cfg {Number} xs (Optional)
5392      */
5393     /**
5394      * @cfg {Number} sm (Optional)
5395      */
5396     /**
5397      * @cfg {Number} md (Optional)
5398      */
5399     /**
5400      * @cfg {Number} lg (Optional)
5401      */
5402     /**
5403      * Returns the id of the column at the specified index.
5404      * @param {Number} index The column index
5405      * @return {String} the id
5406      */
5407     getColumnId : function(index){
5408         return this.config[index].id;
5409     },
5410
5411     /**
5412      * Returns the column for a specified id.
5413      * @param {String} id The column id
5414      * @return {Object} the column
5415      */
5416     getColumnById : function(id){
5417         return this.lookup[id];
5418     },
5419
5420     
5421     /**
5422      * Returns the column for a specified dataIndex.
5423      * @param {String} dataIndex The column dataIndex
5424      * @return {Object|Boolean} the column or false if not found
5425      */
5426     getColumnByDataIndex: function(dataIndex){
5427         var index = this.findColumnIndex(dataIndex);
5428         return index > -1 ? this.config[index] : false;
5429     },
5430     
5431     /**
5432      * Returns the index for a specified column id.
5433      * @param {String} id The column id
5434      * @return {Number} the index, or -1 if not found
5435      */
5436     getIndexById : function(id){
5437         for(var i = 0, len = this.config.length; i < len; i++){
5438             if(this.config[i].id == id){
5439                 return i;
5440             }
5441         }
5442         return -1;
5443     },
5444     
5445     /**
5446      * Returns the index for a specified column dataIndex.
5447      * @param {String} dataIndex The column dataIndex
5448      * @return {Number} the index, or -1 if not found
5449      */
5450     
5451     findColumnIndex : function(dataIndex){
5452         for(var i = 0, len = this.config.length; i < len; i++){
5453             if(this.config[i].dataIndex == dataIndex){
5454                 return i;
5455             }
5456         }
5457         return -1;
5458     },
5459     
5460     
5461     moveColumn : function(oldIndex, newIndex){
5462         var c = this.config[oldIndex];
5463         this.config.splice(oldIndex, 1);
5464         this.config.splice(newIndex, 0, c);
5465         this.dataMap = null;
5466         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5467     },
5468
5469     isLocked : function(colIndex){
5470         return this.config[colIndex].locked === true;
5471     },
5472
5473     setLocked : function(colIndex, value, suppressEvent){
5474         if(this.isLocked(colIndex) == value){
5475             return;
5476         }
5477         this.config[colIndex].locked = value;
5478         if(!suppressEvent){
5479             this.fireEvent("columnlockchange", this, colIndex, value);
5480         }
5481     },
5482
5483     getTotalLockedWidth : function(){
5484         var totalWidth = 0;
5485         for(var i = 0; i < this.config.length; i++){
5486             if(this.isLocked(i) && !this.isHidden(i)){
5487                 this.totalWidth += this.getColumnWidth(i);
5488             }
5489         }
5490         return totalWidth;
5491     },
5492
5493     getLockedCount : function(){
5494         for(var i = 0, len = this.config.length; i < len; i++){
5495             if(!this.isLocked(i)){
5496                 return i;
5497             }
5498         }
5499         
5500         return this.config.length;
5501     },
5502
5503     /**
5504      * Returns the number of columns.
5505      * @return {Number}
5506      */
5507     getColumnCount : function(visibleOnly){
5508         if(visibleOnly === true){
5509             var c = 0;
5510             for(var i = 0, len = this.config.length; i < len; i++){
5511                 if(!this.isHidden(i)){
5512                     c++;
5513                 }
5514             }
5515             return c;
5516         }
5517         return this.config.length;
5518     },
5519
5520     /**
5521      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5522      * @param {Function} fn
5523      * @param {Object} scope (optional)
5524      * @return {Array} result
5525      */
5526     getColumnsBy : function(fn, scope){
5527         var r = [];
5528         for(var i = 0, len = this.config.length; i < len; i++){
5529             var c = this.config[i];
5530             if(fn.call(scope||this, c, i) === true){
5531                 r[r.length] = c;
5532             }
5533         }
5534         return r;
5535     },
5536
5537     /**
5538      * Returns true if the specified column is sortable.
5539      * @param {Number} col The column index
5540      * @return {Boolean}
5541      */
5542     isSortable : function(col){
5543         if(typeof this.config[col].sortable == "undefined"){
5544             return this.defaultSortable;
5545         }
5546         return this.config[col].sortable;
5547     },
5548
5549     /**
5550      * Returns the rendering (formatting) function defined for the column.
5551      * @param {Number} col The column index.
5552      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5553      */
5554     getRenderer : function(col){
5555         if(!this.config[col].renderer){
5556             return Roo.grid.ColumnModel.defaultRenderer;
5557         }
5558         return this.config[col].renderer;
5559     },
5560
5561     /**
5562      * Sets the rendering (formatting) function for a column.
5563      * @param {Number} col The column index
5564      * @param {Function} fn The function to use to process the cell's raw data
5565      * to return HTML markup for the grid view. The render function is called with
5566      * the following parameters:<ul>
5567      * <li>Data value.</li>
5568      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5569      * <li>css A CSS style string to apply to the table cell.</li>
5570      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5571      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5572      * <li>Row index</li>
5573      * <li>Column index</li>
5574      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5575      */
5576     setRenderer : function(col, fn){
5577         this.config[col].renderer = fn;
5578     },
5579
5580     /**
5581      * Returns the width for the specified column.
5582      * @param {Number} col The column index
5583      * @return {Number}
5584      */
5585     getColumnWidth : function(col){
5586         return this.config[col].width * 1 || this.defaultWidth;
5587     },
5588
5589     /**
5590      * Sets the width for a column.
5591      * @param {Number} col The column index
5592      * @param {Number} width The new width
5593      */
5594     setColumnWidth : function(col, width, suppressEvent){
5595         this.config[col].width = width;
5596         this.totalWidth = null;
5597         if(!suppressEvent){
5598              this.fireEvent("widthchange", this, col, width);
5599         }
5600     },
5601
5602     /**
5603      * Returns the total width of all columns.
5604      * @param {Boolean} includeHidden True to include hidden column widths
5605      * @return {Number}
5606      */
5607     getTotalWidth : function(includeHidden){
5608         if(!this.totalWidth){
5609             this.totalWidth = 0;
5610             for(var i = 0, len = this.config.length; i < len; i++){
5611                 if(includeHidden || !this.isHidden(i)){
5612                     this.totalWidth += this.getColumnWidth(i);
5613                 }
5614             }
5615         }
5616         return this.totalWidth;
5617     },
5618
5619     /**
5620      * Returns the header for the specified column.
5621      * @param {Number} col The column index
5622      * @return {String}
5623      */
5624     getColumnHeader : function(col){
5625         return this.config[col].header;
5626     },
5627
5628     /**
5629      * Sets the header for a column.
5630      * @param {Number} col The column index
5631      * @param {String} header The new header
5632      */
5633     setColumnHeader : function(col, header){
5634         this.config[col].header = header;
5635         this.fireEvent("headerchange", this, col, header);
5636     },
5637
5638     /**
5639      * Returns the tooltip for the specified column.
5640      * @param {Number} col The column index
5641      * @return {String}
5642      */
5643     getColumnTooltip : function(col){
5644             return this.config[col].tooltip;
5645     },
5646     /**
5647      * Sets the tooltip for a column.
5648      * @param {Number} col The column index
5649      * @param {String} tooltip The new tooltip
5650      */
5651     setColumnTooltip : function(col, tooltip){
5652             this.config[col].tooltip = tooltip;
5653     },
5654
5655     /**
5656      * Returns the dataIndex for the specified column.
5657      * @param {Number} col The column index
5658      * @return {Number}
5659      */
5660     getDataIndex : function(col){
5661         return this.config[col].dataIndex;
5662     },
5663
5664     /**
5665      * Sets the dataIndex for a column.
5666      * @param {Number} col The column index
5667      * @param {Number} dataIndex The new dataIndex
5668      */
5669     setDataIndex : function(col, dataIndex){
5670         this.config[col].dataIndex = dataIndex;
5671     },
5672
5673     
5674     
5675     /**
5676      * Returns true if the cell is editable.
5677      * @param {Number} colIndex The column index
5678      * @param {Number} rowIndex The row index - this is nto actually used..?
5679      * @return {Boolean}
5680      */
5681     isCellEditable : function(colIndex, rowIndex){
5682         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5683     },
5684
5685     /**
5686      * Returns the editor defined for the cell/column.
5687      * return false or null to disable editing.
5688      * @param {Number} colIndex The column index
5689      * @param {Number} rowIndex The row index
5690      * @return {Object}
5691      */
5692     getCellEditor : function(colIndex, rowIndex){
5693         return this.config[colIndex].editor;
5694     },
5695
5696     /**
5697      * Sets if a column is editable.
5698      * @param {Number} col The column index
5699      * @param {Boolean} editable True if the column is editable
5700      */
5701     setEditable : function(col, editable){
5702         this.config[col].editable = editable;
5703     },
5704
5705
5706     /**
5707      * Returns true if the column is hidden.
5708      * @param {Number} colIndex The column index
5709      * @return {Boolean}
5710      */
5711     isHidden : function(colIndex){
5712         return this.config[colIndex].hidden;
5713     },
5714
5715
5716     /**
5717      * Returns true if the column width cannot be changed
5718      */
5719     isFixed : function(colIndex){
5720         return this.config[colIndex].fixed;
5721     },
5722
5723     /**
5724      * Returns true if the column can be resized
5725      * @return {Boolean}
5726      */
5727     isResizable : function(colIndex){
5728         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5729     },
5730     /**
5731      * Sets if a column is hidden.
5732      * @param {Number} colIndex The column index
5733      * @param {Boolean} hidden True if the column is hidden
5734      */
5735     setHidden : function(colIndex, hidden){
5736         this.config[colIndex].hidden = hidden;
5737         this.totalWidth = null;
5738         this.fireEvent("hiddenchange", this, colIndex, hidden);
5739     },
5740
5741     /**
5742      * Sets the editor for a column.
5743      * @param {Number} col The column index
5744      * @param {Object} editor The editor object
5745      */
5746     setEditor : function(col, editor){
5747         this.config[col].editor = editor;
5748     }
5749 });
5750
5751 Roo.grid.ColumnModel.defaultRenderer = function(value)
5752 {
5753     if(typeof value == "object") {
5754         return value;
5755     }
5756         if(typeof value == "string" && value.length < 1){
5757             return "&#160;";
5758         }
5759     
5760         return String.format("{0}", value);
5761 };
5762
5763 // Alias for backwards compatibility
5764 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5765 /*
5766  * Based on:
5767  * Ext JS Library 1.1.1
5768  * Copyright(c) 2006-2007, Ext JS, LLC.
5769  *
5770  * Originally Released Under LGPL - original licence link has changed is not relivant.
5771  *
5772  * Fork - LGPL
5773  * <script type="text/javascript">
5774  */
5775  
5776 /**
5777  * @class Roo.LoadMask
5778  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5779  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5780  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5781  * element's UpdateManager load indicator and will be destroyed after the initial load.
5782  * @constructor
5783  * Create a new LoadMask
5784  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5785  * @param {Object} config The config object
5786  */
5787 Roo.LoadMask = function(el, config){
5788     this.el = Roo.get(el);
5789     Roo.apply(this, config);
5790     if(this.store){
5791         this.store.on('beforeload', this.onBeforeLoad, this);
5792         this.store.on('load', this.onLoad, this);
5793         this.store.on('loadexception', this.onLoadException, this);
5794         this.removeMask = false;
5795     }else{
5796         var um = this.el.getUpdateManager();
5797         um.showLoadIndicator = false; // disable the default indicator
5798         um.on('beforeupdate', this.onBeforeLoad, this);
5799         um.on('update', this.onLoad, this);
5800         um.on('failure', this.onLoad, this);
5801         this.removeMask = true;
5802     }
5803 };
5804
5805 Roo.LoadMask.prototype = {
5806     /**
5807      * @cfg {Boolean} removeMask
5808      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5809      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5810      */
5811     /**
5812      * @cfg {String} msg
5813      * The text to display in a centered loading message box (defaults to 'Loading...')
5814      */
5815     msg : 'Loading...',
5816     /**
5817      * @cfg {String} msgCls
5818      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5819      */
5820     msgCls : 'x-mask-loading',
5821
5822     /**
5823      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5824      * @type Boolean
5825      */
5826     disabled: false,
5827
5828     /**
5829      * Disables the mask to prevent it from being displayed
5830      */
5831     disable : function(){
5832        this.disabled = true;
5833     },
5834
5835     /**
5836      * Enables the mask so that it can be displayed
5837      */
5838     enable : function(){
5839         this.disabled = false;
5840     },
5841     
5842     onLoadException : function()
5843     {
5844         Roo.log(arguments);
5845         
5846         if (typeof(arguments[3]) != 'undefined') {
5847             Roo.MessageBox.alert("Error loading",arguments[3]);
5848         } 
5849         /*
5850         try {
5851             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5852                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5853             }   
5854         } catch(e) {
5855             
5856         }
5857         */
5858     
5859         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5860     },
5861     // private
5862     onLoad : function()
5863     {
5864         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5865     },
5866
5867     // private
5868     onBeforeLoad : function(){
5869         if(!this.disabled){
5870             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5871         }
5872     },
5873
5874     // private
5875     destroy : function(){
5876         if(this.store){
5877             this.store.un('beforeload', this.onBeforeLoad, this);
5878             this.store.un('load', this.onLoad, this);
5879             this.store.un('loadexception', this.onLoadException, this);
5880         }else{
5881             var um = this.el.getUpdateManager();
5882             um.un('beforeupdate', this.onBeforeLoad, this);
5883             um.un('update', this.onLoad, this);
5884             um.un('failure', this.onLoad, this);
5885         }
5886     }
5887 };/*
5888  * - LGPL
5889  *
5890  * table
5891  * 
5892  */
5893
5894 /**
5895  * @class Roo.bootstrap.Table
5896  * @extends Roo.bootstrap.Component
5897  * Bootstrap Table class
5898  * @cfg {String} cls table class
5899  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5900  * @cfg {String} bgcolor Specifies the background color for a table
5901  * @cfg {Number} border Specifies whether the table cells should have borders or not
5902  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5903  * @cfg {Number} cellspacing Specifies the space between cells
5904  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5905  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5906  * @cfg {String} sortable Specifies that the table should be sortable
5907  * @cfg {String} summary Specifies a summary of the content of a table
5908  * @cfg {Number} width Specifies the width of a table
5909  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5910  * 
5911  * @cfg {boolean} striped Should the rows be alternative striped
5912  * @cfg {boolean} bordered Add borders to the table
5913  * @cfg {boolean} hover Add hover highlighting
5914  * @cfg {boolean} condensed Format condensed
5915  * @cfg {boolean} responsive Format condensed
5916  * @cfg {Boolean} loadMask (true|false) default false
5917  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5918  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5919  * @cfg {Boolean} rowSelection (true|false) default false
5920  * @cfg {Boolean} cellSelection (true|false) default false
5921  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5922  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5923  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5924  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5925  
5926  * 
5927  * @constructor
5928  * Create a new Table
5929  * @param {Object} config The config object
5930  */
5931
5932 Roo.bootstrap.Table = function(config){
5933     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5934     
5935   
5936     
5937     // BC...
5938     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5939     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5940     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5941     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5942     
5943     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5944     if (this.sm) {
5945         this.sm.grid = this;
5946         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5947         this.sm = this.selModel;
5948         this.sm.xmodule = this.xmodule || false;
5949     }
5950     
5951     if (this.cm && typeof(this.cm.config) == 'undefined') {
5952         this.colModel = new Roo.grid.ColumnModel(this.cm);
5953         this.cm = this.colModel;
5954         this.cm.xmodule = this.xmodule || false;
5955     }
5956     if (this.store) {
5957         this.store= Roo.factory(this.store, Roo.data);
5958         this.ds = this.store;
5959         this.ds.xmodule = this.xmodule || false;
5960          
5961     }
5962     if (this.footer && this.store) {
5963         this.footer.dataSource = this.ds;
5964         this.footer = Roo.factory(this.footer);
5965     }
5966     
5967     /** @private */
5968     this.addEvents({
5969         /**
5970          * @event cellclick
5971          * Fires when a cell is clicked
5972          * @param {Roo.bootstrap.Table} this
5973          * @param {Roo.Element} el
5974          * @param {Number} rowIndex
5975          * @param {Number} columnIndex
5976          * @param {Roo.EventObject} e
5977          */
5978         "cellclick" : true,
5979         /**
5980          * @event celldblclick
5981          * Fires when a cell is double clicked
5982          * @param {Roo.bootstrap.Table} this
5983          * @param {Roo.Element} el
5984          * @param {Number} rowIndex
5985          * @param {Number} columnIndex
5986          * @param {Roo.EventObject} e
5987          */
5988         "celldblclick" : true,
5989         /**
5990          * @event rowclick
5991          * Fires when a row is clicked
5992          * @param {Roo.bootstrap.Table} this
5993          * @param {Roo.Element} el
5994          * @param {Number} rowIndex
5995          * @param {Roo.EventObject} e
5996          */
5997         "rowclick" : true,
5998         /**
5999          * @event rowdblclick
6000          * Fires when a row is double clicked
6001          * @param {Roo.bootstrap.Table} this
6002          * @param {Roo.Element} el
6003          * @param {Number} rowIndex
6004          * @param {Roo.EventObject} e
6005          */
6006         "rowdblclick" : true,
6007         /**
6008          * @event mouseover
6009          * Fires when a mouseover occur
6010          * @param {Roo.bootstrap.Table} this
6011          * @param {Roo.Element} el
6012          * @param {Number} rowIndex
6013          * @param {Number} columnIndex
6014          * @param {Roo.EventObject} e
6015          */
6016         "mouseover" : true,
6017         /**
6018          * @event mouseout
6019          * Fires when a mouseout occur
6020          * @param {Roo.bootstrap.Table} this
6021          * @param {Roo.Element} el
6022          * @param {Number} rowIndex
6023          * @param {Number} columnIndex
6024          * @param {Roo.EventObject} e
6025          */
6026         "mouseout" : true,
6027         /**
6028          * @event rowclass
6029          * Fires when a row is rendered, so you can change add a style to it.
6030          * @param {Roo.bootstrap.Table} this
6031          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6032          */
6033         'rowclass' : true,
6034           /**
6035          * @event rowsrendered
6036          * Fires when all the  rows have been rendered
6037          * @param {Roo.bootstrap.Table} this
6038          */
6039         'rowsrendered' : true,
6040         /**
6041          * @event contextmenu
6042          * The raw contextmenu event for the entire grid.
6043          * @param {Roo.EventObject} e
6044          */
6045         "contextmenu" : true,
6046         /**
6047          * @event rowcontextmenu
6048          * Fires when a row is right clicked
6049          * @param {Roo.bootstrap.Table} this
6050          * @param {Number} rowIndex
6051          * @param {Roo.EventObject} e
6052          */
6053         "rowcontextmenu" : true,
6054         /**
6055          * @event cellcontextmenu
6056          * Fires when a cell is right clicked
6057          * @param {Roo.bootstrap.Table} this
6058          * @param {Number} rowIndex
6059          * @param {Number} cellIndex
6060          * @param {Roo.EventObject} e
6061          */
6062          "cellcontextmenu" : true,
6063          /**
6064          * @event headercontextmenu
6065          * Fires when a header is right clicked
6066          * @param {Roo.bootstrap.Table} this
6067          * @param {Number} columnIndex
6068          * @param {Roo.EventObject} e
6069          */
6070         "headercontextmenu" : true
6071     });
6072 };
6073
6074 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6075     
6076     cls: false,
6077     align: false,
6078     bgcolor: false,
6079     border: false,
6080     cellpadding: false,
6081     cellspacing: false,
6082     frame: false,
6083     rules: false,
6084     sortable: false,
6085     summary: false,
6086     width: false,
6087     striped : false,
6088     scrollBody : false,
6089     bordered: false,
6090     hover:  false,
6091     condensed : false,
6092     responsive : false,
6093     sm : false,
6094     cm : false,
6095     store : false,
6096     loadMask : false,
6097     footerShow : true,
6098     headerShow : true,
6099   
6100     rowSelection : false,
6101     cellSelection : false,
6102     layout : false,
6103     
6104     // Roo.Element - the tbody
6105     mainBody: false,
6106     // Roo.Element - thead element
6107     mainHead: false,
6108     
6109     container: false, // used by gridpanel...
6110     
6111     lazyLoad : false,
6112     
6113     CSS : Roo.util.CSS,
6114     
6115     auto_hide_footer : false,
6116     
6117     getAutoCreate : function()
6118     {
6119         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6120         
6121         cfg = {
6122             tag: 'table',
6123             cls : 'table',
6124             cn : []
6125         };
6126         if (this.scrollBody) {
6127             cfg.cls += ' table-body-fixed';
6128         }    
6129         if (this.striped) {
6130             cfg.cls += ' table-striped';
6131         }
6132         
6133         if (this.hover) {
6134             cfg.cls += ' table-hover';
6135         }
6136         if (this.bordered) {
6137             cfg.cls += ' table-bordered';
6138         }
6139         if (this.condensed) {
6140             cfg.cls += ' table-condensed';
6141         }
6142         if (this.responsive) {
6143             cfg.cls += ' table-responsive';
6144         }
6145         
6146         if (this.cls) {
6147             cfg.cls+=  ' ' +this.cls;
6148         }
6149         
6150         // this lot should be simplifed...
6151         var _t = this;
6152         var cp = [
6153             'align',
6154             'bgcolor',
6155             'border',
6156             'cellpadding',
6157             'cellspacing',
6158             'frame',
6159             'rules',
6160             'sortable',
6161             'summary',
6162             'width'
6163         ].forEach(function(k) {
6164             if (_t[k]) {
6165                 cfg[k] = _t[k];
6166             }
6167         });
6168         
6169         
6170         if (this.layout) {
6171             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6172         }
6173         
6174         if(this.store || this.cm){
6175             if(this.headerShow){
6176                 cfg.cn.push(this.renderHeader());
6177             }
6178             
6179             cfg.cn.push(this.renderBody());
6180             
6181             if(this.footerShow){
6182                 cfg.cn.push(this.renderFooter());
6183             }
6184             // where does this come from?
6185             //cfg.cls+=  ' TableGrid';
6186         }
6187         
6188         return { cn : [ cfg ] };
6189     },
6190     
6191     initEvents : function()
6192     {   
6193         if(!this.store || !this.cm){
6194             return;
6195         }
6196         if (this.selModel) {
6197             this.selModel.initEvents();
6198         }
6199         
6200         
6201         //Roo.log('initEvents with ds!!!!');
6202         
6203         this.mainBody = this.el.select('tbody', true).first();
6204         this.mainHead = this.el.select('thead', true).first();
6205         this.mainFoot = this.el.select('tfoot', true).first();
6206         
6207         
6208         
6209         var _this = this;
6210         
6211         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6212             e.on('click', _this.sort, _this);
6213         });
6214         
6215         this.mainBody.on("click", this.onClick, this);
6216         this.mainBody.on("dblclick", this.onDblClick, this);
6217         
6218         // why is this done????? = it breaks dialogs??
6219         //this.parent().el.setStyle('position', 'relative');
6220         
6221         
6222         if (this.footer) {
6223             this.footer.parentId = this.id;
6224             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6225             
6226             if(this.lazyLoad){
6227                 this.el.select('tfoot tr td').first().addClass('hide');
6228             }
6229         } 
6230         
6231         if(this.loadMask) {
6232             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6233         }
6234         
6235         this.store.on('load', this.onLoad, this);
6236         this.store.on('beforeload', this.onBeforeLoad, this);
6237         this.store.on('update', this.onUpdate, this);
6238         this.store.on('add', this.onAdd, this);
6239         this.store.on("clear", this.clear, this);
6240         
6241         this.el.on("contextmenu", this.onContextMenu, this);
6242         
6243         this.mainBody.on('scroll', this.onBodyScroll, this);
6244         
6245         this.cm.on("headerchange", this.onHeaderChange, this);
6246         
6247         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6248         
6249     },
6250     
6251     onContextMenu : function(e, t)
6252     {
6253         this.processEvent("contextmenu", e);
6254     },
6255     
6256     processEvent : function(name, e)
6257     {
6258         if (name != 'touchstart' ) {
6259             this.fireEvent(name, e);    
6260         }
6261         
6262         var t = e.getTarget();
6263         
6264         var cell = Roo.get(t);
6265         
6266         if(!cell){
6267             return;
6268         }
6269         
6270         if(cell.findParent('tfoot', false, true)){
6271             return;
6272         }
6273         
6274         if(cell.findParent('thead', false, true)){
6275             
6276             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6277                 cell = Roo.get(t).findParent('th', false, true);
6278                 if (!cell) {
6279                     Roo.log("failed to find th in thead?");
6280                     Roo.log(e.getTarget());
6281                     return;
6282                 }
6283             }
6284             
6285             var cellIndex = cell.dom.cellIndex;
6286             
6287             var ename = name == 'touchstart' ? 'click' : name;
6288             this.fireEvent("header" + ename, this, cellIndex, e);
6289             
6290             return;
6291         }
6292         
6293         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6294             cell = Roo.get(t).findParent('td', false, true);
6295             if (!cell) {
6296                 Roo.log("failed to find th in tbody?");
6297                 Roo.log(e.getTarget());
6298                 return;
6299             }
6300         }
6301         
6302         var row = cell.findParent('tr', false, true);
6303         var cellIndex = cell.dom.cellIndex;
6304         var rowIndex = row.dom.rowIndex - 1;
6305         
6306         if(row !== false){
6307             
6308             this.fireEvent("row" + name, this, rowIndex, e);
6309             
6310             if(cell !== false){
6311             
6312                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6313             }
6314         }
6315         
6316     },
6317     
6318     onMouseover : function(e, el)
6319     {
6320         var cell = Roo.get(el);
6321         
6322         if(!cell){
6323             return;
6324         }
6325         
6326         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6327             cell = cell.findParent('td', false, true);
6328         }
6329         
6330         var row = cell.findParent('tr', false, true);
6331         var cellIndex = cell.dom.cellIndex;
6332         var rowIndex = row.dom.rowIndex - 1; // start from 0
6333         
6334         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6335         
6336     },
6337     
6338     onMouseout : function(e, el)
6339     {
6340         var cell = Roo.get(el);
6341         
6342         if(!cell){
6343             return;
6344         }
6345         
6346         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6347             cell = cell.findParent('td', false, true);
6348         }
6349         
6350         var row = cell.findParent('tr', false, true);
6351         var cellIndex = cell.dom.cellIndex;
6352         var rowIndex = row.dom.rowIndex - 1; // start from 0
6353         
6354         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6355         
6356     },
6357     
6358     onClick : function(e, el)
6359     {
6360         var cell = Roo.get(el);
6361         
6362         if(!cell || (!this.cellSelection && !this.rowSelection)){
6363             return;
6364         }
6365         
6366         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6367             cell = cell.findParent('td', false, true);
6368         }
6369         
6370         if(!cell || typeof(cell) == 'undefined'){
6371             return;
6372         }
6373         
6374         var row = cell.findParent('tr', false, true);
6375         
6376         if(!row || typeof(row) == 'undefined'){
6377             return;
6378         }
6379         
6380         var cellIndex = cell.dom.cellIndex;
6381         var rowIndex = this.getRowIndex(row);
6382         
6383         // why??? - should these not be based on SelectionModel?
6384         if(this.cellSelection){
6385             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6386         }
6387         
6388         if(this.rowSelection){
6389             this.fireEvent('rowclick', this, row, rowIndex, e);
6390         }
6391         
6392         
6393     },
6394         
6395     onDblClick : function(e,el)
6396     {
6397         var cell = Roo.get(el);
6398         
6399         if(!cell || (!this.cellSelection && !this.rowSelection)){
6400             return;
6401         }
6402         
6403         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6404             cell = cell.findParent('td', false, true);
6405         }
6406         
6407         if(!cell || typeof(cell) == 'undefined'){
6408             return;
6409         }
6410         
6411         var row = cell.findParent('tr', false, true);
6412         
6413         if(!row || typeof(row) == 'undefined'){
6414             return;
6415         }
6416         
6417         var cellIndex = cell.dom.cellIndex;
6418         var rowIndex = this.getRowIndex(row);
6419         
6420         if(this.cellSelection){
6421             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6422         }
6423         
6424         if(this.rowSelection){
6425             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6426         }
6427     },
6428     
6429     sort : function(e,el)
6430     {
6431         var col = Roo.get(el);
6432         
6433         if(!col.hasClass('sortable')){
6434             return;
6435         }
6436         
6437         var sort = col.attr('sort');
6438         var dir = 'ASC';
6439         
6440         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6441             dir = 'DESC';
6442         }
6443         
6444         this.store.sortInfo = {field : sort, direction : dir};
6445         
6446         if (this.footer) {
6447             Roo.log("calling footer first");
6448             this.footer.onClick('first');
6449         } else {
6450         
6451             this.store.load({ params : { start : 0 } });
6452         }
6453     },
6454     
6455     renderHeader : function()
6456     {
6457         var header = {
6458             tag: 'thead',
6459             cn : []
6460         };
6461         
6462         var cm = this.cm;
6463         this.totalWidth = 0;
6464         
6465         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6466             
6467             var config = cm.config[i];
6468             
6469             var c = {
6470                 tag: 'th',
6471                 cls : 'x-hcol-' + i,
6472                 style : '',
6473                 html: cm.getColumnHeader(i)
6474             };
6475             
6476             var hh = '';
6477             
6478             if(typeof(config.sortable) != 'undefined' && config.sortable){
6479                 c.cls = 'sortable';
6480                 c.html = '<i class="glyphicon"></i>' + c.html;
6481             }
6482             
6483             if(typeof(config.lgHeader) != 'undefined'){
6484                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6485             }
6486             
6487             if(typeof(config.mdHeader) != 'undefined'){
6488                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6489             }
6490             
6491             if(typeof(config.smHeader) != 'undefined'){
6492                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6493             }
6494             
6495             if(typeof(config.xsHeader) != 'undefined'){
6496                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6497             }
6498             
6499             if(hh.length){
6500                 c.html = hh;
6501             }
6502             
6503             if(typeof(config.tooltip) != 'undefined'){
6504                 c.tooltip = config.tooltip;
6505             }
6506             
6507             if(typeof(config.colspan) != 'undefined'){
6508                 c.colspan = config.colspan;
6509             }
6510             
6511             if(typeof(config.hidden) != 'undefined' && config.hidden){
6512                 c.style += ' display:none;';
6513             }
6514             
6515             if(typeof(config.dataIndex) != 'undefined'){
6516                 c.sort = config.dataIndex;
6517             }
6518             
6519            
6520             
6521             if(typeof(config.align) != 'undefined' && config.align.length){
6522                 c.style += ' text-align:' + config.align + ';';
6523             }
6524             
6525             if(typeof(config.width) != 'undefined'){
6526                 c.style += ' width:' + config.width + 'px;';
6527                 this.totalWidth += config.width;
6528             } else {
6529                 this.totalWidth += 100; // assume minimum of 100 per column?
6530             }
6531             
6532             if(typeof(config.cls) != 'undefined'){
6533                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6534             }
6535             
6536             ['xs','sm','md','lg'].map(function(size){
6537                 
6538                 if(typeof(config[size]) == 'undefined'){
6539                     return;
6540                 }
6541                 
6542                 if (!config[size]) { // 0 = hidden
6543                     c.cls += ' hidden-' + size;
6544                     return;
6545                 }
6546                 
6547                 c.cls += ' col-' + size + '-' + config[size];
6548
6549             });
6550             
6551             header.cn.push(c)
6552         }
6553         
6554         return header;
6555     },
6556     
6557     renderBody : function()
6558     {
6559         var body = {
6560             tag: 'tbody',
6561             cn : [
6562                 {
6563                     tag: 'tr',
6564                     cn : [
6565                         {
6566                             tag : 'td',
6567                             colspan :  this.cm.getColumnCount()
6568                         }
6569                     ]
6570                 }
6571             ]
6572         };
6573         
6574         return body;
6575     },
6576     
6577     renderFooter : function()
6578     {
6579         var footer = {
6580             tag: 'tfoot',
6581             cn : [
6582                 {
6583                     tag: 'tr',
6584                     cn : [
6585                         {
6586                             tag : 'td',
6587                             colspan :  this.cm.getColumnCount()
6588                         }
6589                     ]
6590                 }
6591             ]
6592         };
6593         
6594         return footer;
6595     },
6596     
6597     
6598     
6599     onLoad : function()
6600     {
6601 //        Roo.log('ds onload');
6602         this.clear();
6603         
6604         var _this = this;
6605         var cm = this.cm;
6606         var ds = this.store;
6607         
6608         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6609             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6610             if (_this.store.sortInfo) {
6611                     
6612                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6613                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6614                 }
6615                 
6616                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6617                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6618                 }
6619             }
6620         });
6621         
6622         var tbody =  this.mainBody;
6623               
6624         if(ds.getCount() > 0){
6625             ds.data.each(function(d,rowIndex){
6626                 var row =  this.renderRow(cm, ds, rowIndex);
6627                 
6628                 tbody.createChild(row);
6629                 
6630                 var _this = this;
6631                 
6632                 if(row.cellObjects.length){
6633                     Roo.each(row.cellObjects, function(r){
6634                         _this.renderCellObject(r);
6635                     })
6636                 }
6637                 
6638             }, this);
6639         }
6640         
6641         var tfoot = this.el.select('tfoot', true).first();
6642         
6643         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6644             
6645             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6646             
6647             var total = this.ds.getTotalCount();
6648             
6649             if(this.footer.pageSize < total){
6650                 this.mainFoot.show();
6651             }
6652         }
6653         
6654         Roo.each(this.el.select('tbody td', true).elements, function(e){
6655             e.on('mouseover', _this.onMouseover, _this);
6656         });
6657         
6658         Roo.each(this.el.select('tbody td', true).elements, function(e){
6659             e.on('mouseout', _this.onMouseout, _this);
6660         });
6661         this.fireEvent('rowsrendered', this);
6662         
6663         this.autoSize();
6664     },
6665     
6666     
6667     onUpdate : function(ds,record)
6668     {
6669         this.refreshRow(record);
6670         this.autoSize();
6671     },
6672     
6673     onRemove : function(ds, record, index, isUpdate){
6674         if(isUpdate !== true){
6675             this.fireEvent("beforerowremoved", this, index, record);
6676         }
6677         var bt = this.mainBody.dom;
6678         
6679         var rows = this.el.select('tbody > tr', true).elements;
6680         
6681         if(typeof(rows[index]) != 'undefined'){
6682             bt.removeChild(rows[index].dom);
6683         }
6684         
6685 //        if(bt.rows[index]){
6686 //            bt.removeChild(bt.rows[index]);
6687 //        }
6688         
6689         if(isUpdate !== true){
6690             //this.stripeRows(index);
6691             //this.syncRowHeights(index, index);
6692             //this.layout();
6693             this.fireEvent("rowremoved", this, index, record);
6694         }
6695     },
6696     
6697     onAdd : function(ds, records, rowIndex)
6698     {
6699         //Roo.log('on Add called');
6700         // - note this does not handle multiple adding very well..
6701         var bt = this.mainBody.dom;
6702         for (var i =0 ; i < records.length;i++) {
6703             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6704             //Roo.log(records[i]);
6705             //Roo.log(this.store.getAt(rowIndex+i));
6706             this.insertRow(this.store, rowIndex + i, false);
6707             return;
6708         }
6709         
6710     },
6711     
6712     
6713     refreshRow : function(record){
6714         var ds = this.store, index;
6715         if(typeof record == 'number'){
6716             index = record;
6717             record = ds.getAt(index);
6718         }else{
6719             index = ds.indexOf(record);
6720         }
6721         this.insertRow(ds, index, true);
6722         this.autoSize();
6723         this.onRemove(ds, record, index+1, true);
6724         this.autoSize();
6725         //this.syncRowHeights(index, index);
6726         //this.layout();
6727         this.fireEvent("rowupdated", this, index, record);
6728     },
6729     
6730     insertRow : function(dm, rowIndex, isUpdate){
6731         
6732         if(!isUpdate){
6733             this.fireEvent("beforerowsinserted", this, rowIndex);
6734         }
6735             //var s = this.getScrollState();
6736         var row = this.renderRow(this.cm, this.store, rowIndex);
6737         // insert before rowIndex..
6738         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6739         
6740         var _this = this;
6741                 
6742         if(row.cellObjects.length){
6743             Roo.each(row.cellObjects, function(r){
6744                 _this.renderCellObject(r);
6745             })
6746         }
6747             
6748         if(!isUpdate){
6749             this.fireEvent("rowsinserted", this, rowIndex);
6750             //this.syncRowHeights(firstRow, lastRow);
6751             //this.stripeRows(firstRow);
6752             //this.layout();
6753         }
6754         
6755     },
6756     
6757     
6758     getRowDom : function(rowIndex)
6759     {
6760         var rows = this.el.select('tbody > tr', true).elements;
6761         
6762         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6763         
6764     },
6765     // returns the object tree for a tr..
6766   
6767     
6768     renderRow : function(cm, ds, rowIndex) 
6769     {
6770         var d = ds.getAt(rowIndex);
6771         
6772         var row = {
6773             tag : 'tr',
6774             cls : 'x-row-' + rowIndex,
6775             cn : []
6776         };
6777             
6778         var cellObjects = [];
6779         
6780         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6781             var config = cm.config[i];
6782             
6783             var renderer = cm.getRenderer(i);
6784             var value = '';
6785             var id = false;
6786             
6787             if(typeof(renderer) !== 'undefined'){
6788                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6789             }
6790             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6791             // and are rendered into the cells after the row is rendered - using the id for the element.
6792             
6793             if(typeof(value) === 'object'){
6794                 id = Roo.id();
6795                 cellObjects.push({
6796                     container : id,
6797                     cfg : value 
6798                 })
6799             }
6800             
6801             var rowcfg = {
6802                 record: d,
6803                 rowIndex : rowIndex,
6804                 colIndex : i,
6805                 rowClass : ''
6806             };
6807
6808             this.fireEvent('rowclass', this, rowcfg);
6809             
6810             var td = {
6811                 tag: 'td',
6812                 cls : rowcfg.rowClass + ' x-col-' + i,
6813                 style: '',
6814                 html: (typeof(value) === 'object') ? '' : value
6815             };
6816             
6817             if (id) {
6818                 td.id = id;
6819             }
6820             
6821             if(typeof(config.colspan) != 'undefined'){
6822                 td.colspan = config.colspan;
6823             }
6824             
6825             if(typeof(config.hidden) != 'undefined' && config.hidden){
6826                 td.style += ' display:none;';
6827             }
6828             
6829             if(typeof(config.align) != 'undefined' && config.align.length){
6830                 td.style += ' text-align:' + config.align + ';';
6831             }
6832             if(typeof(config.valign) != 'undefined' && config.valign.length){
6833                 td.style += ' vertical-align:' + config.valign + ';';
6834             }
6835             
6836             if(typeof(config.width) != 'undefined'){
6837                 td.style += ' width:' +  config.width + 'px;';
6838             }
6839             
6840             if(typeof(config.cursor) != 'undefined'){
6841                 td.style += ' cursor:' +  config.cursor + ';';
6842             }
6843             
6844             if(typeof(config.cls) != 'undefined'){
6845                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6846             }
6847             
6848             ['xs','sm','md','lg'].map(function(size){
6849                 
6850                 if(typeof(config[size]) == 'undefined'){
6851                     return;
6852                 }
6853                 
6854                 if (!config[size]) { // 0 = hidden
6855                     td.cls += ' hidden-' + size;
6856                     return;
6857                 }
6858                 
6859                 td.cls += ' col-' + size + '-' + config[size];
6860
6861             });
6862             
6863             row.cn.push(td);
6864            
6865         }
6866         
6867         row.cellObjects = cellObjects;
6868         
6869         return row;
6870           
6871     },
6872     
6873     
6874     
6875     onBeforeLoad : function()
6876     {
6877         
6878     },
6879      /**
6880      * Remove all rows
6881      */
6882     clear : function()
6883     {
6884         this.el.select('tbody', true).first().dom.innerHTML = '';
6885     },
6886     /**
6887      * Show or hide a row.
6888      * @param {Number} rowIndex to show or hide
6889      * @param {Boolean} state hide
6890      */
6891     setRowVisibility : function(rowIndex, state)
6892     {
6893         var bt = this.mainBody.dom;
6894         
6895         var rows = this.el.select('tbody > tr', true).elements;
6896         
6897         if(typeof(rows[rowIndex]) == 'undefined'){
6898             return;
6899         }
6900         rows[rowIndex].dom.style.display = state ? '' : 'none';
6901     },
6902     
6903     
6904     getSelectionModel : function(){
6905         if(!this.selModel){
6906             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6907         }
6908         return this.selModel;
6909     },
6910     /*
6911      * Render the Roo.bootstrap object from renderder
6912      */
6913     renderCellObject : function(r)
6914     {
6915         var _this = this;
6916         
6917         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6918         
6919         var t = r.cfg.render(r.container);
6920         
6921         if(r.cfg.cn){
6922             Roo.each(r.cfg.cn, function(c){
6923                 var child = {
6924                     container: t.getChildContainer(),
6925                     cfg: c
6926                 };
6927                 _this.renderCellObject(child);
6928             })
6929         }
6930     },
6931     
6932     getRowIndex : function(row)
6933     {
6934         var rowIndex = -1;
6935         
6936         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6937             if(el != row){
6938                 return;
6939             }
6940             
6941             rowIndex = index;
6942         });
6943         
6944         return rowIndex;
6945     },
6946      /**
6947      * Returns the grid's underlying element = used by panel.Grid
6948      * @return {Element} The element
6949      */
6950     getGridEl : function(){
6951         return this.el;
6952     },
6953      /**
6954      * Forces a resize - used by panel.Grid
6955      * @return {Element} The element
6956      */
6957     autoSize : function()
6958     {
6959         //var ctr = Roo.get(this.container.dom.parentElement);
6960         var ctr = Roo.get(this.el.dom);
6961         
6962         var thd = this.getGridEl().select('thead',true).first();
6963         var tbd = this.getGridEl().select('tbody', true).first();
6964         var tfd = this.getGridEl().select('tfoot', true).first();
6965         
6966         var cw = ctr.getWidth();
6967         
6968         if (tbd) {
6969             
6970             tbd.setSize(ctr.getWidth(),
6971                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6972             );
6973             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6974             cw -= barsize;
6975         }
6976         cw = Math.max(cw, this.totalWidth);
6977         this.getGridEl().select('tr',true).setWidth(cw);
6978         // resize 'expandable coloumn?
6979         
6980         return; // we doe not have a view in this design..
6981         
6982     },
6983     onBodyScroll: function()
6984     {
6985         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6986         if(this.mainHead){
6987             this.mainHead.setStyle({
6988                 'position' : 'relative',
6989                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6990             });
6991         }
6992         
6993         if(this.lazyLoad){
6994             
6995             var scrollHeight = this.mainBody.dom.scrollHeight;
6996             
6997             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6998             
6999             var height = this.mainBody.getHeight();
7000             
7001             if(scrollHeight - height == scrollTop) {
7002                 
7003                 var total = this.ds.getTotalCount();
7004                 
7005                 if(this.footer.cursor + this.footer.pageSize < total){
7006                     
7007                     this.footer.ds.load({
7008                         params : {
7009                             start : this.footer.cursor + this.footer.pageSize,
7010                             limit : this.footer.pageSize
7011                         },
7012                         add : true
7013                     });
7014                 }
7015             }
7016             
7017         }
7018     },
7019     
7020     onHeaderChange : function()
7021     {
7022         var header = this.renderHeader();
7023         var table = this.el.select('table', true).first();
7024         
7025         this.mainHead.remove();
7026         this.mainHead = table.createChild(header, this.mainBody, false);
7027     },
7028     
7029     onHiddenChange : function(colModel, colIndex, hidden)
7030     {
7031         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7032         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7033         
7034         this.CSS.updateRule(thSelector, "display", "");
7035         this.CSS.updateRule(tdSelector, "display", "");
7036         
7037         if(hidden){
7038             this.CSS.updateRule(thSelector, "display", "none");
7039             this.CSS.updateRule(tdSelector, "display", "none");
7040         }
7041         
7042         this.onHeaderChange();
7043         this.onLoad();
7044         
7045     }
7046     
7047 });
7048
7049  
7050
7051  /*
7052  * - LGPL
7053  *
7054  * table cell
7055  * 
7056  */
7057
7058 /**
7059  * @class Roo.bootstrap.TableCell
7060  * @extends Roo.bootstrap.Component
7061  * Bootstrap TableCell class
7062  * @cfg {String} html cell contain text
7063  * @cfg {String} cls cell class
7064  * @cfg {String} tag cell tag (td|th) default td
7065  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7066  * @cfg {String} align Aligns the content in a cell
7067  * @cfg {String} axis Categorizes cells
7068  * @cfg {String} bgcolor Specifies the background color of a cell
7069  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7070  * @cfg {Number} colspan Specifies the number of columns a cell should span
7071  * @cfg {String} headers Specifies one or more header cells a cell is related to
7072  * @cfg {Number} height Sets the height of a cell
7073  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7074  * @cfg {Number} rowspan Sets the number of rows a cell should span
7075  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7076  * @cfg {String} valign Vertical aligns the content in a cell
7077  * @cfg {Number} width Specifies the width of a cell
7078  * 
7079  * @constructor
7080  * Create a new TableCell
7081  * @param {Object} config The config object
7082  */
7083
7084 Roo.bootstrap.TableCell = function(config){
7085     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7086 };
7087
7088 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7089     
7090     html: false,
7091     cls: false,
7092     tag: false,
7093     abbr: false,
7094     align: false,
7095     axis: false,
7096     bgcolor: false,
7097     charoff: false,
7098     colspan: false,
7099     headers: false,
7100     height: false,
7101     nowrap: false,
7102     rowspan: false,
7103     scope: false,
7104     valign: false,
7105     width: false,
7106     
7107     
7108     getAutoCreate : function(){
7109         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7110         
7111         cfg = {
7112             tag: 'td'
7113         };
7114         
7115         if(this.tag){
7116             cfg.tag = this.tag;
7117         }
7118         
7119         if (this.html) {
7120             cfg.html=this.html
7121         }
7122         if (this.cls) {
7123             cfg.cls=this.cls
7124         }
7125         if (this.abbr) {
7126             cfg.abbr=this.abbr
7127         }
7128         if (this.align) {
7129             cfg.align=this.align
7130         }
7131         if (this.axis) {
7132             cfg.axis=this.axis
7133         }
7134         if (this.bgcolor) {
7135             cfg.bgcolor=this.bgcolor
7136         }
7137         if (this.charoff) {
7138             cfg.charoff=this.charoff
7139         }
7140         if (this.colspan) {
7141             cfg.colspan=this.colspan
7142         }
7143         if (this.headers) {
7144             cfg.headers=this.headers
7145         }
7146         if (this.height) {
7147             cfg.height=this.height
7148         }
7149         if (this.nowrap) {
7150             cfg.nowrap=this.nowrap
7151         }
7152         if (this.rowspan) {
7153             cfg.rowspan=this.rowspan
7154         }
7155         if (this.scope) {
7156             cfg.scope=this.scope
7157         }
7158         if (this.valign) {
7159             cfg.valign=this.valign
7160         }
7161         if (this.width) {
7162             cfg.width=this.width
7163         }
7164         
7165         
7166         return cfg;
7167     }
7168    
7169 });
7170
7171  
7172
7173  /*
7174  * - LGPL
7175  *
7176  * table row
7177  * 
7178  */
7179
7180 /**
7181  * @class Roo.bootstrap.TableRow
7182  * @extends Roo.bootstrap.Component
7183  * Bootstrap TableRow class
7184  * @cfg {String} cls row class
7185  * @cfg {String} align Aligns the content in a table row
7186  * @cfg {String} bgcolor Specifies a background color for a table row
7187  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7188  * @cfg {String} valign Vertical aligns the content in a table row
7189  * 
7190  * @constructor
7191  * Create a new TableRow
7192  * @param {Object} config The config object
7193  */
7194
7195 Roo.bootstrap.TableRow = function(config){
7196     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7197 };
7198
7199 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7200     
7201     cls: false,
7202     align: false,
7203     bgcolor: false,
7204     charoff: false,
7205     valign: false,
7206     
7207     getAutoCreate : function(){
7208         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7209         
7210         cfg = {
7211             tag: 'tr'
7212         };
7213             
7214         if(this.cls){
7215             cfg.cls = this.cls;
7216         }
7217         if(this.align){
7218             cfg.align = this.align;
7219         }
7220         if(this.bgcolor){
7221             cfg.bgcolor = this.bgcolor;
7222         }
7223         if(this.charoff){
7224             cfg.charoff = this.charoff;
7225         }
7226         if(this.valign){
7227             cfg.valign = this.valign;
7228         }
7229         
7230         return cfg;
7231     }
7232    
7233 });
7234
7235  
7236
7237  /*
7238  * - LGPL
7239  *
7240  * table body
7241  * 
7242  */
7243
7244 /**
7245  * @class Roo.bootstrap.TableBody
7246  * @extends Roo.bootstrap.Component
7247  * Bootstrap TableBody class
7248  * @cfg {String} cls element class
7249  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7250  * @cfg {String} align Aligns the content inside the element
7251  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7252  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7253  * 
7254  * @constructor
7255  * Create a new TableBody
7256  * @param {Object} config The config object
7257  */
7258
7259 Roo.bootstrap.TableBody = function(config){
7260     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7261 };
7262
7263 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7264     
7265     cls: false,
7266     tag: false,
7267     align: false,
7268     charoff: false,
7269     valign: false,
7270     
7271     getAutoCreate : function(){
7272         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7273         
7274         cfg = {
7275             tag: 'tbody'
7276         };
7277             
7278         if (this.cls) {
7279             cfg.cls=this.cls
7280         }
7281         if(this.tag){
7282             cfg.tag = this.tag;
7283         }
7284         
7285         if(this.align){
7286             cfg.align = this.align;
7287         }
7288         if(this.charoff){
7289             cfg.charoff = this.charoff;
7290         }
7291         if(this.valign){
7292             cfg.valign = this.valign;
7293         }
7294         
7295         return cfg;
7296     }
7297     
7298     
7299 //    initEvents : function()
7300 //    {
7301 //        
7302 //        if(!this.store){
7303 //            return;
7304 //        }
7305 //        
7306 //        this.store = Roo.factory(this.store, Roo.data);
7307 //        this.store.on('load', this.onLoad, this);
7308 //        
7309 //        this.store.load();
7310 //        
7311 //    },
7312 //    
7313 //    onLoad: function () 
7314 //    {   
7315 //        this.fireEvent('load', this);
7316 //    }
7317 //    
7318 //   
7319 });
7320
7321  
7322
7323  /*
7324  * Based on:
7325  * Ext JS Library 1.1.1
7326  * Copyright(c) 2006-2007, Ext JS, LLC.
7327  *
7328  * Originally Released Under LGPL - original licence link has changed is not relivant.
7329  *
7330  * Fork - LGPL
7331  * <script type="text/javascript">
7332  */
7333
7334 // as we use this in bootstrap.
7335 Roo.namespace('Roo.form');
7336  /**
7337  * @class Roo.form.Action
7338  * Internal Class used to handle form actions
7339  * @constructor
7340  * @param {Roo.form.BasicForm} el The form element or its id
7341  * @param {Object} config Configuration options
7342  */
7343
7344  
7345  
7346 // define the action interface
7347 Roo.form.Action = function(form, options){
7348     this.form = form;
7349     this.options = options || {};
7350 };
7351 /**
7352  * Client Validation Failed
7353  * @const 
7354  */
7355 Roo.form.Action.CLIENT_INVALID = 'client';
7356 /**
7357  * Server Validation Failed
7358  * @const 
7359  */
7360 Roo.form.Action.SERVER_INVALID = 'server';
7361  /**
7362  * Connect to Server Failed
7363  * @const 
7364  */
7365 Roo.form.Action.CONNECT_FAILURE = 'connect';
7366 /**
7367  * Reading Data from Server Failed
7368  * @const 
7369  */
7370 Roo.form.Action.LOAD_FAILURE = 'load';
7371
7372 Roo.form.Action.prototype = {
7373     type : 'default',
7374     failureType : undefined,
7375     response : undefined,
7376     result : undefined,
7377
7378     // interface method
7379     run : function(options){
7380
7381     },
7382
7383     // interface method
7384     success : function(response){
7385
7386     },
7387
7388     // interface method
7389     handleResponse : function(response){
7390
7391     },
7392
7393     // default connection failure
7394     failure : function(response){
7395         
7396         this.response = response;
7397         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7398         this.form.afterAction(this, false);
7399     },
7400
7401     processResponse : function(response){
7402         this.response = response;
7403         if(!response.responseText){
7404             return true;
7405         }
7406         this.result = this.handleResponse(response);
7407         return this.result;
7408     },
7409
7410     // utility functions used internally
7411     getUrl : function(appendParams){
7412         var url = this.options.url || this.form.url || this.form.el.dom.action;
7413         if(appendParams){
7414             var p = this.getParams();
7415             if(p){
7416                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7417             }
7418         }
7419         return url;
7420     },
7421
7422     getMethod : function(){
7423         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7424     },
7425
7426     getParams : function(){
7427         var bp = this.form.baseParams;
7428         var p = this.options.params;
7429         if(p){
7430             if(typeof p == "object"){
7431                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7432             }else if(typeof p == 'string' && bp){
7433                 p += '&' + Roo.urlEncode(bp);
7434             }
7435         }else if(bp){
7436             p = Roo.urlEncode(bp);
7437         }
7438         return p;
7439     },
7440
7441     createCallback : function(){
7442         return {
7443             success: this.success,
7444             failure: this.failure,
7445             scope: this,
7446             timeout: (this.form.timeout*1000),
7447             upload: this.form.fileUpload ? this.success : undefined
7448         };
7449     }
7450 };
7451
7452 Roo.form.Action.Submit = function(form, options){
7453     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7454 };
7455
7456 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7457     type : 'submit',
7458
7459     haveProgress : false,
7460     uploadComplete : false,
7461     
7462     // uploadProgress indicator.
7463     uploadProgress : function()
7464     {
7465         if (!this.form.progressUrl) {
7466             return;
7467         }
7468         
7469         if (!this.haveProgress) {
7470             Roo.MessageBox.progress("Uploading", "Uploading");
7471         }
7472         if (this.uploadComplete) {
7473            Roo.MessageBox.hide();
7474            return;
7475         }
7476         
7477         this.haveProgress = true;
7478    
7479         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7480         
7481         var c = new Roo.data.Connection();
7482         c.request({
7483             url : this.form.progressUrl,
7484             params: {
7485                 id : uid
7486             },
7487             method: 'GET',
7488             success : function(req){
7489                //console.log(data);
7490                 var rdata = false;
7491                 var edata;
7492                 try  {
7493                    rdata = Roo.decode(req.responseText)
7494                 } catch (e) {
7495                     Roo.log("Invalid data from server..");
7496                     Roo.log(edata);
7497                     return;
7498                 }
7499                 if (!rdata || !rdata.success) {
7500                     Roo.log(rdata);
7501                     Roo.MessageBox.alert(Roo.encode(rdata));
7502                     return;
7503                 }
7504                 var data = rdata.data;
7505                 
7506                 if (this.uploadComplete) {
7507                    Roo.MessageBox.hide();
7508                    return;
7509                 }
7510                    
7511                 if (data){
7512                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7513                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7514                     );
7515                 }
7516                 this.uploadProgress.defer(2000,this);
7517             },
7518        
7519             failure: function(data) {
7520                 Roo.log('progress url failed ');
7521                 Roo.log(data);
7522             },
7523             scope : this
7524         });
7525            
7526     },
7527     
7528     
7529     run : function()
7530     {
7531         // run get Values on the form, so it syncs any secondary forms.
7532         this.form.getValues();
7533         
7534         var o = this.options;
7535         var method = this.getMethod();
7536         var isPost = method == 'POST';
7537         if(o.clientValidation === false || this.form.isValid()){
7538             
7539             if (this.form.progressUrl) {
7540                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7541                     (new Date() * 1) + '' + Math.random());
7542                     
7543             } 
7544             
7545             
7546             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7547                 form:this.form.el.dom,
7548                 url:this.getUrl(!isPost),
7549                 method: method,
7550                 params:isPost ? this.getParams() : null,
7551                 isUpload: this.form.fileUpload
7552             }));
7553             
7554             this.uploadProgress();
7555
7556         }else if (o.clientValidation !== false){ // client validation failed
7557             this.failureType = Roo.form.Action.CLIENT_INVALID;
7558             this.form.afterAction(this, false);
7559         }
7560     },
7561
7562     success : function(response)
7563     {
7564         this.uploadComplete= true;
7565         if (this.haveProgress) {
7566             Roo.MessageBox.hide();
7567         }
7568         
7569         
7570         var result = this.processResponse(response);
7571         if(result === true || result.success){
7572             this.form.afterAction(this, true);
7573             return;
7574         }
7575         if(result.errors){
7576             this.form.markInvalid(result.errors);
7577             this.failureType = Roo.form.Action.SERVER_INVALID;
7578         }
7579         this.form.afterAction(this, false);
7580     },
7581     failure : function(response)
7582     {
7583         this.uploadComplete= true;
7584         if (this.haveProgress) {
7585             Roo.MessageBox.hide();
7586         }
7587         
7588         this.response = response;
7589         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7590         this.form.afterAction(this, false);
7591     },
7592     
7593     handleResponse : function(response){
7594         if(this.form.errorReader){
7595             var rs = this.form.errorReader.read(response);
7596             var errors = [];
7597             if(rs.records){
7598                 for(var i = 0, len = rs.records.length; i < len; i++) {
7599                     var r = rs.records[i];
7600                     errors[i] = r.data;
7601                 }
7602             }
7603             if(errors.length < 1){
7604                 errors = null;
7605             }
7606             return {
7607                 success : rs.success,
7608                 errors : errors
7609             };
7610         }
7611         var ret = false;
7612         try {
7613             ret = Roo.decode(response.responseText);
7614         } catch (e) {
7615             ret = {
7616                 success: false,
7617                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7618                 errors : []
7619             };
7620         }
7621         return ret;
7622         
7623     }
7624 });
7625
7626
7627 Roo.form.Action.Load = function(form, options){
7628     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7629     this.reader = this.form.reader;
7630 };
7631
7632 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7633     type : 'load',
7634
7635     run : function(){
7636         
7637         Roo.Ajax.request(Roo.apply(
7638                 this.createCallback(), {
7639                     method:this.getMethod(),
7640                     url:this.getUrl(false),
7641                     params:this.getParams()
7642         }));
7643     },
7644
7645     success : function(response){
7646         
7647         var result = this.processResponse(response);
7648         if(result === true || !result.success || !result.data){
7649             this.failureType = Roo.form.Action.LOAD_FAILURE;
7650             this.form.afterAction(this, false);
7651             return;
7652         }
7653         this.form.clearInvalid();
7654         this.form.setValues(result.data);
7655         this.form.afterAction(this, true);
7656     },
7657
7658     handleResponse : function(response){
7659         if(this.form.reader){
7660             var rs = this.form.reader.read(response);
7661             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7662             return {
7663                 success : rs.success,
7664                 data : data
7665             };
7666         }
7667         return Roo.decode(response.responseText);
7668     }
7669 });
7670
7671 Roo.form.Action.ACTION_TYPES = {
7672     'load' : Roo.form.Action.Load,
7673     'submit' : Roo.form.Action.Submit
7674 };/*
7675  * - LGPL
7676  *
7677  * form
7678  *
7679  */
7680
7681 /**
7682  * @class Roo.bootstrap.Form
7683  * @extends Roo.bootstrap.Component
7684  * Bootstrap Form class
7685  * @cfg {String} method  GET | POST (default POST)
7686  * @cfg {String} labelAlign top | left (default top)
7687  * @cfg {String} align left  | right - for navbars
7688  * @cfg {Boolean} loadMask load mask when submit (default true)
7689
7690  *
7691  * @constructor
7692  * Create a new Form
7693  * @param {Object} config The config object
7694  */
7695
7696
7697 Roo.bootstrap.Form = function(config){
7698     
7699     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7700     
7701     Roo.bootstrap.Form.popover.apply();
7702     
7703     this.addEvents({
7704         /**
7705          * @event clientvalidation
7706          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7707          * @param {Form} this
7708          * @param {Boolean} valid true if the form has passed client-side validation
7709          */
7710         clientvalidation: true,
7711         /**
7712          * @event beforeaction
7713          * Fires before any action is performed. Return false to cancel the action.
7714          * @param {Form} this
7715          * @param {Action} action The action to be performed
7716          */
7717         beforeaction: true,
7718         /**
7719          * @event actionfailed
7720          * Fires when an action fails.
7721          * @param {Form} this
7722          * @param {Action} action The action that failed
7723          */
7724         actionfailed : true,
7725         /**
7726          * @event actioncomplete
7727          * Fires when an action is completed.
7728          * @param {Form} this
7729          * @param {Action} action The action that completed
7730          */
7731         actioncomplete : true
7732     });
7733 };
7734
7735 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7736
7737      /**
7738      * @cfg {String} method
7739      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7740      */
7741     method : 'POST',
7742     /**
7743      * @cfg {String} url
7744      * The URL to use for form actions if one isn't supplied in the action options.
7745      */
7746     /**
7747      * @cfg {Boolean} fileUpload
7748      * Set to true if this form is a file upload.
7749      */
7750
7751     /**
7752      * @cfg {Object} baseParams
7753      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7754      */
7755
7756     /**
7757      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7758      */
7759     timeout: 30,
7760     /**
7761      * @cfg {Sting} align (left|right) for navbar forms
7762      */
7763     align : 'left',
7764
7765     // private
7766     activeAction : null,
7767
7768     /**
7769      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7770      * element by passing it or its id or mask the form itself by passing in true.
7771      * @type Mixed
7772      */
7773     waitMsgTarget : false,
7774
7775     loadMask : true,
7776     
7777     /**
7778      * @cfg {Boolean} errorMask (true|false) default false
7779      */
7780     errorMask : false,
7781     
7782     /**
7783      * @cfg {Number} maskOffset Default 100
7784      */
7785     maskOffset : 100,
7786     
7787     /**
7788      * @cfg {Boolean} maskBody
7789      */
7790     maskBody : false,
7791
7792     getAutoCreate : function(){
7793
7794         var cfg = {
7795             tag: 'form',
7796             method : this.method || 'POST',
7797             id : this.id || Roo.id(),
7798             cls : ''
7799         };
7800         if (this.parent().xtype.match(/^Nav/)) {
7801             cfg.cls = 'navbar-form navbar-' + this.align;
7802
7803         }
7804
7805         if (this.labelAlign == 'left' ) {
7806             cfg.cls += ' form-horizontal';
7807         }
7808
7809
7810         return cfg;
7811     },
7812     initEvents : function()
7813     {
7814         this.el.on('submit', this.onSubmit, this);
7815         // this was added as random key presses on the form where triggering form submit.
7816         this.el.on('keypress', function(e) {
7817             if (e.getCharCode() != 13) {
7818                 return true;
7819             }
7820             // we might need to allow it for textareas.. and some other items.
7821             // check e.getTarget().
7822
7823             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7824                 return true;
7825             }
7826
7827             Roo.log("keypress blocked");
7828
7829             e.preventDefault();
7830             return false;
7831         });
7832         
7833     },
7834     // private
7835     onSubmit : function(e){
7836         e.stopEvent();
7837     },
7838
7839      /**
7840      * Returns true if client-side validation on the form is successful.
7841      * @return Boolean
7842      */
7843     isValid : function(){
7844         var items = this.getItems();
7845         var valid = true;
7846         var target = false;
7847         
7848         items.each(function(f){
7849             
7850             if(f.validate()){
7851                 return;
7852             }
7853             
7854             valid = false;
7855
7856             if(!target && f.el.isVisible(true)){
7857                 target = f;
7858             }
7859            
7860         });
7861         
7862         if(this.errorMask && !valid){
7863             Roo.bootstrap.Form.popover.mask(this, target);
7864         }
7865         
7866         return valid;
7867     },
7868     
7869     /**
7870      * Returns true if any fields in this form have changed since their original load.
7871      * @return Boolean
7872      */
7873     isDirty : function(){
7874         var dirty = false;
7875         var items = this.getItems();
7876         items.each(function(f){
7877            if(f.isDirty()){
7878                dirty = true;
7879                return false;
7880            }
7881            return true;
7882         });
7883         return dirty;
7884     },
7885      /**
7886      * Performs a predefined action (submit or load) or custom actions you define on this form.
7887      * @param {String} actionName The name of the action type
7888      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7889      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7890      * accept other config options):
7891      * <pre>
7892 Property          Type             Description
7893 ----------------  ---------------  ----------------------------------------------------------------------------------
7894 url               String           The url for the action (defaults to the form's url)
7895 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7896 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7897 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7898                                    validate the form on the client (defaults to false)
7899      * </pre>
7900      * @return {BasicForm} this
7901      */
7902     doAction : function(action, options){
7903         if(typeof action == 'string'){
7904             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7905         }
7906         if(this.fireEvent('beforeaction', this, action) !== false){
7907             this.beforeAction(action);
7908             action.run.defer(100, action);
7909         }
7910         return this;
7911     },
7912
7913     // private
7914     beforeAction : function(action){
7915         var o = action.options;
7916         
7917         if(this.loadMask){
7918             
7919             if(this.maskBody){
7920                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7921             } else {
7922                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7923             }
7924         }
7925         // not really supported yet.. ??
7926
7927         //if(this.waitMsgTarget === true){
7928         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7929         //}else if(this.waitMsgTarget){
7930         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7931         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7932         //}else {
7933         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7934        // }
7935
7936     },
7937
7938     // private
7939     afterAction : function(action, success){
7940         this.activeAction = null;
7941         var o = action.options;
7942
7943         if(this.loadMask){
7944             
7945             if(this.maskBody){
7946                 Roo.get(document.body).unmask();
7947             } else {
7948                 this.el.unmask();
7949             }
7950         }
7951         
7952         //if(this.waitMsgTarget === true){
7953 //            this.el.unmask();
7954         //}else if(this.waitMsgTarget){
7955         //    this.waitMsgTarget.unmask();
7956         //}else{
7957         //    Roo.MessageBox.updateProgress(1);
7958         //    Roo.MessageBox.hide();
7959        // }
7960         //
7961         if(success){
7962             if(o.reset){
7963                 this.reset();
7964             }
7965             Roo.callback(o.success, o.scope, [this, action]);
7966             this.fireEvent('actioncomplete', this, action);
7967
7968         }else{
7969
7970             // failure condition..
7971             // we have a scenario where updates need confirming.
7972             // eg. if a locking scenario exists..
7973             // we look for { errors : { needs_confirm : true }} in the response.
7974             if (
7975                 (typeof(action.result) != 'undefined')  &&
7976                 (typeof(action.result.errors) != 'undefined')  &&
7977                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7978            ){
7979                 var _t = this;
7980                 Roo.log("not supported yet");
7981                  /*
7982
7983                 Roo.MessageBox.confirm(
7984                     "Change requires confirmation",
7985                     action.result.errorMsg,
7986                     function(r) {
7987                         if (r != 'yes') {
7988                             return;
7989                         }
7990                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7991                     }
7992
7993                 );
7994                 */
7995
7996
7997                 return;
7998             }
7999
8000             Roo.callback(o.failure, o.scope, [this, action]);
8001             // show an error message if no failed handler is set..
8002             if (!this.hasListener('actionfailed')) {
8003                 Roo.log("need to add dialog support");
8004                 /*
8005                 Roo.MessageBox.alert("Error",
8006                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8007                         action.result.errorMsg :
8008                         "Saving Failed, please check your entries or try again"
8009                 );
8010                 */
8011             }
8012
8013             this.fireEvent('actionfailed', this, action);
8014         }
8015
8016     },
8017     /**
8018      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8019      * @param {String} id The value to search for
8020      * @return Field
8021      */
8022     findField : function(id){
8023         var items = this.getItems();
8024         var field = items.get(id);
8025         if(!field){
8026              items.each(function(f){
8027                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8028                     field = f;
8029                     return false;
8030                 }
8031                 return true;
8032             });
8033         }
8034         return field || null;
8035     },
8036      /**
8037      * Mark fields in this form invalid in bulk.
8038      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8039      * @return {BasicForm} this
8040      */
8041     markInvalid : function(errors){
8042         if(errors instanceof Array){
8043             for(var i = 0, len = errors.length; i < len; i++){
8044                 var fieldError = errors[i];
8045                 var f = this.findField(fieldError.id);
8046                 if(f){
8047                     f.markInvalid(fieldError.msg);
8048                 }
8049             }
8050         }else{
8051             var field, id;
8052             for(id in errors){
8053                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8054                     field.markInvalid(errors[id]);
8055                 }
8056             }
8057         }
8058         //Roo.each(this.childForms || [], function (f) {
8059         //    f.markInvalid(errors);
8060         //});
8061
8062         return this;
8063     },
8064
8065     /**
8066      * Set values for fields in this form in bulk.
8067      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8068      * @return {BasicForm} this
8069      */
8070     setValues : function(values){
8071         if(values instanceof Array){ // array of objects
8072             for(var i = 0, len = values.length; i < len; i++){
8073                 var v = values[i];
8074                 var f = this.findField(v.id);
8075                 if(f){
8076                     f.setValue(v.value);
8077                     if(this.trackResetOnLoad){
8078                         f.originalValue = f.getValue();
8079                     }
8080                 }
8081             }
8082         }else{ // object hash
8083             var field, id;
8084             for(id in values){
8085                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8086
8087                     if (field.setFromData &&
8088                         field.valueField &&
8089                         field.displayField &&
8090                         // combos' with local stores can
8091                         // be queried via setValue()
8092                         // to set their value..
8093                         (field.store && !field.store.isLocal)
8094                         ) {
8095                         // it's a combo
8096                         var sd = { };
8097                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8098                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8099                         field.setFromData(sd);
8100
8101                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8102                         
8103                         field.setFromData(values);
8104                         
8105                     } else {
8106                         field.setValue(values[id]);
8107                     }
8108
8109
8110                     if(this.trackResetOnLoad){
8111                         field.originalValue = field.getValue();
8112                     }
8113                 }
8114             }
8115         }
8116
8117         //Roo.each(this.childForms || [], function (f) {
8118         //    f.setValues(values);
8119         //});
8120
8121         return this;
8122     },
8123
8124     /**
8125      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8126      * they are returned as an array.
8127      * @param {Boolean} asString
8128      * @return {Object}
8129      */
8130     getValues : function(asString){
8131         //if (this.childForms) {
8132             // copy values from the child forms
8133         //    Roo.each(this.childForms, function (f) {
8134         //        this.setValues(f.getValues());
8135         //    }, this);
8136         //}
8137
8138
8139
8140         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8141         if(asString === true){
8142             return fs;
8143         }
8144         return Roo.urlDecode(fs);
8145     },
8146
8147     /**
8148      * Returns the fields in this form as an object with key/value pairs.
8149      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8150      * @return {Object}
8151      */
8152     getFieldValues : function(with_hidden)
8153     {
8154         var items = this.getItems();
8155         var ret = {};
8156         items.each(function(f){
8157             
8158             if (!f.getName()) {
8159                 return;
8160             }
8161             
8162             var v = f.getValue();
8163             
8164             if (f.inputType =='radio') {
8165                 if (typeof(ret[f.getName()]) == 'undefined') {
8166                     ret[f.getName()] = ''; // empty..
8167                 }
8168
8169                 if (!f.el.dom.checked) {
8170                     return;
8171
8172                 }
8173                 v = f.el.dom.value;
8174
8175             }
8176             
8177             if(f.xtype == 'MoneyField'){
8178                 ret[f.currencyName] = f.getCurrency();
8179             }
8180
8181             // not sure if this supported any more..
8182             if ((typeof(v) == 'object') && f.getRawValue) {
8183                 v = f.getRawValue() ; // dates..
8184             }
8185             // combo boxes where name != hiddenName...
8186             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8187                 ret[f.name] = f.getRawValue();
8188             }
8189             ret[f.getName()] = v;
8190         });
8191
8192         return ret;
8193     },
8194
8195     /**
8196      * Clears all invalid messages in this form.
8197      * @return {BasicForm} this
8198      */
8199     clearInvalid : function(){
8200         var items = this.getItems();
8201
8202         items.each(function(f){
8203            f.clearInvalid();
8204         });
8205
8206         return this;
8207     },
8208
8209     /**
8210      * Resets this form.
8211      * @return {BasicForm} this
8212      */
8213     reset : function(){
8214         var items = this.getItems();
8215         items.each(function(f){
8216             f.reset();
8217         });
8218
8219         Roo.each(this.childForms || [], function (f) {
8220             f.reset();
8221         });
8222
8223
8224         return this;
8225     },
8226     
8227     getItems : function()
8228     {
8229         var r=new Roo.util.MixedCollection(false, function(o){
8230             return o.id || (o.id = Roo.id());
8231         });
8232         var iter = function(el) {
8233             if (el.inputEl) {
8234                 r.add(el);
8235             }
8236             if (!el.items) {
8237                 return;
8238             }
8239             Roo.each(el.items,function(e) {
8240                 iter(e);
8241             });
8242         };
8243
8244         iter(this);
8245         return r;
8246     },
8247     
8248     hideFields : function(items)
8249     {
8250         Roo.each(items, function(i){
8251             
8252             var f = this.findField(i);
8253             
8254             if(!f){
8255                 return;
8256             }
8257             
8258             if(f.xtype == 'DateField'){
8259                 f.setVisible(false);
8260                 return;
8261             }
8262             
8263             f.hide();
8264             
8265         }, this);
8266     },
8267     
8268     showFields : function(items)
8269     {
8270         Roo.each(items, function(i){
8271             
8272             var f = this.findField(i);
8273             
8274             if(!f){
8275                 return;
8276             }
8277             
8278             if(f.xtype == 'DateField'){
8279                 f.setVisible(true);
8280                 return;
8281             }
8282             
8283             f.show();
8284             
8285         }, this);
8286     }
8287
8288 });
8289
8290 Roo.apply(Roo.bootstrap.Form, {
8291     
8292     popover : {
8293         
8294         padding : 5,
8295         
8296         isApplied : false,
8297         
8298         isMasked : false,
8299         
8300         form : false,
8301         
8302         target : false,
8303         
8304         toolTip : false,
8305         
8306         intervalID : false,
8307         
8308         maskEl : false,
8309         
8310         apply : function()
8311         {
8312             if(this.isApplied){
8313                 return;
8314             }
8315             
8316             this.maskEl = {
8317                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8318                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8319                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8320                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8321             };
8322             
8323             this.maskEl.top.enableDisplayMode("block");
8324             this.maskEl.left.enableDisplayMode("block");
8325             this.maskEl.bottom.enableDisplayMode("block");
8326             this.maskEl.right.enableDisplayMode("block");
8327             
8328             this.toolTip = new Roo.bootstrap.Tooltip({
8329                 cls : 'roo-form-error-popover',
8330                 alignment : {
8331                     'left' : ['r-l', [-2,0], 'right'],
8332                     'right' : ['l-r', [2,0], 'left'],
8333                     'bottom' : ['tl-bl', [0,2], 'top'],
8334                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8335                 }
8336             });
8337             
8338             this.toolTip.render(Roo.get(document.body));
8339
8340             this.toolTip.el.enableDisplayMode("block");
8341             
8342             Roo.get(document.body).on('click', function(){
8343                 this.unmask();
8344             }, this);
8345             
8346             Roo.get(document.body).on('touchstart', function(){
8347                 this.unmask();
8348             }, this);
8349             
8350             this.isApplied = true
8351         },
8352         
8353         mask : function(form, target)
8354         {
8355             this.form = form;
8356             
8357             this.target = target;
8358             
8359             if(!this.form.errorMask || !target.el){
8360                 return;
8361             }
8362             
8363             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8364             
8365             Roo.log(scrollable);
8366             
8367             var ot = this.target.el.calcOffsetsTo(scrollable);
8368             
8369             var scrollTo = ot[1] - this.form.maskOffset;
8370             
8371             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8372             
8373             scrollable.scrollTo('top', scrollTo);
8374             
8375             var box = this.target.el.getBox();
8376             Roo.log(box);
8377             var zIndex = Roo.bootstrap.Modal.zIndex++;
8378
8379             
8380             this.maskEl.top.setStyle('position', 'absolute');
8381             this.maskEl.top.setStyle('z-index', zIndex);
8382             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8383             this.maskEl.top.setLeft(0);
8384             this.maskEl.top.setTop(0);
8385             this.maskEl.top.show();
8386             
8387             this.maskEl.left.setStyle('position', 'absolute');
8388             this.maskEl.left.setStyle('z-index', zIndex);
8389             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8390             this.maskEl.left.setLeft(0);
8391             this.maskEl.left.setTop(box.y - this.padding);
8392             this.maskEl.left.show();
8393
8394             this.maskEl.bottom.setStyle('position', 'absolute');
8395             this.maskEl.bottom.setStyle('z-index', zIndex);
8396             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8397             this.maskEl.bottom.setLeft(0);
8398             this.maskEl.bottom.setTop(box.bottom + this.padding);
8399             this.maskEl.bottom.show();
8400
8401             this.maskEl.right.setStyle('position', 'absolute');
8402             this.maskEl.right.setStyle('z-index', zIndex);
8403             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8404             this.maskEl.right.setLeft(box.right + this.padding);
8405             this.maskEl.right.setTop(box.y - this.padding);
8406             this.maskEl.right.show();
8407
8408             this.toolTip.bindEl = this.target.el;
8409
8410             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8411
8412             var tip = this.target.blankText;
8413
8414             if(this.target.getValue() !== '' ) {
8415                 
8416                 if (this.target.invalidText.length) {
8417                     tip = this.target.invalidText;
8418                 } else if (this.target.regexText.length){
8419                     tip = this.target.regexText;
8420                 }
8421             }
8422
8423             this.toolTip.show(tip);
8424
8425             this.intervalID = window.setInterval(function() {
8426                 Roo.bootstrap.Form.popover.unmask();
8427             }, 10000);
8428
8429             window.onwheel = function(){ return false;};
8430             
8431             (function(){ this.isMasked = true; }).defer(500, this);
8432             
8433         },
8434         
8435         unmask : function()
8436         {
8437             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8438                 return;
8439             }
8440             
8441             this.maskEl.top.setStyle('position', 'absolute');
8442             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8443             this.maskEl.top.hide();
8444
8445             this.maskEl.left.setStyle('position', 'absolute');
8446             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8447             this.maskEl.left.hide();
8448
8449             this.maskEl.bottom.setStyle('position', 'absolute');
8450             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8451             this.maskEl.bottom.hide();
8452
8453             this.maskEl.right.setStyle('position', 'absolute');
8454             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8455             this.maskEl.right.hide();
8456             
8457             this.toolTip.hide();
8458             
8459             this.toolTip.el.hide();
8460             
8461             window.onwheel = function(){ return true;};
8462             
8463             if(this.intervalID){
8464                 window.clearInterval(this.intervalID);
8465                 this.intervalID = false;
8466             }
8467             
8468             this.isMasked = false;
8469             
8470         }
8471         
8472     }
8473     
8474 });
8475
8476 /*
8477  * Based on:
8478  * Ext JS Library 1.1.1
8479  * Copyright(c) 2006-2007, Ext JS, LLC.
8480  *
8481  * Originally Released Under LGPL - original licence link has changed is not relivant.
8482  *
8483  * Fork - LGPL
8484  * <script type="text/javascript">
8485  */
8486 /**
8487  * @class Roo.form.VTypes
8488  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8489  * @singleton
8490  */
8491 Roo.form.VTypes = function(){
8492     // closure these in so they are only created once.
8493     var alpha = /^[a-zA-Z_]+$/;
8494     var alphanum = /^[a-zA-Z0-9_]+$/;
8495     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8496     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8497
8498     // All these messages and functions are configurable
8499     return {
8500         /**
8501          * The function used to validate email addresses
8502          * @param {String} value The email address
8503          */
8504         'email' : function(v){
8505             return email.test(v);
8506         },
8507         /**
8508          * The error text to display when the email validation function returns false
8509          * @type String
8510          */
8511         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8512         /**
8513          * The keystroke filter mask to be applied on email input
8514          * @type RegExp
8515          */
8516         'emailMask' : /[a-z0-9_\.\-@]/i,
8517
8518         /**
8519          * The function used to validate URLs
8520          * @param {String} value The URL
8521          */
8522         'url' : function(v){
8523             return url.test(v);
8524         },
8525         /**
8526          * The error text to display when the url validation function returns false
8527          * @type String
8528          */
8529         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8530         
8531         /**
8532          * The function used to validate alpha values
8533          * @param {String} value The value
8534          */
8535         'alpha' : function(v){
8536             return alpha.test(v);
8537         },
8538         /**
8539          * The error text to display when the alpha validation function returns false
8540          * @type String
8541          */
8542         'alphaText' : 'This field should only contain letters and _',
8543         /**
8544          * The keystroke filter mask to be applied on alpha input
8545          * @type RegExp
8546          */
8547         'alphaMask' : /[a-z_]/i,
8548
8549         /**
8550          * The function used to validate alphanumeric values
8551          * @param {String} value The value
8552          */
8553         'alphanum' : function(v){
8554             return alphanum.test(v);
8555         },
8556         /**
8557          * The error text to display when the alphanumeric validation function returns false
8558          * @type String
8559          */
8560         'alphanumText' : 'This field should only contain letters, numbers and _',
8561         /**
8562          * The keystroke filter mask to be applied on alphanumeric input
8563          * @type RegExp
8564          */
8565         'alphanumMask' : /[a-z0-9_]/i
8566     };
8567 }();/*
8568  * - LGPL
8569  *
8570  * Input
8571  * 
8572  */
8573
8574 /**
8575  * @class Roo.bootstrap.Input
8576  * @extends Roo.bootstrap.Component
8577  * Bootstrap Input class
8578  * @cfg {Boolean} disabled is it disabled
8579  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8580  * @cfg {String} name name of the input
8581  * @cfg {string} fieldLabel - the label associated
8582  * @cfg {string} placeholder - placeholder to put in text.
8583  * @cfg {string}  before - input group add on before
8584  * @cfg {string} after - input group add on after
8585  * @cfg {string} size - (lg|sm) or leave empty..
8586  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8587  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8588  * @cfg {Number} md colspan out of 12 for computer-sized screens
8589  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8590  * @cfg {string} value default value of the input
8591  * @cfg {Number} labelWidth set the width of label 
8592  * @cfg {Number} labellg set the width of label (1-12)
8593  * @cfg {Number} labelmd set the width of label (1-12)
8594  * @cfg {Number} labelsm set the width of label (1-12)
8595  * @cfg {Number} labelxs set the width of label (1-12)
8596  * @cfg {String} labelAlign (top|left)
8597  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8598  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8599  * @cfg {String} indicatorpos (left|right) default left
8600  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8601  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8602
8603  * @cfg {String} align (left|center|right) Default left
8604  * @cfg {Boolean} forceFeedback (true|false) Default false
8605  * 
8606  * @constructor
8607  * Create a new Input
8608  * @param {Object} config The config object
8609  */
8610
8611 Roo.bootstrap.Input = function(config){
8612     
8613     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8614     
8615     this.addEvents({
8616         /**
8617          * @event focus
8618          * Fires when this field receives input focus.
8619          * @param {Roo.form.Field} this
8620          */
8621         focus : true,
8622         /**
8623          * @event blur
8624          * Fires when this field loses input focus.
8625          * @param {Roo.form.Field} this
8626          */
8627         blur : true,
8628         /**
8629          * @event specialkey
8630          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8631          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8632          * @param {Roo.form.Field} this
8633          * @param {Roo.EventObject} e The event object
8634          */
8635         specialkey : true,
8636         /**
8637          * @event change
8638          * Fires just before the field blurs if the field value has changed.
8639          * @param {Roo.form.Field} this
8640          * @param {Mixed} newValue The new value
8641          * @param {Mixed} oldValue The original value
8642          */
8643         change : true,
8644         /**
8645          * @event invalid
8646          * Fires after the field has been marked as invalid.
8647          * @param {Roo.form.Field} this
8648          * @param {String} msg The validation message
8649          */
8650         invalid : true,
8651         /**
8652          * @event valid
8653          * Fires after the field has been validated with no errors.
8654          * @param {Roo.form.Field} this
8655          */
8656         valid : true,
8657          /**
8658          * @event keyup
8659          * Fires after the key up
8660          * @param {Roo.form.Field} this
8661          * @param {Roo.EventObject}  e The event Object
8662          */
8663         keyup : true
8664     });
8665 };
8666
8667 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8668      /**
8669      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8670       automatic validation (defaults to "keyup").
8671      */
8672     validationEvent : "keyup",
8673      /**
8674      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8675      */
8676     validateOnBlur : true,
8677     /**
8678      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8679      */
8680     validationDelay : 250,
8681      /**
8682      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8683      */
8684     focusClass : "x-form-focus",  // not needed???
8685     
8686        
8687     /**
8688      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8689      */
8690     invalidClass : "has-warning",
8691     
8692     /**
8693      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8694      */
8695     validClass : "has-success",
8696     
8697     /**
8698      * @cfg {Boolean} hasFeedback (true|false) default true
8699      */
8700     hasFeedback : true,
8701     
8702     /**
8703      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8704      */
8705     invalidFeedbackClass : "glyphicon-warning-sign",
8706     
8707     /**
8708      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8709      */
8710     validFeedbackClass : "glyphicon-ok",
8711     
8712     /**
8713      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8714      */
8715     selectOnFocus : false,
8716     
8717      /**
8718      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8719      */
8720     maskRe : null,
8721        /**
8722      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8723      */
8724     vtype : null,
8725     
8726       /**
8727      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8728      */
8729     disableKeyFilter : false,
8730     
8731        /**
8732      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8733      */
8734     disabled : false,
8735      /**
8736      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8737      */
8738     allowBlank : true,
8739     /**
8740      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8741      */
8742     blankText : "Please complete this mandatory field",
8743     
8744      /**
8745      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8746      */
8747     minLength : 0,
8748     /**
8749      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8750      */
8751     maxLength : Number.MAX_VALUE,
8752     /**
8753      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8754      */
8755     minLengthText : "The minimum length for this field is {0}",
8756     /**
8757      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8758      */
8759     maxLengthText : "The maximum length for this field is {0}",
8760   
8761     
8762     /**
8763      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8764      * If available, this function will be called only after the basic validators all return true, and will be passed the
8765      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8766      */
8767     validator : null,
8768     /**
8769      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8770      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8771      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8772      */
8773     regex : null,
8774     /**
8775      * @cfg {String} regexText -- Depricated - use Invalid Text
8776      */
8777     regexText : "",
8778     
8779     /**
8780      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8781      */
8782     invalidText : "",
8783     
8784     
8785     
8786     autocomplete: false,
8787     
8788     
8789     fieldLabel : '',
8790     inputType : 'text',
8791     
8792     name : false,
8793     placeholder: false,
8794     before : false,
8795     after : false,
8796     size : false,
8797     hasFocus : false,
8798     preventMark: false,
8799     isFormField : true,
8800     value : '',
8801     labelWidth : 2,
8802     labelAlign : false,
8803     readOnly : false,
8804     align : false,
8805     formatedValue : false,
8806     forceFeedback : false,
8807     
8808     indicatorpos : 'left',
8809     
8810     labellg : 0,
8811     labelmd : 0,
8812     labelsm : 0,
8813     labelxs : 0,
8814     
8815     capture : '',
8816     accept : '',
8817     
8818     parentLabelAlign : function()
8819     {
8820         var parent = this;
8821         while (parent.parent()) {
8822             parent = parent.parent();
8823             if (typeof(parent.labelAlign) !='undefined') {
8824                 return parent.labelAlign;
8825             }
8826         }
8827         return 'left';
8828         
8829     },
8830     
8831     getAutoCreate : function()
8832     {
8833         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8834         
8835         var id = Roo.id();
8836         
8837         var cfg = {};
8838         
8839         if(this.inputType != 'hidden'){
8840             cfg.cls = 'form-group' //input-group
8841         }
8842         
8843         var input =  {
8844             tag: 'input',
8845             id : id,
8846             type : this.inputType,
8847             value : this.value,
8848             cls : 'form-control',
8849             placeholder : this.placeholder || '',
8850             autocomplete : this.autocomplete || 'new-password'
8851         };
8852         
8853         if(this.capture.length){
8854             input.capture = this.capture;
8855         }
8856         
8857         if(this.accept.length){
8858             input.accept = this.accept + "/*";
8859         }
8860         
8861         if(this.align){
8862             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8863         }
8864         
8865         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8866             input.maxLength = this.maxLength;
8867         }
8868         
8869         if (this.disabled) {
8870             input.disabled=true;
8871         }
8872         
8873         if (this.readOnly) {
8874             input.readonly=true;
8875         }
8876         
8877         if (this.name) {
8878             input.name = this.name;
8879         }
8880         
8881         if (this.size) {
8882             input.cls += ' input-' + this.size;
8883         }
8884         
8885         var settings=this;
8886         ['xs','sm','md','lg'].map(function(size){
8887             if (settings[size]) {
8888                 cfg.cls += ' col-' + size + '-' + settings[size];
8889             }
8890         });
8891         
8892         var inputblock = input;
8893         
8894         var feedback = {
8895             tag: 'span',
8896             cls: 'glyphicon form-control-feedback'
8897         };
8898             
8899         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8900             
8901             inputblock = {
8902                 cls : 'has-feedback',
8903                 cn :  [
8904                     input,
8905                     feedback
8906                 ] 
8907             };  
8908         }
8909         
8910         if (this.before || this.after) {
8911             
8912             inputblock = {
8913                 cls : 'input-group',
8914                 cn :  [] 
8915             };
8916             
8917             if (this.before && typeof(this.before) == 'string') {
8918                 
8919                 inputblock.cn.push({
8920                     tag :'span',
8921                     cls : 'roo-input-before input-group-addon',
8922                     html : this.before
8923                 });
8924             }
8925             if (this.before && typeof(this.before) == 'object') {
8926                 this.before = Roo.factory(this.before);
8927                 
8928                 inputblock.cn.push({
8929                     tag :'span',
8930                     cls : 'roo-input-before input-group-' +
8931                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8932                 });
8933             }
8934             
8935             inputblock.cn.push(input);
8936             
8937             if (this.after && typeof(this.after) == 'string') {
8938                 inputblock.cn.push({
8939                     tag :'span',
8940                     cls : 'roo-input-after input-group-addon',
8941                     html : this.after
8942                 });
8943             }
8944             if (this.after && typeof(this.after) == 'object') {
8945                 this.after = Roo.factory(this.after);
8946                 
8947                 inputblock.cn.push({
8948                     tag :'span',
8949                     cls : 'roo-input-after input-group-' +
8950                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8951                 });
8952             }
8953             
8954             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8955                 inputblock.cls += ' has-feedback';
8956                 inputblock.cn.push(feedback);
8957             }
8958         };
8959         
8960         if (align ==='left' && this.fieldLabel.length) {
8961             
8962             cfg.cls += ' roo-form-group-label-left';
8963             
8964             cfg.cn = [
8965                 {
8966                     tag : 'i',
8967                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8968                     tooltip : 'This field is required'
8969                 },
8970                 {
8971                     tag: 'label',
8972                     'for' :  id,
8973                     cls : 'control-label',
8974                     html : this.fieldLabel
8975
8976                 },
8977                 {
8978                     cls : "", 
8979                     cn: [
8980                         inputblock
8981                     ]
8982                 }
8983             ];
8984             
8985             var labelCfg = cfg.cn[1];
8986             var contentCfg = cfg.cn[2];
8987             
8988             if(this.indicatorpos == 'right'){
8989                 cfg.cn = [
8990                     {
8991                         tag: 'label',
8992                         'for' :  id,
8993                         cls : 'control-label',
8994                         cn : [
8995                             {
8996                                 tag : 'span',
8997                                 html : this.fieldLabel
8998                             },
8999                             {
9000                                 tag : 'i',
9001                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9002                                 tooltip : 'This field is required'
9003                             }
9004                         ]
9005                     },
9006                     {
9007                         cls : "",
9008                         cn: [
9009                             inputblock
9010                         ]
9011                     }
9012
9013                 ];
9014                 
9015                 labelCfg = cfg.cn[0];
9016                 contentCfg = cfg.cn[1];
9017             
9018             }
9019             
9020             if(this.labelWidth > 12){
9021                 labelCfg.style = "width: " + this.labelWidth + 'px';
9022             }
9023             
9024             if(this.labelWidth < 13 && this.labelmd == 0){
9025                 this.labelmd = this.labelWidth;
9026             }
9027             
9028             if(this.labellg > 0){
9029                 labelCfg.cls += ' col-lg-' + this.labellg;
9030                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9031             }
9032             
9033             if(this.labelmd > 0){
9034                 labelCfg.cls += ' col-md-' + this.labelmd;
9035                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9036             }
9037             
9038             if(this.labelsm > 0){
9039                 labelCfg.cls += ' col-sm-' + this.labelsm;
9040                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9041             }
9042             
9043             if(this.labelxs > 0){
9044                 labelCfg.cls += ' col-xs-' + this.labelxs;
9045                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9046             }
9047             
9048             
9049         } else if ( this.fieldLabel.length) {
9050                 
9051             cfg.cn = [
9052                 {
9053                     tag : 'i',
9054                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9055                     tooltip : 'This field is required'
9056                 },
9057                 {
9058                     tag: 'label',
9059                    //cls : 'input-group-addon',
9060                     html : this.fieldLabel
9061
9062                 },
9063
9064                inputblock
9065
9066            ];
9067            
9068            if(this.indicatorpos == 'right'){
9069                 
9070                 cfg.cn = [
9071                     {
9072                         tag: 'label',
9073                        //cls : 'input-group-addon',
9074                         html : this.fieldLabel
9075
9076                     },
9077                     {
9078                         tag : 'i',
9079                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9080                         tooltip : 'This field is required'
9081                     },
9082
9083                    inputblock
9084
9085                ];
9086
9087             }
9088
9089         } else {
9090             
9091             cfg.cn = [
9092
9093                     inputblock
9094
9095             ];
9096                 
9097                 
9098         };
9099         
9100         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9101            cfg.cls += ' navbar-form';
9102         }
9103         
9104         if (this.parentType === 'NavGroup') {
9105            cfg.cls += ' navbar-form';
9106            cfg.tag = 'li';
9107         }
9108         
9109         return cfg;
9110         
9111     },
9112     /**
9113      * return the real input element.
9114      */
9115     inputEl: function ()
9116     {
9117         return this.el.select('input.form-control',true).first();
9118     },
9119     
9120     tooltipEl : function()
9121     {
9122         return this.inputEl();
9123     },
9124     
9125     indicatorEl : function()
9126     {
9127         var indicator = this.el.select('i.roo-required-indicator',true).first();
9128         
9129         if(!indicator){
9130             return false;
9131         }
9132         
9133         return indicator;
9134         
9135     },
9136     
9137     setDisabled : function(v)
9138     {
9139         var i  = this.inputEl().dom;
9140         if (!v) {
9141             i.removeAttribute('disabled');
9142             return;
9143             
9144         }
9145         i.setAttribute('disabled','true');
9146     },
9147     initEvents : function()
9148     {
9149           
9150         this.inputEl().on("keydown" , this.fireKey,  this);
9151         this.inputEl().on("focus", this.onFocus,  this);
9152         this.inputEl().on("blur", this.onBlur,  this);
9153         
9154         this.inputEl().relayEvent('keyup', this);
9155         
9156         this.indicator = this.indicatorEl();
9157         
9158         if(this.indicator){
9159             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9160         }
9161  
9162         // reference to original value for reset
9163         this.originalValue = this.getValue();
9164         //Roo.form.TextField.superclass.initEvents.call(this);
9165         if(this.validationEvent == 'keyup'){
9166             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9167             this.inputEl().on('keyup', this.filterValidation, this);
9168         }
9169         else if(this.validationEvent !== false){
9170             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9171         }
9172         
9173         if(this.selectOnFocus){
9174             this.on("focus", this.preFocus, this);
9175             
9176         }
9177         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9178             this.inputEl().on("keypress", this.filterKeys, this);
9179         } else {
9180             this.inputEl().relayEvent('keypress', this);
9181         }
9182        /* if(this.grow){
9183             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9184             this.el.on("click", this.autoSize,  this);
9185         }
9186         */
9187         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9188             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9189         }
9190         
9191         if (typeof(this.before) == 'object') {
9192             this.before.render(this.el.select('.roo-input-before',true).first());
9193         }
9194         if (typeof(this.after) == 'object') {
9195             this.after.render(this.el.select('.roo-input-after',true).first());
9196         }
9197         
9198         this.inputEl().on('change', this.onChange, this);
9199         
9200     },
9201     filterValidation : function(e){
9202         if(!e.isNavKeyPress()){
9203             this.validationTask.delay(this.validationDelay);
9204         }
9205     },
9206      /**
9207      * Validates the field value
9208      * @return {Boolean} True if the value is valid, else false
9209      */
9210     validate : function(){
9211         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9212         if(this.disabled || this.validateValue(this.getRawValue())){
9213             this.markValid();
9214             return true;
9215         }
9216         
9217         this.markInvalid();
9218         return false;
9219     },
9220     
9221     
9222     /**
9223      * Validates a value according to the field's validation rules and marks the field as invalid
9224      * if the validation fails
9225      * @param {Mixed} value The value to validate
9226      * @return {Boolean} True if the value is valid, else false
9227      */
9228     validateValue : function(value)
9229     {
9230         if(this.getVisibilityEl().hasClass('hidden')){
9231             return true;
9232         }
9233         
9234         if(value.length < 1)  { // if it's blank
9235             if(this.allowBlank){
9236                 return true;
9237             }
9238             return false;
9239         }
9240         
9241         if(value.length < this.minLength){
9242             return false;
9243         }
9244         if(value.length > this.maxLength){
9245             return false;
9246         }
9247         if(this.vtype){
9248             var vt = Roo.form.VTypes;
9249             if(!vt[this.vtype](value, this)){
9250                 return false;
9251             }
9252         }
9253         if(typeof this.validator == "function"){
9254             var msg = this.validator(value);
9255             if(msg !== true){
9256                 return false;
9257             }
9258             if (typeof(msg) == 'string') {
9259                 this.invalidText = msg;
9260             }
9261         }
9262         
9263         if(this.regex && !this.regex.test(value)){
9264             return false;
9265         }
9266         
9267         return true;
9268     },
9269     
9270      // private
9271     fireKey : function(e){
9272         //Roo.log('field ' + e.getKey());
9273         if(e.isNavKeyPress()){
9274             this.fireEvent("specialkey", this, e);
9275         }
9276     },
9277     focus : function (selectText){
9278         if(this.rendered){
9279             this.inputEl().focus();
9280             if(selectText === true){
9281                 this.inputEl().dom.select();
9282             }
9283         }
9284         return this;
9285     } ,
9286     
9287     onFocus : function(){
9288         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9289            // this.el.addClass(this.focusClass);
9290         }
9291         if(!this.hasFocus){
9292             this.hasFocus = true;
9293             this.startValue = this.getValue();
9294             this.fireEvent("focus", this);
9295         }
9296     },
9297     
9298     beforeBlur : Roo.emptyFn,
9299
9300     
9301     // private
9302     onBlur : function(){
9303         this.beforeBlur();
9304         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9305             //this.el.removeClass(this.focusClass);
9306         }
9307         this.hasFocus = false;
9308         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9309             this.validate();
9310         }
9311         var v = this.getValue();
9312         if(String(v) !== String(this.startValue)){
9313             this.fireEvent('change', this, v, this.startValue);
9314         }
9315         this.fireEvent("blur", this);
9316     },
9317     
9318     onChange : function(e)
9319     {
9320         var v = this.getValue();
9321         if(String(v) !== String(this.startValue)){
9322             this.fireEvent('change', this, v, this.startValue);
9323         }
9324         
9325     },
9326     
9327     /**
9328      * Resets the current field value to the originally loaded value and clears any validation messages
9329      */
9330     reset : function(){
9331         this.setValue(this.originalValue);
9332         this.validate();
9333     },
9334      /**
9335      * Returns the name of the field
9336      * @return {Mixed} name The name field
9337      */
9338     getName: function(){
9339         return this.name;
9340     },
9341      /**
9342      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9343      * @return {Mixed} value The field value
9344      */
9345     getValue : function(){
9346         
9347         var v = this.inputEl().getValue();
9348         
9349         return v;
9350     },
9351     /**
9352      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9353      * @return {Mixed} value The field value
9354      */
9355     getRawValue : function(){
9356         var v = this.inputEl().getValue();
9357         
9358         return v;
9359     },
9360     
9361     /**
9362      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9363      * @param {Mixed} value The value to set
9364      */
9365     setRawValue : function(v){
9366         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9367     },
9368     
9369     selectText : function(start, end){
9370         var v = this.getRawValue();
9371         if(v.length > 0){
9372             start = start === undefined ? 0 : start;
9373             end = end === undefined ? v.length : end;
9374             var d = this.inputEl().dom;
9375             if(d.setSelectionRange){
9376                 d.setSelectionRange(start, end);
9377             }else if(d.createTextRange){
9378                 var range = d.createTextRange();
9379                 range.moveStart("character", start);
9380                 range.moveEnd("character", v.length-end);
9381                 range.select();
9382             }
9383         }
9384     },
9385     
9386     /**
9387      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9388      * @param {Mixed} value The value to set
9389      */
9390     setValue : function(v){
9391         this.value = v;
9392         if(this.rendered){
9393             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9394             this.validate();
9395         }
9396     },
9397     
9398     /*
9399     processValue : function(value){
9400         if(this.stripCharsRe){
9401             var newValue = value.replace(this.stripCharsRe, '');
9402             if(newValue !== value){
9403                 this.setRawValue(newValue);
9404                 return newValue;
9405             }
9406         }
9407         return value;
9408     },
9409   */
9410     preFocus : function(){
9411         
9412         if(this.selectOnFocus){
9413             this.inputEl().dom.select();
9414         }
9415     },
9416     filterKeys : function(e){
9417         var k = e.getKey();
9418         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9419             return;
9420         }
9421         var c = e.getCharCode(), cc = String.fromCharCode(c);
9422         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9423             return;
9424         }
9425         if(!this.maskRe.test(cc)){
9426             e.stopEvent();
9427         }
9428     },
9429      /**
9430      * Clear any invalid styles/messages for this field
9431      */
9432     clearInvalid : function(){
9433         
9434         if(!this.el || this.preventMark){ // not rendered
9435             return;
9436         }
9437         
9438      
9439         this.el.removeClass(this.invalidClass);
9440         
9441         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9442             
9443             var feedback = this.el.select('.form-control-feedback', true).first();
9444             
9445             if(feedback){
9446                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9447             }
9448             
9449         }
9450         
9451         if(this.indicator){
9452             this.indicator.removeClass('visible');
9453             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9454         }
9455         
9456         this.fireEvent('valid', this);
9457     },
9458     
9459      /**
9460      * Mark this field as valid
9461      */
9462     markValid : function()
9463     {
9464         if(!this.el  || this.preventMark){ // not rendered...
9465             return;
9466         }
9467         
9468         this.el.removeClass([this.invalidClass, this.validClass]);
9469         
9470         var feedback = this.el.select('.form-control-feedback', true).first();
9471             
9472         if(feedback){
9473             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9474         }
9475         
9476         if(this.indicator){
9477             this.indicator.removeClass('visible');
9478             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9479         }
9480         
9481         if(this.disabled){
9482             return;
9483         }
9484         
9485         if(this.allowBlank && !this.getRawValue().length){
9486             return;
9487         }
9488         
9489         this.el.addClass(this.validClass);
9490         
9491         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9492             
9493             var feedback = this.el.select('.form-control-feedback', true).first();
9494             
9495             if(feedback){
9496                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9497                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9498             }
9499             
9500         }
9501         
9502         this.fireEvent('valid', this);
9503     },
9504     
9505      /**
9506      * Mark this field as invalid
9507      * @param {String} msg The validation message
9508      */
9509     markInvalid : function(msg)
9510     {
9511         if(!this.el  || this.preventMark){ // not rendered
9512             return;
9513         }
9514         
9515         this.el.removeClass([this.invalidClass, this.validClass]);
9516         
9517         var feedback = this.el.select('.form-control-feedback', true).first();
9518             
9519         if(feedback){
9520             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9521         }
9522
9523         if(this.disabled){
9524             return;
9525         }
9526         
9527         if(this.allowBlank && !this.getRawValue().length){
9528             return;
9529         }
9530         
9531         if(this.indicator){
9532             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9533             this.indicator.addClass('visible');
9534         }
9535         
9536         this.el.addClass(this.invalidClass);
9537         
9538         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9539             
9540             var feedback = this.el.select('.form-control-feedback', true).first();
9541             
9542             if(feedback){
9543                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9544                 
9545                 if(this.getValue().length || this.forceFeedback){
9546                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9547                 }
9548                 
9549             }
9550             
9551         }
9552         
9553         this.fireEvent('invalid', this, msg);
9554     },
9555     // private
9556     SafariOnKeyDown : function(event)
9557     {
9558         // this is a workaround for a password hang bug on chrome/ webkit.
9559         if (this.inputEl().dom.type != 'password') {
9560             return;
9561         }
9562         
9563         var isSelectAll = false;
9564         
9565         if(this.inputEl().dom.selectionEnd > 0){
9566             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9567         }
9568         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9569             event.preventDefault();
9570             this.setValue('');
9571             return;
9572         }
9573         
9574         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9575             
9576             event.preventDefault();
9577             // this is very hacky as keydown always get's upper case.
9578             //
9579             var cc = String.fromCharCode(event.getCharCode());
9580             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9581             
9582         }
9583     },
9584     adjustWidth : function(tag, w){
9585         tag = tag.toLowerCase();
9586         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9587             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9588                 if(tag == 'input'){
9589                     return w + 2;
9590                 }
9591                 if(tag == 'textarea'){
9592                     return w-2;
9593                 }
9594             }else if(Roo.isOpera){
9595                 if(tag == 'input'){
9596                     return w + 2;
9597                 }
9598                 if(tag == 'textarea'){
9599                     return w-2;
9600                 }
9601             }
9602         }
9603         return w;
9604     },
9605     
9606     setFieldLabel : function(v)
9607     {
9608         if(!this.rendered){
9609             return;
9610         }
9611         
9612         if(this.indicator){
9613             var ar = this.el.select('label > span',true);
9614             
9615             if (ar.elements.length) {
9616                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9617                 this.fieldLabel = v;
9618                 return;
9619             }
9620             
9621             var br = this.el.select('label',true);
9622             
9623             if(br.elements.length) {
9624                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9625                 this.fieldLabel = v;
9626                 return;
9627             }
9628             
9629             Roo.log('Cannot Found any of label > span || label in input');
9630             return;
9631         }
9632         
9633         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9634         this.fieldLabel = v;
9635         
9636         
9637     }
9638 });
9639
9640  
9641 /*
9642  * - LGPL
9643  *
9644  * Input
9645  * 
9646  */
9647
9648 /**
9649  * @class Roo.bootstrap.TextArea
9650  * @extends Roo.bootstrap.Input
9651  * Bootstrap TextArea class
9652  * @cfg {Number} cols Specifies the visible width of a text area
9653  * @cfg {Number} rows Specifies the visible number of lines in a text area
9654  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9655  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9656  * @cfg {string} html text
9657  * 
9658  * @constructor
9659  * Create a new TextArea
9660  * @param {Object} config The config object
9661  */
9662
9663 Roo.bootstrap.TextArea = function(config){
9664     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9665    
9666 };
9667
9668 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9669      
9670     cols : false,
9671     rows : 5,
9672     readOnly : false,
9673     warp : 'soft',
9674     resize : false,
9675     value: false,
9676     html: false,
9677     
9678     getAutoCreate : function(){
9679         
9680         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9681         
9682         var id = Roo.id();
9683         
9684         var cfg = {};
9685         
9686         if(this.inputType != 'hidden'){
9687             cfg.cls = 'form-group' //input-group
9688         }
9689         
9690         var input =  {
9691             tag: 'textarea',
9692             id : id,
9693             warp : this.warp,
9694             rows : this.rows,
9695             value : this.value || '',
9696             html: this.html || '',
9697             cls : 'form-control',
9698             placeholder : this.placeholder || '' 
9699             
9700         };
9701         
9702         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9703             input.maxLength = this.maxLength;
9704         }
9705         
9706         if(this.resize){
9707             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9708         }
9709         
9710         if(this.cols){
9711             input.cols = this.cols;
9712         }
9713         
9714         if (this.readOnly) {
9715             input.readonly = true;
9716         }
9717         
9718         if (this.name) {
9719             input.name = this.name;
9720         }
9721         
9722         if (this.size) {
9723             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9724         }
9725         
9726         var settings=this;
9727         ['xs','sm','md','lg'].map(function(size){
9728             if (settings[size]) {
9729                 cfg.cls += ' col-' + size + '-' + settings[size];
9730             }
9731         });
9732         
9733         var inputblock = input;
9734         
9735         if(this.hasFeedback && !this.allowBlank){
9736             
9737             var feedback = {
9738                 tag: 'span',
9739                 cls: 'glyphicon form-control-feedback'
9740             };
9741
9742             inputblock = {
9743                 cls : 'has-feedback',
9744                 cn :  [
9745                     input,
9746                     feedback
9747                 ] 
9748             };  
9749         }
9750         
9751         
9752         if (this.before || this.after) {
9753             
9754             inputblock = {
9755                 cls : 'input-group',
9756                 cn :  [] 
9757             };
9758             if (this.before) {
9759                 inputblock.cn.push({
9760                     tag :'span',
9761                     cls : 'input-group-addon',
9762                     html : this.before
9763                 });
9764             }
9765             
9766             inputblock.cn.push(input);
9767             
9768             if(this.hasFeedback && !this.allowBlank){
9769                 inputblock.cls += ' has-feedback';
9770                 inputblock.cn.push(feedback);
9771             }
9772             
9773             if (this.after) {
9774                 inputblock.cn.push({
9775                     tag :'span',
9776                     cls : 'input-group-addon',
9777                     html : this.after
9778                 });
9779             }
9780             
9781         }
9782         
9783         if (align ==='left' && this.fieldLabel.length) {
9784             cfg.cn = [
9785                 {
9786                     tag: 'label',
9787                     'for' :  id,
9788                     cls : 'control-label',
9789                     html : this.fieldLabel
9790                 },
9791                 {
9792                     cls : "",
9793                     cn: [
9794                         inputblock
9795                     ]
9796                 }
9797
9798             ];
9799             
9800             if(this.labelWidth > 12){
9801                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9802             }
9803
9804             if(this.labelWidth < 13 && this.labelmd == 0){
9805                 this.labelmd = this.labelWidth;
9806             }
9807
9808             if(this.labellg > 0){
9809                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9810                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9811             }
9812
9813             if(this.labelmd > 0){
9814                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9815                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9816             }
9817
9818             if(this.labelsm > 0){
9819                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9820                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9821             }
9822
9823             if(this.labelxs > 0){
9824                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9825                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9826             }
9827             
9828         } else if ( this.fieldLabel.length) {
9829             cfg.cn = [
9830
9831                {
9832                    tag: 'label',
9833                    //cls : 'input-group-addon',
9834                    html : this.fieldLabel
9835
9836                },
9837
9838                inputblock
9839
9840            ];
9841
9842         } else {
9843
9844             cfg.cn = [
9845
9846                 inputblock
9847
9848             ];
9849                 
9850         }
9851         
9852         if (this.disabled) {
9853             input.disabled=true;
9854         }
9855         
9856         return cfg;
9857         
9858     },
9859     /**
9860      * return the real textarea element.
9861      */
9862     inputEl: function ()
9863     {
9864         return this.el.select('textarea.form-control',true).first();
9865     },
9866     
9867     /**
9868      * Clear any invalid styles/messages for this field
9869      */
9870     clearInvalid : function()
9871     {
9872         
9873         if(!this.el || this.preventMark){ // not rendered
9874             return;
9875         }
9876         
9877         var label = this.el.select('label', true).first();
9878         var icon = this.el.select('i.fa-star', true).first();
9879         
9880         if(label && icon){
9881             icon.remove();
9882         }
9883         
9884         this.el.removeClass(this.invalidClass);
9885         
9886         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9887             
9888             var feedback = this.el.select('.form-control-feedback', true).first();
9889             
9890             if(feedback){
9891                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9892             }
9893             
9894         }
9895         
9896         this.fireEvent('valid', this);
9897     },
9898     
9899      /**
9900      * Mark this field as valid
9901      */
9902     markValid : function()
9903     {
9904         if(!this.el  || this.preventMark){ // not rendered
9905             return;
9906         }
9907         
9908         this.el.removeClass([this.invalidClass, this.validClass]);
9909         
9910         var feedback = this.el.select('.form-control-feedback', true).first();
9911             
9912         if(feedback){
9913             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9914         }
9915
9916         if(this.disabled || this.allowBlank){
9917             return;
9918         }
9919         
9920         var label = this.el.select('label', true).first();
9921         var icon = this.el.select('i.fa-star', true).first();
9922         
9923         if(label && icon){
9924             icon.remove();
9925         }
9926         
9927         this.el.addClass(this.validClass);
9928         
9929         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9930             
9931             var feedback = this.el.select('.form-control-feedback', true).first();
9932             
9933             if(feedback){
9934                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9935                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9936             }
9937             
9938         }
9939         
9940         this.fireEvent('valid', this);
9941     },
9942     
9943      /**
9944      * Mark this field as invalid
9945      * @param {String} msg The validation message
9946      */
9947     markInvalid : function(msg)
9948     {
9949         if(!this.el  || this.preventMark){ // not rendered
9950             return;
9951         }
9952         
9953         this.el.removeClass([this.invalidClass, this.validClass]);
9954         
9955         var feedback = this.el.select('.form-control-feedback', true).first();
9956             
9957         if(feedback){
9958             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9959         }
9960
9961         if(this.disabled || this.allowBlank){
9962             return;
9963         }
9964         
9965         var label = this.el.select('label', true).first();
9966         var icon = this.el.select('i.fa-star', true).first();
9967         
9968         if(!this.getValue().length && label && !icon){
9969             this.el.createChild({
9970                 tag : 'i',
9971                 cls : 'text-danger fa fa-lg fa-star',
9972                 tooltip : 'This field is required',
9973                 style : 'margin-right:5px;'
9974             }, label, true);
9975         }
9976
9977         this.el.addClass(this.invalidClass);
9978         
9979         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9980             
9981             var feedback = this.el.select('.form-control-feedback', true).first();
9982             
9983             if(feedback){
9984                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9985                 
9986                 if(this.getValue().length || this.forceFeedback){
9987                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9988                 }
9989                 
9990             }
9991             
9992         }
9993         
9994         this.fireEvent('invalid', this, msg);
9995     }
9996 });
9997
9998  
9999 /*
10000  * - LGPL
10001  *
10002  * trigger field - base class for combo..
10003  * 
10004  */
10005  
10006 /**
10007  * @class Roo.bootstrap.TriggerField
10008  * @extends Roo.bootstrap.Input
10009  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10010  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10011  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10012  * for which you can provide a custom implementation.  For example:
10013  * <pre><code>
10014 var trigger = new Roo.bootstrap.TriggerField();
10015 trigger.onTriggerClick = myTriggerFn;
10016 trigger.applyTo('my-field');
10017 </code></pre>
10018  *
10019  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10020  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10021  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10022  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10023  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10024
10025  * @constructor
10026  * Create a new TriggerField.
10027  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10028  * to the base TextField)
10029  */
10030 Roo.bootstrap.TriggerField = function(config){
10031     this.mimicing = false;
10032     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10033 };
10034
10035 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10036     /**
10037      * @cfg {String} triggerClass A CSS class to apply to the trigger
10038      */
10039      /**
10040      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10041      */
10042     hideTrigger:false,
10043
10044     /**
10045      * @cfg {Boolean} removable (true|false) special filter default false
10046      */
10047     removable : false,
10048     
10049     /** @cfg {Boolean} grow @hide */
10050     /** @cfg {Number} growMin @hide */
10051     /** @cfg {Number} growMax @hide */
10052
10053     /**
10054      * @hide 
10055      * @method
10056      */
10057     autoSize: Roo.emptyFn,
10058     // private
10059     monitorTab : true,
10060     // private
10061     deferHeight : true,
10062
10063     
10064     actionMode : 'wrap',
10065     
10066     caret : false,
10067     
10068     
10069     getAutoCreate : function(){
10070        
10071         var align = this.labelAlign || this.parentLabelAlign();
10072         
10073         var id = Roo.id();
10074         
10075         var cfg = {
10076             cls: 'form-group' //input-group
10077         };
10078         
10079         
10080         var input =  {
10081             tag: 'input',
10082             id : id,
10083             type : this.inputType,
10084             cls : 'form-control',
10085             autocomplete: 'new-password',
10086             placeholder : this.placeholder || '' 
10087             
10088         };
10089         if (this.name) {
10090             input.name = this.name;
10091         }
10092         if (this.size) {
10093             input.cls += ' input-' + this.size;
10094         }
10095         
10096         if (this.disabled) {
10097             input.disabled=true;
10098         }
10099         
10100         var inputblock = input;
10101         
10102         if(this.hasFeedback && !this.allowBlank){
10103             
10104             var feedback = {
10105                 tag: 'span',
10106                 cls: 'glyphicon form-control-feedback'
10107             };
10108             
10109             if(this.removable && !this.editable && !this.tickable){
10110                 inputblock = {
10111                     cls : 'has-feedback',
10112                     cn :  [
10113                         inputblock,
10114                         {
10115                             tag: 'button',
10116                             html : 'x',
10117                             cls : 'roo-combo-removable-btn close'
10118                         },
10119                         feedback
10120                     ] 
10121                 };
10122             } else {
10123                 inputblock = {
10124                     cls : 'has-feedback',
10125                     cn :  [
10126                         inputblock,
10127                         feedback
10128                     ] 
10129                 };
10130             }
10131
10132         } else {
10133             if(this.removable && !this.editable && !this.tickable){
10134                 inputblock = {
10135                     cls : 'roo-removable',
10136                     cn :  [
10137                         inputblock,
10138                         {
10139                             tag: 'button',
10140                             html : 'x',
10141                             cls : 'roo-combo-removable-btn close'
10142                         }
10143                     ] 
10144                 };
10145             }
10146         }
10147         
10148         if (this.before || this.after) {
10149             
10150             inputblock = {
10151                 cls : 'input-group',
10152                 cn :  [] 
10153             };
10154             if (this.before) {
10155                 inputblock.cn.push({
10156                     tag :'span',
10157                     cls : 'input-group-addon',
10158                     html : this.before
10159                 });
10160             }
10161             
10162             inputblock.cn.push(input);
10163             
10164             if(this.hasFeedback && !this.allowBlank){
10165                 inputblock.cls += ' has-feedback';
10166                 inputblock.cn.push(feedback);
10167             }
10168             
10169             if (this.after) {
10170                 inputblock.cn.push({
10171                     tag :'span',
10172                     cls : 'input-group-addon',
10173                     html : this.after
10174                 });
10175             }
10176             
10177         };
10178         
10179         var box = {
10180             tag: 'div',
10181             cn: [
10182                 {
10183                     tag: 'input',
10184                     type : 'hidden',
10185                     cls: 'form-hidden-field'
10186                 },
10187                 inputblock
10188             ]
10189             
10190         };
10191         
10192         if(this.multiple){
10193             box = {
10194                 tag: 'div',
10195                 cn: [
10196                     {
10197                         tag: 'input',
10198                         type : 'hidden',
10199                         cls: 'form-hidden-field'
10200                     },
10201                     {
10202                         tag: 'ul',
10203                         cls: 'roo-select2-choices',
10204                         cn:[
10205                             {
10206                                 tag: 'li',
10207                                 cls: 'roo-select2-search-field',
10208                                 cn: [
10209
10210                                     inputblock
10211                                 ]
10212                             }
10213                         ]
10214                     }
10215                 ]
10216             }
10217         };
10218         
10219         var combobox = {
10220             cls: 'roo-select2-container input-group',
10221             cn: [
10222                 box
10223 //                {
10224 //                    tag: 'ul',
10225 //                    cls: 'typeahead typeahead-long dropdown-menu',
10226 //                    style: 'display:none'
10227 //                }
10228             ]
10229         };
10230         
10231         if(!this.multiple && this.showToggleBtn){
10232             
10233             var caret = {
10234                         tag: 'span',
10235                         cls: 'caret'
10236              };
10237             if (this.caret != false) {
10238                 caret = {
10239                      tag: 'i',
10240                      cls: 'fa fa-' + this.caret
10241                 };
10242                 
10243             }
10244             
10245             combobox.cn.push({
10246                 tag :'span',
10247                 cls : 'input-group-addon btn dropdown-toggle',
10248                 cn : [
10249                     caret,
10250                     {
10251                         tag: 'span',
10252                         cls: 'combobox-clear',
10253                         cn  : [
10254                             {
10255                                 tag : 'i',
10256                                 cls: 'icon-remove'
10257                             }
10258                         ]
10259                     }
10260                 ]
10261
10262             })
10263         }
10264         
10265         if(this.multiple){
10266             combobox.cls += ' roo-select2-container-multi';
10267         }
10268         
10269         if (align ==='left' && this.fieldLabel.length) {
10270             
10271             cfg.cls += ' roo-form-group-label-left';
10272
10273             cfg.cn = [
10274                 {
10275                     tag : 'i',
10276                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10277                     tooltip : 'This field is required'
10278                 },
10279                 {
10280                     tag: 'label',
10281                     'for' :  id,
10282                     cls : 'control-label',
10283                     html : this.fieldLabel
10284
10285                 },
10286                 {
10287                     cls : "", 
10288                     cn: [
10289                         combobox
10290                     ]
10291                 }
10292
10293             ];
10294             
10295             var labelCfg = cfg.cn[1];
10296             var contentCfg = cfg.cn[2];
10297             
10298             if(this.indicatorpos == 'right'){
10299                 cfg.cn = [
10300                     {
10301                         tag: 'label',
10302                         'for' :  id,
10303                         cls : 'control-label',
10304                         cn : [
10305                             {
10306                                 tag : 'span',
10307                                 html : this.fieldLabel
10308                             },
10309                             {
10310                                 tag : 'i',
10311                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10312                                 tooltip : 'This field is required'
10313                             }
10314                         ]
10315                     },
10316                     {
10317                         cls : "", 
10318                         cn: [
10319                             combobox
10320                         ]
10321                     }
10322
10323                 ];
10324                 
10325                 labelCfg = cfg.cn[0];
10326                 contentCfg = cfg.cn[1];
10327             }
10328             
10329             if(this.labelWidth > 12){
10330                 labelCfg.style = "width: " + this.labelWidth + 'px';
10331             }
10332             
10333             if(this.labelWidth < 13 && this.labelmd == 0){
10334                 this.labelmd = this.labelWidth;
10335             }
10336             
10337             if(this.labellg > 0){
10338                 labelCfg.cls += ' col-lg-' + this.labellg;
10339                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10340             }
10341             
10342             if(this.labelmd > 0){
10343                 labelCfg.cls += ' col-md-' + this.labelmd;
10344                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10345             }
10346             
10347             if(this.labelsm > 0){
10348                 labelCfg.cls += ' col-sm-' + this.labelsm;
10349                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10350             }
10351             
10352             if(this.labelxs > 0){
10353                 labelCfg.cls += ' col-xs-' + this.labelxs;
10354                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10355             }
10356             
10357         } else if ( this.fieldLabel.length) {
10358 //                Roo.log(" label");
10359             cfg.cn = [
10360                 {
10361                    tag : 'i',
10362                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10363                    tooltip : 'This field is required'
10364                },
10365                {
10366                    tag: 'label',
10367                    //cls : 'input-group-addon',
10368                    html : this.fieldLabel
10369
10370                },
10371
10372                combobox
10373
10374             ];
10375             
10376             if(this.indicatorpos == 'right'){
10377                 
10378                 cfg.cn = [
10379                     {
10380                        tag: 'label',
10381                        cn : [
10382                            {
10383                                tag : 'span',
10384                                html : this.fieldLabel
10385                            },
10386                            {
10387                               tag : 'i',
10388                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10389                               tooltip : 'This field is required'
10390                            }
10391                        ]
10392
10393                     },
10394                     combobox
10395
10396                 ];
10397
10398             }
10399
10400         } else {
10401             
10402 //                Roo.log(" no label && no align");
10403                 cfg = combobox
10404                      
10405                 
10406         }
10407         
10408         var settings=this;
10409         ['xs','sm','md','lg'].map(function(size){
10410             if (settings[size]) {
10411                 cfg.cls += ' col-' + size + '-' + settings[size];
10412             }
10413         });
10414         
10415         return cfg;
10416         
10417     },
10418     
10419     
10420     
10421     // private
10422     onResize : function(w, h){
10423 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10424 //        if(typeof w == 'number'){
10425 //            var x = w - this.trigger.getWidth();
10426 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10427 //            this.trigger.setStyle('left', x+'px');
10428 //        }
10429     },
10430
10431     // private
10432     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10433
10434     // private
10435     getResizeEl : function(){
10436         return this.inputEl();
10437     },
10438
10439     // private
10440     getPositionEl : function(){
10441         return this.inputEl();
10442     },
10443
10444     // private
10445     alignErrorIcon : function(){
10446         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10447     },
10448
10449     // private
10450     initEvents : function(){
10451         
10452         this.createList();
10453         
10454         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10455         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10456         if(!this.multiple && this.showToggleBtn){
10457             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10458             if(this.hideTrigger){
10459                 this.trigger.setDisplayed(false);
10460             }
10461             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10462         }
10463         
10464         if(this.multiple){
10465             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10466         }
10467         
10468         if(this.removable && !this.editable && !this.tickable){
10469             var close = this.closeTriggerEl();
10470             
10471             if(close){
10472                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10473                 close.on('click', this.removeBtnClick, this, close);
10474             }
10475         }
10476         
10477         //this.trigger.addClassOnOver('x-form-trigger-over');
10478         //this.trigger.addClassOnClick('x-form-trigger-click');
10479         
10480         //if(!this.width){
10481         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10482         //}
10483     },
10484     
10485     closeTriggerEl : function()
10486     {
10487         var close = this.el.select('.roo-combo-removable-btn', true).first();
10488         return close ? close : false;
10489     },
10490     
10491     removeBtnClick : function(e, h, el)
10492     {
10493         e.preventDefault();
10494         
10495         if(this.fireEvent("remove", this) !== false){
10496             this.reset();
10497             this.fireEvent("afterremove", this)
10498         }
10499     },
10500     
10501     createList : function()
10502     {
10503         this.list = Roo.get(document.body).createChild({
10504             tag: 'ul',
10505             cls: 'typeahead typeahead-long dropdown-menu',
10506             style: 'display:none'
10507         });
10508         
10509         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10510         
10511     },
10512
10513     // private
10514     initTrigger : function(){
10515        
10516     },
10517
10518     // private
10519     onDestroy : function(){
10520         if(this.trigger){
10521             this.trigger.removeAllListeners();
10522           //  this.trigger.remove();
10523         }
10524         //if(this.wrap){
10525         //    this.wrap.remove();
10526         //}
10527         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10528     },
10529
10530     // private
10531     onFocus : function(){
10532         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10533         /*
10534         if(!this.mimicing){
10535             this.wrap.addClass('x-trigger-wrap-focus');
10536             this.mimicing = true;
10537             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10538             if(this.monitorTab){
10539                 this.el.on("keydown", this.checkTab, this);
10540             }
10541         }
10542         */
10543     },
10544
10545     // private
10546     checkTab : function(e){
10547         if(e.getKey() == e.TAB){
10548             this.triggerBlur();
10549         }
10550     },
10551
10552     // private
10553     onBlur : function(){
10554         // do nothing
10555     },
10556
10557     // private
10558     mimicBlur : function(e, t){
10559         /*
10560         if(!this.wrap.contains(t) && this.validateBlur()){
10561             this.triggerBlur();
10562         }
10563         */
10564     },
10565
10566     // private
10567     triggerBlur : function(){
10568         this.mimicing = false;
10569         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10570         if(this.monitorTab){
10571             this.el.un("keydown", this.checkTab, this);
10572         }
10573         //this.wrap.removeClass('x-trigger-wrap-focus');
10574         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10575     },
10576
10577     // private
10578     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10579     validateBlur : function(e, t){
10580         return true;
10581     },
10582
10583     // private
10584     onDisable : function(){
10585         this.inputEl().dom.disabled = true;
10586         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10587         //if(this.wrap){
10588         //    this.wrap.addClass('x-item-disabled');
10589         //}
10590     },
10591
10592     // private
10593     onEnable : function(){
10594         this.inputEl().dom.disabled = false;
10595         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10596         //if(this.wrap){
10597         //    this.el.removeClass('x-item-disabled');
10598         //}
10599     },
10600
10601     // private
10602     onShow : function(){
10603         var ae = this.getActionEl();
10604         
10605         if(ae){
10606             ae.dom.style.display = '';
10607             ae.dom.style.visibility = 'visible';
10608         }
10609     },
10610
10611     // private
10612     
10613     onHide : function(){
10614         var ae = this.getActionEl();
10615         ae.dom.style.display = 'none';
10616     },
10617
10618     /**
10619      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10620      * by an implementing function.
10621      * @method
10622      * @param {EventObject} e
10623      */
10624     onTriggerClick : Roo.emptyFn
10625 });
10626  /*
10627  * Based on:
10628  * Ext JS Library 1.1.1
10629  * Copyright(c) 2006-2007, Ext JS, LLC.
10630  *
10631  * Originally Released Under LGPL - original licence link has changed is not relivant.
10632  *
10633  * Fork - LGPL
10634  * <script type="text/javascript">
10635  */
10636
10637
10638 /**
10639  * @class Roo.data.SortTypes
10640  * @singleton
10641  * Defines the default sorting (casting?) comparison functions used when sorting data.
10642  */
10643 Roo.data.SortTypes = {
10644     /**
10645      * Default sort that does nothing
10646      * @param {Mixed} s The value being converted
10647      * @return {Mixed} The comparison value
10648      */
10649     none : function(s){
10650         return s;
10651     },
10652     
10653     /**
10654      * The regular expression used to strip tags
10655      * @type {RegExp}
10656      * @property
10657      */
10658     stripTagsRE : /<\/?[^>]+>/gi,
10659     
10660     /**
10661      * Strips all HTML tags to sort on text only
10662      * @param {Mixed} s The value being converted
10663      * @return {String} The comparison value
10664      */
10665     asText : function(s){
10666         return String(s).replace(this.stripTagsRE, "");
10667     },
10668     
10669     /**
10670      * Strips all HTML tags to sort on text only - Case insensitive
10671      * @param {Mixed} s The value being converted
10672      * @return {String} The comparison value
10673      */
10674     asUCText : function(s){
10675         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10676     },
10677     
10678     /**
10679      * Case insensitive string
10680      * @param {Mixed} s The value being converted
10681      * @return {String} The comparison value
10682      */
10683     asUCString : function(s) {
10684         return String(s).toUpperCase();
10685     },
10686     
10687     /**
10688      * Date sorting
10689      * @param {Mixed} s The value being converted
10690      * @return {Number} The comparison value
10691      */
10692     asDate : function(s) {
10693         if(!s){
10694             return 0;
10695         }
10696         if(s instanceof Date){
10697             return s.getTime();
10698         }
10699         return Date.parse(String(s));
10700     },
10701     
10702     /**
10703      * Float sorting
10704      * @param {Mixed} s The value being converted
10705      * @return {Float} The comparison value
10706      */
10707     asFloat : function(s) {
10708         var val = parseFloat(String(s).replace(/,/g, ""));
10709         if(isNaN(val)) {
10710             val = 0;
10711         }
10712         return val;
10713     },
10714     
10715     /**
10716      * Integer sorting
10717      * @param {Mixed} s The value being converted
10718      * @return {Number} The comparison value
10719      */
10720     asInt : function(s) {
10721         var val = parseInt(String(s).replace(/,/g, ""));
10722         if(isNaN(val)) {
10723             val = 0;
10724         }
10725         return val;
10726     }
10727 };/*
10728  * Based on:
10729  * Ext JS Library 1.1.1
10730  * Copyright(c) 2006-2007, Ext JS, LLC.
10731  *
10732  * Originally Released Under LGPL - original licence link has changed is not relivant.
10733  *
10734  * Fork - LGPL
10735  * <script type="text/javascript">
10736  */
10737
10738 /**
10739 * @class Roo.data.Record
10740  * Instances of this class encapsulate both record <em>definition</em> information, and record
10741  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10742  * to access Records cached in an {@link Roo.data.Store} object.<br>
10743  * <p>
10744  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10745  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10746  * objects.<br>
10747  * <p>
10748  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10749  * @constructor
10750  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10751  * {@link #create}. The parameters are the same.
10752  * @param {Array} data An associative Array of data values keyed by the field name.
10753  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10754  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10755  * not specified an integer id is generated.
10756  */
10757 Roo.data.Record = function(data, id){
10758     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10759     this.data = data;
10760 };
10761
10762 /**
10763  * Generate a constructor for a specific record layout.
10764  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10765  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10766  * Each field definition object may contain the following properties: <ul>
10767  * <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,
10768  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10769  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10770  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10771  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10772  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10773  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10774  * this may be omitted.</p></li>
10775  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10776  * <ul><li>auto (Default, implies no conversion)</li>
10777  * <li>string</li>
10778  * <li>int</li>
10779  * <li>float</li>
10780  * <li>boolean</li>
10781  * <li>date</li></ul></p></li>
10782  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10783  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10784  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10785  * by the Reader into an object that will be stored in the Record. It is passed the
10786  * following parameters:<ul>
10787  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10788  * </ul></p></li>
10789  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10790  * </ul>
10791  * <br>usage:<br><pre><code>
10792 var TopicRecord = Roo.data.Record.create(
10793     {name: 'title', mapping: 'topic_title'},
10794     {name: 'author', mapping: 'username'},
10795     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10796     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10797     {name: 'lastPoster', mapping: 'user2'},
10798     {name: 'excerpt', mapping: 'post_text'}
10799 );
10800
10801 var myNewRecord = new TopicRecord({
10802     title: 'Do my job please',
10803     author: 'noobie',
10804     totalPosts: 1,
10805     lastPost: new Date(),
10806     lastPoster: 'Animal',
10807     excerpt: 'No way dude!'
10808 });
10809 myStore.add(myNewRecord);
10810 </code></pre>
10811  * @method create
10812  * @static
10813  */
10814 Roo.data.Record.create = function(o){
10815     var f = function(){
10816         f.superclass.constructor.apply(this, arguments);
10817     };
10818     Roo.extend(f, Roo.data.Record);
10819     var p = f.prototype;
10820     p.fields = new Roo.util.MixedCollection(false, function(field){
10821         return field.name;
10822     });
10823     for(var i = 0, len = o.length; i < len; i++){
10824         p.fields.add(new Roo.data.Field(o[i]));
10825     }
10826     f.getField = function(name){
10827         return p.fields.get(name);  
10828     };
10829     return f;
10830 };
10831
10832 Roo.data.Record.AUTO_ID = 1000;
10833 Roo.data.Record.EDIT = 'edit';
10834 Roo.data.Record.REJECT = 'reject';
10835 Roo.data.Record.COMMIT = 'commit';
10836
10837 Roo.data.Record.prototype = {
10838     /**
10839      * Readonly flag - true if this record has been modified.
10840      * @type Boolean
10841      */
10842     dirty : false,
10843     editing : false,
10844     error: null,
10845     modified: null,
10846
10847     // private
10848     join : function(store){
10849         this.store = store;
10850     },
10851
10852     /**
10853      * Set the named field to the specified value.
10854      * @param {String} name The name of the field to set.
10855      * @param {Object} value The value to set the field to.
10856      */
10857     set : function(name, value){
10858         if(this.data[name] == value){
10859             return;
10860         }
10861         this.dirty = true;
10862         if(!this.modified){
10863             this.modified = {};
10864         }
10865         if(typeof this.modified[name] == 'undefined'){
10866             this.modified[name] = this.data[name];
10867         }
10868         this.data[name] = value;
10869         if(!this.editing && this.store){
10870             this.store.afterEdit(this);
10871         }       
10872     },
10873
10874     /**
10875      * Get the value of the named field.
10876      * @param {String} name The name of the field to get the value of.
10877      * @return {Object} The value of the field.
10878      */
10879     get : function(name){
10880         return this.data[name]; 
10881     },
10882
10883     // private
10884     beginEdit : function(){
10885         this.editing = true;
10886         this.modified = {}; 
10887     },
10888
10889     // private
10890     cancelEdit : function(){
10891         this.editing = false;
10892         delete this.modified;
10893     },
10894
10895     // private
10896     endEdit : function(){
10897         this.editing = false;
10898         if(this.dirty && this.store){
10899             this.store.afterEdit(this);
10900         }
10901     },
10902
10903     /**
10904      * Usually called by the {@link Roo.data.Store} which owns the Record.
10905      * Rejects all changes made to the Record since either creation, or the last commit operation.
10906      * Modified fields are reverted to their original values.
10907      * <p>
10908      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10909      * of reject operations.
10910      */
10911     reject : function(){
10912         var m = this.modified;
10913         for(var n in m){
10914             if(typeof m[n] != "function"){
10915                 this.data[n] = m[n];
10916             }
10917         }
10918         this.dirty = false;
10919         delete this.modified;
10920         this.editing = false;
10921         if(this.store){
10922             this.store.afterReject(this);
10923         }
10924     },
10925
10926     /**
10927      * Usually called by the {@link Roo.data.Store} which owns the Record.
10928      * Commits all changes made to the Record since either creation, or the last commit operation.
10929      * <p>
10930      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10931      * of commit operations.
10932      */
10933     commit : function(){
10934         this.dirty = false;
10935         delete this.modified;
10936         this.editing = false;
10937         if(this.store){
10938             this.store.afterCommit(this);
10939         }
10940     },
10941
10942     // private
10943     hasError : function(){
10944         return this.error != null;
10945     },
10946
10947     // private
10948     clearError : function(){
10949         this.error = null;
10950     },
10951
10952     /**
10953      * Creates a copy of this record.
10954      * @param {String} id (optional) A new record id if you don't want to use this record's id
10955      * @return {Record}
10956      */
10957     copy : function(newId) {
10958         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10959     }
10960 };/*
10961  * Based on:
10962  * Ext JS Library 1.1.1
10963  * Copyright(c) 2006-2007, Ext JS, LLC.
10964  *
10965  * Originally Released Under LGPL - original licence link has changed is not relivant.
10966  *
10967  * Fork - LGPL
10968  * <script type="text/javascript">
10969  */
10970
10971
10972
10973 /**
10974  * @class Roo.data.Store
10975  * @extends Roo.util.Observable
10976  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10977  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10978  * <p>
10979  * 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
10980  * has no knowledge of the format of the data returned by the Proxy.<br>
10981  * <p>
10982  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10983  * instances from the data object. These records are cached and made available through accessor functions.
10984  * @constructor
10985  * Creates a new Store.
10986  * @param {Object} config A config object containing the objects needed for the Store to access data,
10987  * and read the data into Records.
10988  */
10989 Roo.data.Store = function(config){
10990     this.data = new Roo.util.MixedCollection(false);
10991     this.data.getKey = function(o){
10992         return o.id;
10993     };
10994     this.baseParams = {};
10995     // private
10996     this.paramNames = {
10997         "start" : "start",
10998         "limit" : "limit",
10999         "sort" : "sort",
11000         "dir" : "dir",
11001         "multisort" : "_multisort"
11002     };
11003
11004     if(config && config.data){
11005         this.inlineData = config.data;
11006         delete config.data;
11007     }
11008
11009     Roo.apply(this, config);
11010     
11011     if(this.reader){ // reader passed
11012         this.reader = Roo.factory(this.reader, Roo.data);
11013         this.reader.xmodule = this.xmodule || false;
11014         if(!this.recordType){
11015             this.recordType = this.reader.recordType;
11016         }
11017         if(this.reader.onMetaChange){
11018             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11019         }
11020     }
11021
11022     if(this.recordType){
11023         this.fields = this.recordType.prototype.fields;
11024     }
11025     this.modified = [];
11026
11027     this.addEvents({
11028         /**
11029          * @event datachanged
11030          * Fires when the data cache has changed, and a widget which is using this Store
11031          * as a Record cache should refresh its view.
11032          * @param {Store} this
11033          */
11034         datachanged : true,
11035         /**
11036          * @event metachange
11037          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11038          * @param {Store} this
11039          * @param {Object} meta The JSON metadata
11040          */
11041         metachange : true,
11042         /**
11043          * @event add
11044          * Fires when Records have been added to the Store
11045          * @param {Store} this
11046          * @param {Roo.data.Record[]} records The array of Records added
11047          * @param {Number} index The index at which the record(s) were added
11048          */
11049         add : true,
11050         /**
11051          * @event remove
11052          * Fires when a Record has been removed from the Store
11053          * @param {Store} this
11054          * @param {Roo.data.Record} record The Record that was removed
11055          * @param {Number} index The index at which the record was removed
11056          */
11057         remove : true,
11058         /**
11059          * @event update
11060          * Fires when a Record has been updated
11061          * @param {Store} this
11062          * @param {Roo.data.Record} record The Record that was updated
11063          * @param {String} operation The update operation being performed.  Value may be one of:
11064          * <pre><code>
11065  Roo.data.Record.EDIT
11066  Roo.data.Record.REJECT
11067  Roo.data.Record.COMMIT
11068          * </code></pre>
11069          */
11070         update : true,
11071         /**
11072          * @event clear
11073          * Fires when the data cache has been cleared.
11074          * @param {Store} this
11075          */
11076         clear : true,
11077         /**
11078          * @event beforeload
11079          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11080          * the load action will be canceled.
11081          * @param {Store} this
11082          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11083          */
11084         beforeload : true,
11085         /**
11086          * @event beforeloadadd
11087          * Fires after a new set of Records has been loaded.
11088          * @param {Store} this
11089          * @param {Roo.data.Record[]} records The Records that were loaded
11090          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11091          */
11092         beforeloadadd : true,
11093         /**
11094          * @event load
11095          * Fires after a new set of Records has been loaded, before they are added to the store.
11096          * @param {Store} this
11097          * @param {Roo.data.Record[]} records The Records that were loaded
11098          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11099          * @params {Object} return from reader
11100          */
11101         load : true,
11102         /**
11103          * @event loadexception
11104          * Fires if an exception occurs in the Proxy during loading.
11105          * Called with the signature of the Proxy's "loadexception" event.
11106          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11107          * 
11108          * @param {Proxy} 
11109          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11110          * @param {Object} load options 
11111          * @param {Object} jsonData from your request (normally this contains the Exception)
11112          */
11113         loadexception : true
11114     });
11115     
11116     if(this.proxy){
11117         this.proxy = Roo.factory(this.proxy, Roo.data);
11118         this.proxy.xmodule = this.xmodule || false;
11119         this.relayEvents(this.proxy,  ["loadexception"]);
11120     }
11121     this.sortToggle = {};
11122     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11123
11124     Roo.data.Store.superclass.constructor.call(this);
11125
11126     if(this.inlineData){
11127         this.loadData(this.inlineData);
11128         delete this.inlineData;
11129     }
11130 };
11131
11132 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11133      /**
11134     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11135     * without a remote query - used by combo/forms at present.
11136     */
11137     
11138     /**
11139     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11140     */
11141     /**
11142     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11143     */
11144     /**
11145     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11146     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11147     */
11148     /**
11149     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11150     * on any HTTP request
11151     */
11152     /**
11153     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11154     */
11155     /**
11156     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11157     */
11158     multiSort: false,
11159     /**
11160     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11161     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11162     */
11163     remoteSort : false,
11164
11165     /**
11166     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11167      * loaded or when a record is removed. (defaults to false).
11168     */
11169     pruneModifiedRecords : false,
11170
11171     // private
11172     lastOptions : null,
11173
11174     /**
11175      * Add Records to the Store and fires the add event.
11176      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11177      */
11178     add : function(records){
11179         records = [].concat(records);
11180         for(var i = 0, len = records.length; i < len; i++){
11181             records[i].join(this);
11182         }
11183         var index = this.data.length;
11184         this.data.addAll(records);
11185         this.fireEvent("add", this, records, index);
11186     },
11187
11188     /**
11189      * Remove a Record from the Store and fires the remove event.
11190      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11191      */
11192     remove : function(record){
11193         var index = this.data.indexOf(record);
11194         this.data.removeAt(index);
11195  
11196         if(this.pruneModifiedRecords){
11197             this.modified.remove(record);
11198         }
11199         this.fireEvent("remove", this, record, index);
11200     },
11201
11202     /**
11203      * Remove all Records from the Store and fires the clear event.
11204      */
11205     removeAll : function(){
11206         this.data.clear();
11207         if(this.pruneModifiedRecords){
11208             this.modified = [];
11209         }
11210         this.fireEvent("clear", this);
11211     },
11212
11213     /**
11214      * Inserts Records to the Store at the given index and fires the add event.
11215      * @param {Number} index The start index at which to insert the passed Records.
11216      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11217      */
11218     insert : function(index, records){
11219         records = [].concat(records);
11220         for(var i = 0, len = records.length; i < len; i++){
11221             this.data.insert(index, records[i]);
11222             records[i].join(this);
11223         }
11224         this.fireEvent("add", this, records, index);
11225     },
11226
11227     /**
11228      * Get the index within the cache of the passed Record.
11229      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11230      * @return {Number} The index of the passed Record. Returns -1 if not found.
11231      */
11232     indexOf : function(record){
11233         return this.data.indexOf(record);
11234     },
11235
11236     /**
11237      * Get the index within the cache of the Record with the passed id.
11238      * @param {String} id The id of the Record to find.
11239      * @return {Number} The index of the Record. Returns -1 if not found.
11240      */
11241     indexOfId : function(id){
11242         return this.data.indexOfKey(id);
11243     },
11244
11245     /**
11246      * Get the Record with the specified id.
11247      * @param {String} id The id of the Record to find.
11248      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11249      */
11250     getById : function(id){
11251         return this.data.key(id);
11252     },
11253
11254     /**
11255      * Get the Record at the specified index.
11256      * @param {Number} index The index of the Record to find.
11257      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11258      */
11259     getAt : function(index){
11260         return this.data.itemAt(index);
11261     },
11262
11263     /**
11264      * Returns a range of Records between specified indices.
11265      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11266      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11267      * @return {Roo.data.Record[]} An array of Records
11268      */
11269     getRange : function(start, end){
11270         return this.data.getRange(start, end);
11271     },
11272
11273     // private
11274     storeOptions : function(o){
11275         o = Roo.apply({}, o);
11276         delete o.callback;
11277         delete o.scope;
11278         this.lastOptions = o;
11279     },
11280
11281     /**
11282      * Loads the Record cache from the configured Proxy using the configured Reader.
11283      * <p>
11284      * If using remote paging, then the first load call must specify the <em>start</em>
11285      * and <em>limit</em> properties in the options.params property to establish the initial
11286      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11287      * <p>
11288      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11289      * and this call will return before the new data has been loaded. Perform any post-processing
11290      * in a callback function, or in a "load" event handler.</strong>
11291      * <p>
11292      * @param {Object} options An object containing properties which control loading options:<ul>
11293      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11294      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11295      * passed the following arguments:<ul>
11296      * <li>r : Roo.data.Record[]</li>
11297      * <li>options: Options object from the load call</li>
11298      * <li>success: Boolean success indicator</li></ul></li>
11299      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11300      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11301      * </ul>
11302      */
11303     load : function(options){
11304         options = options || {};
11305         if(this.fireEvent("beforeload", this, options) !== false){
11306             this.storeOptions(options);
11307             var p = Roo.apply(options.params || {}, this.baseParams);
11308             // if meta was not loaded from remote source.. try requesting it.
11309             if (!this.reader.metaFromRemote) {
11310                 p._requestMeta = 1;
11311             }
11312             if(this.sortInfo && this.remoteSort){
11313                 var pn = this.paramNames;
11314                 p[pn["sort"]] = this.sortInfo.field;
11315                 p[pn["dir"]] = this.sortInfo.direction;
11316             }
11317             if (this.multiSort) {
11318                 var pn = this.paramNames;
11319                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11320             }
11321             
11322             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11323         }
11324     },
11325
11326     /**
11327      * Reloads the Record cache from the configured Proxy using the configured Reader and
11328      * the options from the last load operation performed.
11329      * @param {Object} options (optional) An object containing properties which may override the options
11330      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11331      * the most recently used options are reused).
11332      */
11333     reload : function(options){
11334         this.load(Roo.applyIf(options||{}, this.lastOptions));
11335     },
11336
11337     // private
11338     // Called as a callback by the Reader during a load operation.
11339     loadRecords : function(o, options, success){
11340         if(!o || success === false){
11341             if(success !== false){
11342                 this.fireEvent("load", this, [], options, o);
11343             }
11344             if(options.callback){
11345                 options.callback.call(options.scope || this, [], options, false);
11346             }
11347             return;
11348         }
11349         // if data returned failure - throw an exception.
11350         if (o.success === false) {
11351             // show a message if no listener is registered.
11352             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11353                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11354             }
11355             // loadmask wil be hooked into this..
11356             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11357             return;
11358         }
11359         var r = o.records, t = o.totalRecords || r.length;
11360         
11361         this.fireEvent("beforeloadadd", this, r, options, o);
11362         
11363         if(!options || options.add !== true){
11364             if(this.pruneModifiedRecords){
11365                 this.modified = [];
11366             }
11367             for(var i = 0, len = r.length; i < len; i++){
11368                 r[i].join(this);
11369             }
11370             if(this.snapshot){
11371                 this.data = this.snapshot;
11372                 delete this.snapshot;
11373             }
11374             this.data.clear();
11375             this.data.addAll(r);
11376             this.totalLength = t;
11377             this.applySort();
11378             this.fireEvent("datachanged", this);
11379         }else{
11380             this.totalLength = Math.max(t, this.data.length+r.length);
11381             this.add(r);
11382         }
11383         
11384         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11385                 
11386             var e = new Roo.data.Record({});
11387
11388             e.set(this.parent.displayField, this.parent.emptyTitle);
11389             e.set(this.parent.valueField, '');
11390
11391             this.insert(0, e);
11392         }
11393             
11394         this.fireEvent("load", this, r, options, o);
11395         if(options.callback){
11396             options.callback.call(options.scope || this, r, options, true);
11397         }
11398     },
11399
11400
11401     /**
11402      * Loads data from a passed data block. A Reader which understands the format of the data
11403      * must have been configured in the constructor.
11404      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11405      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11406      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11407      */
11408     loadData : function(o, append){
11409         var r = this.reader.readRecords(o);
11410         this.loadRecords(r, {add: append}, true);
11411     },
11412
11413     /**
11414      * Gets the number of cached records.
11415      * <p>
11416      * <em>If using paging, this may not be the total size of the dataset. If the data object
11417      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11418      * the data set size</em>
11419      */
11420     getCount : function(){
11421         return this.data.length || 0;
11422     },
11423
11424     /**
11425      * Gets the total number of records in the dataset as returned by the server.
11426      * <p>
11427      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11428      * the dataset size</em>
11429      */
11430     getTotalCount : function(){
11431         return this.totalLength || 0;
11432     },
11433
11434     /**
11435      * Returns the sort state of the Store as an object with two properties:
11436      * <pre><code>
11437  field {String} The name of the field by which the Records are sorted
11438  direction {String} The sort order, "ASC" or "DESC"
11439      * </code></pre>
11440      */
11441     getSortState : function(){
11442         return this.sortInfo;
11443     },
11444
11445     // private
11446     applySort : function(){
11447         if(this.sortInfo && !this.remoteSort){
11448             var s = this.sortInfo, f = s.field;
11449             var st = this.fields.get(f).sortType;
11450             var fn = function(r1, r2){
11451                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11452                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11453             };
11454             this.data.sort(s.direction, fn);
11455             if(this.snapshot && this.snapshot != this.data){
11456                 this.snapshot.sort(s.direction, fn);
11457             }
11458         }
11459     },
11460
11461     /**
11462      * Sets the default sort column and order to be used by the next load operation.
11463      * @param {String} fieldName The name of the field to sort by.
11464      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11465      */
11466     setDefaultSort : function(field, dir){
11467         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11468     },
11469
11470     /**
11471      * Sort the Records.
11472      * If remote sorting is used, the sort is performed on the server, and the cache is
11473      * reloaded. If local sorting is used, the cache is sorted internally.
11474      * @param {String} fieldName The name of the field to sort by.
11475      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11476      */
11477     sort : function(fieldName, dir){
11478         var f = this.fields.get(fieldName);
11479         if(!dir){
11480             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11481             
11482             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11483                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11484             }else{
11485                 dir = f.sortDir;
11486             }
11487         }
11488         this.sortToggle[f.name] = dir;
11489         this.sortInfo = {field: f.name, direction: dir};
11490         if(!this.remoteSort){
11491             this.applySort();
11492             this.fireEvent("datachanged", this);
11493         }else{
11494             this.load(this.lastOptions);
11495         }
11496     },
11497
11498     /**
11499      * Calls the specified function for each of the Records in the cache.
11500      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11501      * Returning <em>false</em> aborts and exits the iteration.
11502      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11503      */
11504     each : function(fn, scope){
11505         this.data.each(fn, scope);
11506     },
11507
11508     /**
11509      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11510      * (e.g., during paging).
11511      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11512      */
11513     getModifiedRecords : function(){
11514         return this.modified;
11515     },
11516
11517     // private
11518     createFilterFn : function(property, value, anyMatch){
11519         if(!value.exec){ // not a regex
11520             value = String(value);
11521             if(value.length == 0){
11522                 return false;
11523             }
11524             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11525         }
11526         return function(r){
11527             return value.test(r.data[property]);
11528         };
11529     },
11530
11531     /**
11532      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11533      * @param {String} property A field on your records
11534      * @param {Number} start The record index to start at (defaults to 0)
11535      * @param {Number} end The last record index to include (defaults to length - 1)
11536      * @return {Number} The sum
11537      */
11538     sum : function(property, start, end){
11539         var rs = this.data.items, v = 0;
11540         start = start || 0;
11541         end = (end || end === 0) ? end : rs.length-1;
11542
11543         for(var i = start; i <= end; i++){
11544             v += (rs[i].data[property] || 0);
11545         }
11546         return v;
11547     },
11548
11549     /**
11550      * Filter the records by a specified property.
11551      * @param {String} field A field on your records
11552      * @param {String/RegExp} value Either a string that the field
11553      * should start with or a RegExp to test against the field
11554      * @param {Boolean} anyMatch True to match any part not just the beginning
11555      */
11556     filter : function(property, value, anyMatch){
11557         var fn = this.createFilterFn(property, value, anyMatch);
11558         return fn ? this.filterBy(fn) : this.clearFilter();
11559     },
11560
11561     /**
11562      * Filter by a function. The specified function will be called with each
11563      * record in this data source. If the function returns true the record is included,
11564      * otherwise it is filtered.
11565      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11566      * @param {Object} scope (optional) The scope of the function (defaults to this)
11567      */
11568     filterBy : function(fn, scope){
11569         this.snapshot = this.snapshot || this.data;
11570         this.data = this.queryBy(fn, scope||this);
11571         this.fireEvent("datachanged", this);
11572     },
11573
11574     /**
11575      * Query the records by a specified property.
11576      * @param {String} field A field on your records
11577      * @param {String/RegExp} value Either a string that the field
11578      * should start with or a RegExp to test against the field
11579      * @param {Boolean} anyMatch True to match any part not just the beginning
11580      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11581      */
11582     query : function(property, value, anyMatch){
11583         var fn = this.createFilterFn(property, value, anyMatch);
11584         return fn ? this.queryBy(fn) : this.data.clone();
11585     },
11586
11587     /**
11588      * Query by a function. The specified function will be called with each
11589      * record in this data source. If the function returns true the record is included
11590      * in the results.
11591      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11592      * @param {Object} scope (optional) The scope of the function (defaults to this)
11593       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11594      **/
11595     queryBy : function(fn, scope){
11596         var data = this.snapshot || this.data;
11597         return data.filterBy(fn, scope||this);
11598     },
11599
11600     /**
11601      * Collects unique values for a particular dataIndex from this store.
11602      * @param {String} dataIndex The property to collect
11603      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11604      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11605      * @return {Array} An array of the unique values
11606      **/
11607     collect : function(dataIndex, allowNull, bypassFilter){
11608         var d = (bypassFilter === true && this.snapshot) ?
11609                 this.snapshot.items : this.data.items;
11610         var v, sv, r = [], l = {};
11611         for(var i = 0, len = d.length; i < len; i++){
11612             v = d[i].data[dataIndex];
11613             sv = String(v);
11614             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11615                 l[sv] = true;
11616                 r[r.length] = v;
11617             }
11618         }
11619         return r;
11620     },
11621
11622     /**
11623      * Revert to a view of the Record cache with no filtering applied.
11624      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11625      */
11626     clearFilter : function(suppressEvent){
11627         if(this.snapshot && this.snapshot != this.data){
11628             this.data = this.snapshot;
11629             delete this.snapshot;
11630             if(suppressEvent !== true){
11631                 this.fireEvent("datachanged", this);
11632             }
11633         }
11634     },
11635
11636     // private
11637     afterEdit : function(record){
11638         if(this.modified.indexOf(record) == -1){
11639             this.modified.push(record);
11640         }
11641         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11642     },
11643     
11644     // private
11645     afterReject : function(record){
11646         this.modified.remove(record);
11647         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11648     },
11649
11650     // private
11651     afterCommit : function(record){
11652         this.modified.remove(record);
11653         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11654     },
11655
11656     /**
11657      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11658      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11659      */
11660     commitChanges : function(){
11661         var m = this.modified.slice(0);
11662         this.modified = [];
11663         for(var i = 0, len = m.length; i < len; i++){
11664             m[i].commit();
11665         }
11666     },
11667
11668     /**
11669      * Cancel outstanding changes on all changed records.
11670      */
11671     rejectChanges : function(){
11672         var m = this.modified.slice(0);
11673         this.modified = [];
11674         for(var i = 0, len = m.length; i < len; i++){
11675             m[i].reject();
11676         }
11677     },
11678
11679     onMetaChange : function(meta, rtype, o){
11680         this.recordType = rtype;
11681         this.fields = rtype.prototype.fields;
11682         delete this.snapshot;
11683         this.sortInfo = meta.sortInfo || this.sortInfo;
11684         this.modified = [];
11685         this.fireEvent('metachange', this, this.reader.meta);
11686     },
11687     
11688     moveIndex : function(data, type)
11689     {
11690         var index = this.indexOf(data);
11691         
11692         var newIndex = index + type;
11693         
11694         this.remove(data);
11695         
11696         this.insert(newIndex, data);
11697         
11698     }
11699 });/*
11700  * Based on:
11701  * Ext JS Library 1.1.1
11702  * Copyright(c) 2006-2007, Ext JS, LLC.
11703  *
11704  * Originally Released Under LGPL - original licence link has changed is not relivant.
11705  *
11706  * Fork - LGPL
11707  * <script type="text/javascript">
11708  */
11709
11710 /**
11711  * @class Roo.data.SimpleStore
11712  * @extends Roo.data.Store
11713  * Small helper class to make creating Stores from Array data easier.
11714  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11715  * @cfg {Array} fields An array of field definition objects, or field name strings.
11716  * @cfg {Array} data The multi-dimensional array of data
11717  * @constructor
11718  * @param {Object} config
11719  */
11720 Roo.data.SimpleStore = function(config){
11721     Roo.data.SimpleStore.superclass.constructor.call(this, {
11722         isLocal : true,
11723         reader: new Roo.data.ArrayReader({
11724                 id: config.id
11725             },
11726             Roo.data.Record.create(config.fields)
11727         ),
11728         proxy : new Roo.data.MemoryProxy(config.data)
11729     });
11730     this.load();
11731 };
11732 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11733  * Based on:
11734  * Ext JS Library 1.1.1
11735  * Copyright(c) 2006-2007, Ext JS, LLC.
11736  *
11737  * Originally Released Under LGPL - original licence link has changed is not relivant.
11738  *
11739  * Fork - LGPL
11740  * <script type="text/javascript">
11741  */
11742
11743 /**
11744 /**
11745  * @extends Roo.data.Store
11746  * @class Roo.data.JsonStore
11747  * Small helper class to make creating Stores for JSON data easier. <br/>
11748 <pre><code>
11749 var store = new Roo.data.JsonStore({
11750     url: 'get-images.php',
11751     root: 'images',
11752     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11753 });
11754 </code></pre>
11755  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11756  * JsonReader and HttpProxy (unless inline data is provided).</b>
11757  * @cfg {Array} fields An array of field definition objects, or field name strings.
11758  * @constructor
11759  * @param {Object} config
11760  */
11761 Roo.data.JsonStore = function(c){
11762     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11763         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11764         reader: new Roo.data.JsonReader(c, c.fields)
11765     }));
11766 };
11767 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11768  * Based on:
11769  * Ext JS Library 1.1.1
11770  * Copyright(c) 2006-2007, Ext JS, LLC.
11771  *
11772  * Originally Released Under LGPL - original licence link has changed is not relivant.
11773  *
11774  * Fork - LGPL
11775  * <script type="text/javascript">
11776  */
11777
11778  
11779 Roo.data.Field = function(config){
11780     if(typeof config == "string"){
11781         config = {name: config};
11782     }
11783     Roo.apply(this, config);
11784     
11785     if(!this.type){
11786         this.type = "auto";
11787     }
11788     
11789     var st = Roo.data.SortTypes;
11790     // named sortTypes are supported, here we look them up
11791     if(typeof this.sortType == "string"){
11792         this.sortType = st[this.sortType];
11793     }
11794     
11795     // set default sortType for strings and dates
11796     if(!this.sortType){
11797         switch(this.type){
11798             case "string":
11799                 this.sortType = st.asUCString;
11800                 break;
11801             case "date":
11802                 this.sortType = st.asDate;
11803                 break;
11804             default:
11805                 this.sortType = st.none;
11806         }
11807     }
11808
11809     // define once
11810     var stripRe = /[\$,%]/g;
11811
11812     // prebuilt conversion function for this field, instead of
11813     // switching every time we're reading a value
11814     if(!this.convert){
11815         var cv, dateFormat = this.dateFormat;
11816         switch(this.type){
11817             case "":
11818             case "auto":
11819             case undefined:
11820                 cv = function(v){ return v; };
11821                 break;
11822             case "string":
11823                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11824                 break;
11825             case "int":
11826                 cv = function(v){
11827                     return v !== undefined && v !== null && v !== '' ?
11828                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11829                     };
11830                 break;
11831             case "float":
11832                 cv = function(v){
11833                     return v !== undefined && v !== null && v !== '' ?
11834                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11835                     };
11836                 break;
11837             case "bool":
11838             case "boolean":
11839                 cv = function(v){ return v === true || v === "true" || v == 1; };
11840                 break;
11841             case "date":
11842                 cv = function(v){
11843                     if(!v){
11844                         return '';
11845                     }
11846                     if(v instanceof Date){
11847                         return v;
11848                     }
11849                     if(dateFormat){
11850                         if(dateFormat == "timestamp"){
11851                             return new Date(v*1000);
11852                         }
11853                         return Date.parseDate(v, dateFormat);
11854                     }
11855                     var parsed = Date.parse(v);
11856                     return parsed ? new Date(parsed) : null;
11857                 };
11858              break;
11859             
11860         }
11861         this.convert = cv;
11862     }
11863 };
11864
11865 Roo.data.Field.prototype = {
11866     dateFormat: null,
11867     defaultValue: "",
11868     mapping: null,
11869     sortType : null,
11870     sortDir : "ASC"
11871 };/*
11872  * Based on:
11873  * Ext JS Library 1.1.1
11874  * Copyright(c) 2006-2007, Ext JS, LLC.
11875  *
11876  * Originally Released Under LGPL - original licence link has changed is not relivant.
11877  *
11878  * Fork - LGPL
11879  * <script type="text/javascript">
11880  */
11881  
11882 // Base class for reading structured data from a data source.  This class is intended to be
11883 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11884
11885 /**
11886  * @class Roo.data.DataReader
11887  * Base class for reading structured data from a data source.  This class is intended to be
11888  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11889  */
11890
11891 Roo.data.DataReader = function(meta, recordType){
11892     
11893     this.meta = meta;
11894     
11895     this.recordType = recordType instanceof Array ? 
11896         Roo.data.Record.create(recordType) : recordType;
11897 };
11898
11899 Roo.data.DataReader.prototype = {
11900      /**
11901      * Create an empty record
11902      * @param {Object} data (optional) - overlay some values
11903      * @return {Roo.data.Record} record created.
11904      */
11905     newRow :  function(d) {
11906         var da =  {};
11907         this.recordType.prototype.fields.each(function(c) {
11908             switch( c.type) {
11909                 case 'int' : da[c.name] = 0; break;
11910                 case 'date' : da[c.name] = new Date(); break;
11911                 case 'float' : da[c.name] = 0.0; break;
11912                 case 'boolean' : da[c.name] = false; break;
11913                 default : da[c.name] = ""; break;
11914             }
11915             
11916         });
11917         return new this.recordType(Roo.apply(da, d));
11918     }
11919     
11920 };/*
11921  * Based on:
11922  * Ext JS Library 1.1.1
11923  * Copyright(c) 2006-2007, Ext JS, LLC.
11924  *
11925  * Originally Released Under LGPL - original licence link has changed is not relivant.
11926  *
11927  * Fork - LGPL
11928  * <script type="text/javascript">
11929  */
11930
11931 /**
11932  * @class Roo.data.DataProxy
11933  * @extends Roo.data.Observable
11934  * This class is an abstract base class for implementations which provide retrieval of
11935  * unformatted data objects.<br>
11936  * <p>
11937  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11938  * (of the appropriate type which knows how to parse the data object) to provide a block of
11939  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11940  * <p>
11941  * Custom implementations must implement the load method as described in
11942  * {@link Roo.data.HttpProxy#load}.
11943  */
11944 Roo.data.DataProxy = function(){
11945     this.addEvents({
11946         /**
11947          * @event beforeload
11948          * Fires before a network request is made to retrieve a data object.
11949          * @param {Object} This DataProxy object.
11950          * @param {Object} params The params parameter to the load function.
11951          */
11952         beforeload : true,
11953         /**
11954          * @event load
11955          * Fires before the load method's callback is called.
11956          * @param {Object} This DataProxy object.
11957          * @param {Object} o The data object.
11958          * @param {Object} arg The callback argument object passed to the load function.
11959          */
11960         load : true,
11961         /**
11962          * @event loadexception
11963          * Fires if an Exception occurs during data retrieval.
11964          * @param {Object} This DataProxy object.
11965          * @param {Object} o The data object.
11966          * @param {Object} arg The callback argument object passed to the load function.
11967          * @param {Object} e The Exception.
11968          */
11969         loadexception : true
11970     });
11971     Roo.data.DataProxy.superclass.constructor.call(this);
11972 };
11973
11974 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11975
11976     /**
11977      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11978      */
11979 /*
11980  * Based on:
11981  * Ext JS Library 1.1.1
11982  * Copyright(c) 2006-2007, Ext JS, LLC.
11983  *
11984  * Originally Released Under LGPL - original licence link has changed is not relivant.
11985  *
11986  * Fork - LGPL
11987  * <script type="text/javascript">
11988  */
11989 /**
11990  * @class Roo.data.MemoryProxy
11991  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11992  * to the Reader when its load method is called.
11993  * @constructor
11994  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11995  */
11996 Roo.data.MemoryProxy = function(data){
11997     if (data.data) {
11998         data = data.data;
11999     }
12000     Roo.data.MemoryProxy.superclass.constructor.call(this);
12001     this.data = data;
12002 };
12003
12004 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12005     
12006     /**
12007      * Load data from the requested source (in this case an in-memory
12008      * data object passed to the constructor), read the data object into
12009      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12010      * process that block using the passed callback.
12011      * @param {Object} params This parameter is not used by the MemoryProxy class.
12012      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12013      * object into a block of Roo.data.Records.
12014      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12015      * The function must be passed <ul>
12016      * <li>The Record block object</li>
12017      * <li>The "arg" argument from the load function</li>
12018      * <li>A boolean success indicator</li>
12019      * </ul>
12020      * @param {Object} scope The scope in which to call the callback
12021      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12022      */
12023     load : function(params, reader, callback, scope, arg){
12024         params = params || {};
12025         var result;
12026         try {
12027             result = reader.readRecords(this.data);
12028         }catch(e){
12029             this.fireEvent("loadexception", this, arg, null, e);
12030             callback.call(scope, null, arg, false);
12031             return;
12032         }
12033         callback.call(scope, result, arg, true);
12034     },
12035     
12036     // private
12037     update : function(params, records){
12038         
12039     }
12040 });/*
12041  * Based on:
12042  * Ext JS Library 1.1.1
12043  * Copyright(c) 2006-2007, Ext JS, LLC.
12044  *
12045  * Originally Released Under LGPL - original licence link has changed is not relivant.
12046  *
12047  * Fork - LGPL
12048  * <script type="text/javascript">
12049  */
12050 /**
12051  * @class Roo.data.HttpProxy
12052  * @extends Roo.data.DataProxy
12053  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12054  * configured to reference a certain URL.<br><br>
12055  * <p>
12056  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12057  * from which the running page was served.<br><br>
12058  * <p>
12059  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12060  * <p>
12061  * Be aware that to enable the browser to parse an XML document, the server must set
12062  * the Content-Type header in the HTTP response to "text/xml".
12063  * @constructor
12064  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12065  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12066  * will be used to make the request.
12067  */
12068 Roo.data.HttpProxy = function(conn){
12069     Roo.data.HttpProxy.superclass.constructor.call(this);
12070     // is conn a conn config or a real conn?
12071     this.conn = conn;
12072     this.useAjax = !conn || !conn.events;
12073   
12074 };
12075
12076 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12077     // thse are take from connection...
12078     
12079     /**
12080      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12081      */
12082     /**
12083      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12084      * extra parameters to each request made by this object. (defaults to undefined)
12085      */
12086     /**
12087      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12088      *  to each request made by this object. (defaults to undefined)
12089      */
12090     /**
12091      * @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)
12092      */
12093     /**
12094      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12095      */
12096      /**
12097      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12098      * @type Boolean
12099      */
12100   
12101
12102     /**
12103      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12104      * @type Boolean
12105      */
12106     /**
12107      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12108      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12109      * a finer-grained basis than the DataProxy events.
12110      */
12111     getConnection : function(){
12112         return this.useAjax ? Roo.Ajax : this.conn;
12113     },
12114
12115     /**
12116      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12117      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12118      * process that block using the passed callback.
12119      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12120      * for the request to the remote server.
12121      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12122      * object into a block of Roo.data.Records.
12123      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12124      * The function must be passed <ul>
12125      * <li>The Record block object</li>
12126      * <li>The "arg" argument from the load function</li>
12127      * <li>A boolean success indicator</li>
12128      * </ul>
12129      * @param {Object} scope The scope in which to call the callback
12130      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12131      */
12132     load : function(params, reader, callback, scope, arg){
12133         if(this.fireEvent("beforeload", this, params) !== false){
12134             var  o = {
12135                 params : params || {},
12136                 request: {
12137                     callback : callback,
12138                     scope : scope,
12139                     arg : arg
12140                 },
12141                 reader: reader,
12142                 callback : this.loadResponse,
12143                 scope: this
12144             };
12145             if(this.useAjax){
12146                 Roo.applyIf(o, this.conn);
12147                 if(this.activeRequest){
12148                     Roo.Ajax.abort(this.activeRequest);
12149                 }
12150                 this.activeRequest = Roo.Ajax.request(o);
12151             }else{
12152                 this.conn.request(o);
12153             }
12154         }else{
12155             callback.call(scope||this, null, arg, false);
12156         }
12157     },
12158
12159     // private
12160     loadResponse : function(o, success, response){
12161         delete this.activeRequest;
12162         if(!success){
12163             this.fireEvent("loadexception", this, o, response);
12164             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12165             return;
12166         }
12167         var result;
12168         try {
12169             result = o.reader.read(response);
12170         }catch(e){
12171             this.fireEvent("loadexception", this, o, response, e);
12172             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12173             return;
12174         }
12175         
12176         this.fireEvent("load", this, o, o.request.arg);
12177         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12178     },
12179
12180     // private
12181     update : function(dataSet){
12182
12183     },
12184
12185     // private
12186     updateResponse : function(dataSet){
12187
12188     }
12189 });/*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199
12200 /**
12201  * @class Roo.data.ScriptTagProxy
12202  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12203  * other than the originating domain of the running page.<br><br>
12204  * <p>
12205  * <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
12206  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12207  * <p>
12208  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12209  * source code that is used as the source inside a &lt;script> tag.<br><br>
12210  * <p>
12211  * In order for the browser to process the returned data, the server must wrap the data object
12212  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12213  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12214  * depending on whether the callback name was passed:
12215  * <p>
12216  * <pre><code>
12217 boolean scriptTag = false;
12218 String cb = request.getParameter("callback");
12219 if (cb != null) {
12220     scriptTag = true;
12221     response.setContentType("text/javascript");
12222 } else {
12223     response.setContentType("application/x-json");
12224 }
12225 Writer out = response.getWriter();
12226 if (scriptTag) {
12227     out.write(cb + "(");
12228 }
12229 out.print(dataBlock.toJsonString());
12230 if (scriptTag) {
12231     out.write(");");
12232 }
12233 </pre></code>
12234  *
12235  * @constructor
12236  * @param {Object} config A configuration object.
12237  */
12238 Roo.data.ScriptTagProxy = function(config){
12239     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12240     Roo.apply(this, config);
12241     this.head = document.getElementsByTagName("head")[0];
12242 };
12243
12244 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12245
12246 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12247     /**
12248      * @cfg {String} url The URL from which to request the data object.
12249      */
12250     /**
12251      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12252      */
12253     timeout : 30000,
12254     /**
12255      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12256      * the server the name of the callback function set up by the load call to process the returned data object.
12257      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12258      * javascript output which calls this named function passing the data object as its only parameter.
12259      */
12260     callbackParam : "callback",
12261     /**
12262      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12263      * name to the request.
12264      */
12265     nocache : true,
12266
12267     /**
12268      * Load data from the configured URL, read the data object into
12269      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12270      * process that block using the passed callback.
12271      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12272      * for the request to the remote server.
12273      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12274      * object into a block of Roo.data.Records.
12275      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12276      * The function must be passed <ul>
12277      * <li>The Record block object</li>
12278      * <li>The "arg" argument from the load function</li>
12279      * <li>A boolean success indicator</li>
12280      * </ul>
12281      * @param {Object} scope The scope in which to call the callback
12282      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12283      */
12284     load : function(params, reader, callback, scope, arg){
12285         if(this.fireEvent("beforeload", this, params) !== false){
12286
12287             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12288
12289             var url = this.url;
12290             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12291             if(this.nocache){
12292                 url += "&_dc=" + (new Date().getTime());
12293             }
12294             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12295             var trans = {
12296                 id : transId,
12297                 cb : "stcCallback"+transId,
12298                 scriptId : "stcScript"+transId,
12299                 params : params,
12300                 arg : arg,
12301                 url : url,
12302                 callback : callback,
12303                 scope : scope,
12304                 reader : reader
12305             };
12306             var conn = this;
12307
12308             window[trans.cb] = function(o){
12309                 conn.handleResponse(o, trans);
12310             };
12311
12312             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12313
12314             if(this.autoAbort !== false){
12315                 this.abort();
12316             }
12317
12318             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12319
12320             var script = document.createElement("script");
12321             script.setAttribute("src", url);
12322             script.setAttribute("type", "text/javascript");
12323             script.setAttribute("id", trans.scriptId);
12324             this.head.appendChild(script);
12325
12326             this.trans = trans;
12327         }else{
12328             callback.call(scope||this, null, arg, false);
12329         }
12330     },
12331
12332     // private
12333     isLoading : function(){
12334         return this.trans ? true : false;
12335     },
12336
12337     /**
12338      * Abort the current server request.
12339      */
12340     abort : function(){
12341         if(this.isLoading()){
12342             this.destroyTrans(this.trans);
12343         }
12344     },
12345
12346     // private
12347     destroyTrans : function(trans, isLoaded){
12348         this.head.removeChild(document.getElementById(trans.scriptId));
12349         clearTimeout(trans.timeoutId);
12350         if(isLoaded){
12351             window[trans.cb] = undefined;
12352             try{
12353                 delete window[trans.cb];
12354             }catch(e){}
12355         }else{
12356             // if hasn't been loaded, wait for load to remove it to prevent script error
12357             window[trans.cb] = function(){
12358                 window[trans.cb] = undefined;
12359                 try{
12360                     delete window[trans.cb];
12361                 }catch(e){}
12362             };
12363         }
12364     },
12365
12366     // private
12367     handleResponse : function(o, trans){
12368         this.trans = false;
12369         this.destroyTrans(trans, true);
12370         var result;
12371         try {
12372             result = trans.reader.readRecords(o);
12373         }catch(e){
12374             this.fireEvent("loadexception", this, o, trans.arg, e);
12375             trans.callback.call(trans.scope||window, null, trans.arg, false);
12376             return;
12377         }
12378         this.fireEvent("load", this, o, trans.arg);
12379         trans.callback.call(trans.scope||window, result, trans.arg, true);
12380     },
12381
12382     // private
12383     handleFailure : function(trans){
12384         this.trans = false;
12385         this.destroyTrans(trans, false);
12386         this.fireEvent("loadexception", this, null, trans.arg);
12387         trans.callback.call(trans.scope||window, null, trans.arg, false);
12388     }
12389 });/*
12390  * Based on:
12391  * Ext JS Library 1.1.1
12392  * Copyright(c) 2006-2007, Ext JS, LLC.
12393  *
12394  * Originally Released Under LGPL - original licence link has changed is not relivant.
12395  *
12396  * Fork - LGPL
12397  * <script type="text/javascript">
12398  */
12399
12400 /**
12401  * @class Roo.data.JsonReader
12402  * @extends Roo.data.DataReader
12403  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12404  * based on mappings in a provided Roo.data.Record constructor.
12405  * 
12406  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12407  * in the reply previously. 
12408  * 
12409  * <p>
12410  * Example code:
12411  * <pre><code>
12412 var RecordDef = Roo.data.Record.create([
12413     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12414     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12415 ]);
12416 var myReader = new Roo.data.JsonReader({
12417     totalProperty: "results",    // The property which contains the total dataset size (optional)
12418     root: "rows",                // The property which contains an Array of row objects
12419     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12420 }, RecordDef);
12421 </code></pre>
12422  * <p>
12423  * This would consume a JSON file like this:
12424  * <pre><code>
12425 { 'results': 2, 'rows': [
12426     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12427     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12428 }
12429 </code></pre>
12430  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12431  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12432  * paged from the remote server.
12433  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12434  * @cfg {String} root name of the property which contains the Array of row objects.
12435  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12436  * @cfg {Array} fields Array of field definition objects
12437  * @constructor
12438  * Create a new JsonReader
12439  * @param {Object} meta Metadata configuration options
12440  * @param {Object} recordType Either an Array of field definition objects,
12441  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12442  */
12443 Roo.data.JsonReader = function(meta, recordType){
12444     
12445     meta = meta || {};
12446     // set some defaults:
12447     Roo.applyIf(meta, {
12448         totalProperty: 'total',
12449         successProperty : 'success',
12450         root : 'data',
12451         id : 'id'
12452     });
12453     
12454     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12455 };
12456 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12457     
12458     /**
12459      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12460      * Used by Store query builder to append _requestMeta to params.
12461      * 
12462      */
12463     metaFromRemote : false,
12464     /**
12465      * This method is only used by a DataProxy which has retrieved data from a remote server.
12466      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12467      * @return {Object} data A data block which is used by an Roo.data.Store object as
12468      * a cache of Roo.data.Records.
12469      */
12470     read : function(response){
12471         var json = response.responseText;
12472        
12473         var o = /* eval:var:o */ eval("("+json+")");
12474         if(!o) {
12475             throw {message: "JsonReader.read: Json object not found"};
12476         }
12477         
12478         if(o.metaData){
12479             
12480             delete this.ef;
12481             this.metaFromRemote = true;
12482             this.meta = o.metaData;
12483             this.recordType = Roo.data.Record.create(o.metaData.fields);
12484             this.onMetaChange(this.meta, this.recordType, o);
12485         }
12486         return this.readRecords(o);
12487     },
12488
12489     // private function a store will implement
12490     onMetaChange : function(meta, recordType, o){
12491
12492     },
12493
12494     /**
12495          * @ignore
12496          */
12497     simpleAccess: function(obj, subsc) {
12498         return obj[subsc];
12499     },
12500
12501         /**
12502          * @ignore
12503          */
12504     getJsonAccessor: function(){
12505         var re = /[\[\.]/;
12506         return function(expr) {
12507             try {
12508                 return(re.test(expr))
12509                     ? new Function("obj", "return obj." + expr)
12510                     : function(obj){
12511                         return obj[expr];
12512                     };
12513             } catch(e){}
12514             return Roo.emptyFn;
12515         };
12516     }(),
12517
12518     /**
12519      * Create a data block containing Roo.data.Records from an XML document.
12520      * @param {Object} o An object which contains an Array of row objects in the property specified
12521      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12522      * which contains the total size of the dataset.
12523      * @return {Object} data A data block which is used by an Roo.data.Store object as
12524      * a cache of Roo.data.Records.
12525      */
12526     readRecords : function(o){
12527         /**
12528          * After any data loads, the raw JSON data is available for further custom processing.
12529          * @type Object
12530          */
12531         this.o = o;
12532         var s = this.meta, Record = this.recordType,
12533             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12534
12535 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12536         if (!this.ef) {
12537             if(s.totalProperty) {
12538                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12539                 }
12540                 if(s.successProperty) {
12541                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12542                 }
12543                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12544                 if (s.id) {
12545                         var g = this.getJsonAccessor(s.id);
12546                         this.getId = function(rec) {
12547                                 var r = g(rec);  
12548                                 return (r === undefined || r === "") ? null : r;
12549                         };
12550                 } else {
12551                         this.getId = function(){return null;};
12552                 }
12553             this.ef = [];
12554             for(var jj = 0; jj < fl; jj++){
12555                 f = fi[jj];
12556                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12557                 this.ef[jj] = this.getJsonAccessor(map);
12558             }
12559         }
12560
12561         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12562         if(s.totalProperty){
12563             var vt = parseInt(this.getTotal(o), 10);
12564             if(!isNaN(vt)){
12565                 totalRecords = vt;
12566             }
12567         }
12568         if(s.successProperty){
12569             var vs = this.getSuccess(o);
12570             if(vs === false || vs === 'false'){
12571                 success = false;
12572             }
12573         }
12574         var records = [];
12575         for(var i = 0; i < c; i++){
12576                 var n = root[i];
12577             var values = {};
12578             var id = this.getId(n);
12579             for(var j = 0; j < fl; j++){
12580                 f = fi[j];
12581             var v = this.ef[j](n);
12582             if (!f.convert) {
12583                 Roo.log('missing convert for ' + f.name);
12584                 Roo.log(f);
12585                 continue;
12586             }
12587             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12588             }
12589             var record = new Record(values, id);
12590             record.json = n;
12591             records[i] = record;
12592         }
12593         return {
12594             raw : o,
12595             success : success,
12596             records : records,
12597             totalRecords : totalRecords
12598         };
12599     }
12600 });/*
12601  * Based on:
12602  * Ext JS Library 1.1.1
12603  * Copyright(c) 2006-2007, Ext JS, LLC.
12604  *
12605  * Originally Released Under LGPL - original licence link has changed is not relivant.
12606  *
12607  * Fork - LGPL
12608  * <script type="text/javascript">
12609  */
12610
12611 /**
12612  * @class Roo.data.ArrayReader
12613  * @extends Roo.data.DataReader
12614  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12615  * Each element of that Array represents a row of data fields. The
12616  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12617  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12618  * <p>
12619  * Example code:.
12620  * <pre><code>
12621 var RecordDef = Roo.data.Record.create([
12622     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12623     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12624 ]);
12625 var myReader = new Roo.data.ArrayReader({
12626     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12627 }, RecordDef);
12628 </code></pre>
12629  * <p>
12630  * This would consume an Array like this:
12631  * <pre><code>
12632 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12633   </code></pre>
12634  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12635  * @constructor
12636  * Create a new JsonReader
12637  * @param {Object} meta Metadata configuration options.
12638  * @param {Object} recordType Either an Array of field definition objects
12639  * as specified to {@link Roo.data.Record#create},
12640  * or an {@link Roo.data.Record} object
12641  * created using {@link Roo.data.Record#create}.
12642  */
12643 Roo.data.ArrayReader = function(meta, recordType){
12644     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12645 };
12646
12647 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12648     /**
12649      * Create a data block containing Roo.data.Records from an XML document.
12650      * @param {Object} o An Array of row objects which represents the dataset.
12651      * @return {Object} data A data block which is used by an Roo.data.Store object as
12652      * a cache of Roo.data.Records.
12653      */
12654     readRecords : function(o){
12655         var sid = this.meta ? this.meta.id : null;
12656         var recordType = this.recordType, fields = recordType.prototype.fields;
12657         var records = [];
12658         var root = o;
12659             for(var i = 0; i < root.length; i++){
12660                     var n = root[i];
12661                 var values = {};
12662                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12663                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12664                 var f = fields.items[j];
12665                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12666                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12667                 v = f.convert(v);
12668                 values[f.name] = v;
12669             }
12670                 var record = new recordType(values, id);
12671                 record.json = n;
12672                 records[records.length] = record;
12673             }
12674             return {
12675                 records : records,
12676                 totalRecords : records.length
12677             };
12678     }
12679 });/*
12680  * - LGPL
12681  * * 
12682  */
12683
12684 /**
12685  * @class Roo.bootstrap.ComboBox
12686  * @extends Roo.bootstrap.TriggerField
12687  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12688  * @cfg {Boolean} append (true|false) default false
12689  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12690  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12691  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12692  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12693  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12694  * @cfg {Boolean} animate default true
12695  * @cfg {Boolean} emptyResultText only for touch device
12696  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12697  * @cfg {String} emptyTitle default ''
12698  * @constructor
12699  * Create a new ComboBox.
12700  * @param {Object} config Configuration options
12701  */
12702 Roo.bootstrap.ComboBox = function(config){
12703     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12704     this.addEvents({
12705         /**
12706          * @event expand
12707          * Fires when the dropdown list is expanded
12708         * @param {Roo.bootstrap.ComboBox} combo This combo box
12709         */
12710         'expand' : true,
12711         /**
12712          * @event collapse
12713          * Fires when the dropdown list is collapsed
12714         * @param {Roo.bootstrap.ComboBox} combo This combo box
12715         */
12716         'collapse' : true,
12717         /**
12718          * @event beforeselect
12719          * Fires before a list item is selected. Return false to cancel the selection.
12720         * @param {Roo.bootstrap.ComboBox} combo This combo box
12721         * @param {Roo.data.Record} record The data record returned from the underlying store
12722         * @param {Number} index The index of the selected item in the dropdown list
12723         */
12724         'beforeselect' : true,
12725         /**
12726          * @event select
12727          * Fires when a list item is selected
12728         * @param {Roo.bootstrap.ComboBox} combo This combo box
12729         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12730         * @param {Number} index The index of the selected item in the dropdown list
12731         */
12732         'select' : true,
12733         /**
12734          * @event beforequery
12735          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12736          * The event object passed has these properties:
12737         * @param {Roo.bootstrap.ComboBox} combo This combo box
12738         * @param {String} query The query
12739         * @param {Boolean} forceAll true to force "all" query
12740         * @param {Boolean} cancel true to cancel the query
12741         * @param {Object} e The query event object
12742         */
12743         'beforequery': true,
12744          /**
12745          * @event add
12746          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12747         * @param {Roo.bootstrap.ComboBox} combo This combo box
12748         */
12749         'add' : true,
12750         /**
12751          * @event edit
12752          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12753         * @param {Roo.bootstrap.ComboBox} combo This combo box
12754         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12755         */
12756         'edit' : true,
12757         /**
12758          * @event remove
12759          * Fires when the remove value from the combobox array
12760         * @param {Roo.bootstrap.ComboBox} combo This combo box
12761         */
12762         'remove' : true,
12763         /**
12764          * @event afterremove
12765          * Fires when the remove value from the combobox array
12766         * @param {Roo.bootstrap.ComboBox} combo This combo box
12767         */
12768         'afterremove' : true,
12769         /**
12770          * @event specialfilter
12771          * Fires when specialfilter
12772             * @param {Roo.bootstrap.ComboBox} combo This combo box
12773             */
12774         'specialfilter' : true,
12775         /**
12776          * @event tick
12777          * Fires when tick the element
12778             * @param {Roo.bootstrap.ComboBox} combo This combo box
12779             */
12780         'tick' : true,
12781         /**
12782          * @event touchviewdisplay
12783          * Fires when touch view require special display (default is using displayField)
12784             * @param {Roo.bootstrap.ComboBox} combo This combo box
12785             * @param {Object} cfg set html .
12786             */
12787         'touchviewdisplay' : true
12788         
12789     });
12790     
12791     this.item = [];
12792     this.tickItems = [];
12793     
12794     this.selectedIndex = -1;
12795     if(this.mode == 'local'){
12796         if(config.queryDelay === undefined){
12797             this.queryDelay = 10;
12798         }
12799         if(config.minChars === undefined){
12800             this.minChars = 0;
12801         }
12802     }
12803 };
12804
12805 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12806      
12807     /**
12808      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12809      * rendering into an Roo.Editor, defaults to false)
12810      */
12811     /**
12812      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12813      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12814      */
12815     /**
12816      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12817      */
12818     /**
12819      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12820      * the dropdown list (defaults to undefined, with no header element)
12821      */
12822
12823      /**
12824      * @cfg {String/Roo.Template} tpl The template to use to render the output
12825      */
12826      
12827      /**
12828      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12829      */
12830     listWidth: undefined,
12831     /**
12832      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12833      * mode = 'remote' or 'text' if mode = 'local')
12834      */
12835     displayField: undefined,
12836     
12837     /**
12838      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12839      * mode = 'remote' or 'value' if mode = 'local'). 
12840      * Note: use of a valueField requires the user make a selection
12841      * in order for a value to be mapped.
12842      */
12843     valueField: undefined,
12844     /**
12845      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12846      */
12847     modalTitle : '',
12848     
12849     /**
12850      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12851      * field's data value (defaults to the underlying DOM element's name)
12852      */
12853     hiddenName: undefined,
12854     /**
12855      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12856      */
12857     listClass: '',
12858     /**
12859      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12860      */
12861     selectedClass: 'active',
12862     
12863     /**
12864      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12865      */
12866     shadow:'sides',
12867     /**
12868      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12869      * anchor positions (defaults to 'tl-bl')
12870      */
12871     listAlign: 'tl-bl?',
12872     /**
12873      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12874      */
12875     maxHeight: 300,
12876     /**
12877      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12878      * query specified by the allQuery config option (defaults to 'query')
12879      */
12880     triggerAction: 'query',
12881     /**
12882      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12883      * (defaults to 4, does not apply if editable = false)
12884      */
12885     minChars : 4,
12886     /**
12887      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12888      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12889      */
12890     typeAhead: false,
12891     /**
12892      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12893      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12894      */
12895     queryDelay: 500,
12896     /**
12897      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12898      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12899      */
12900     pageSize: 0,
12901     /**
12902      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12903      * when editable = true (defaults to false)
12904      */
12905     selectOnFocus:false,
12906     /**
12907      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12908      */
12909     queryParam: 'query',
12910     /**
12911      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12912      * when mode = 'remote' (defaults to 'Loading...')
12913      */
12914     loadingText: 'Loading...',
12915     /**
12916      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12917      */
12918     resizable: false,
12919     /**
12920      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12921      */
12922     handleHeight : 8,
12923     /**
12924      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12925      * traditional select (defaults to true)
12926      */
12927     editable: true,
12928     /**
12929      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12930      */
12931     allQuery: '',
12932     /**
12933      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12934      */
12935     mode: 'remote',
12936     /**
12937      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12938      * listWidth has a higher value)
12939      */
12940     minListWidth : 70,
12941     /**
12942      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12943      * allow the user to set arbitrary text into the field (defaults to false)
12944      */
12945     forceSelection:false,
12946     /**
12947      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12948      * if typeAhead = true (defaults to 250)
12949      */
12950     typeAheadDelay : 250,
12951     /**
12952      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12953      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12954      */
12955     valueNotFoundText : undefined,
12956     /**
12957      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12958      */
12959     blockFocus : false,
12960     
12961     /**
12962      * @cfg {Boolean} disableClear Disable showing of clear button.
12963      */
12964     disableClear : false,
12965     /**
12966      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12967      */
12968     alwaysQuery : false,
12969     
12970     /**
12971      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12972      */
12973     multiple : false,
12974     
12975     /**
12976      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12977      */
12978     invalidClass : "has-warning",
12979     
12980     /**
12981      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12982      */
12983     validClass : "has-success",
12984     
12985     /**
12986      * @cfg {Boolean} specialFilter (true|false) special filter default false
12987      */
12988     specialFilter : false,
12989     
12990     /**
12991      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12992      */
12993     mobileTouchView : true,
12994     
12995     /**
12996      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12997      */
12998     useNativeIOS : false,
12999     
13000     ios_options : false,
13001     
13002     //private
13003     addicon : false,
13004     editicon: false,
13005     
13006     page: 0,
13007     hasQuery: false,
13008     append: false,
13009     loadNext: false,
13010     autoFocus : true,
13011     tickable : false,
13012     btnPosition : 'right',
13013     triggerList : true,
13014     showToggleBtn : true,
13015     animate : true,
13016     emptyResultText: 'Empty',
13017     triggerText : 'Select',
13018     emptyTitle : '',
13019     
13020     // element that contains real text value.. (when hidden is used..)
13021     
13022     getAutoCreate : function()
13023     {   
13024         var cfg = false;
13025         //render
13026         /*
13027          * Render classic select for iso
13028          */
13029         
13030         if(Roo.isIOS && this.useNativeIOS){
13031             cfg = this.getAutoCreateNativeIOS();
13032             return cfg;
13033         }
13034         
13035         /*
13036          * Touch Devices
13037          */
13038         
13039         if(Roo.isTouch && this.mobileTouchView){
13040             cfg = this.getAutoCreateTouchView();
13041             return cfg;;
13042         }
13043         
13044         /*
13045          *  Normal ComboBox
13046          */
13047         if(!this.tickable){
13048             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13049             return cfg;
13050         }
13051         
13052         /*
13053          *  ComboBox with tickable selections
13054          */
13055              
13056         var align = this.labelAlign || this.parentLabelAlign();
13057         
13058         cfg = {
13059             cls : 'form-group roo-combobox-tickable' //input-group
13060         };
13061         
13062         var btn_text_select = '';
13063         var btn_text_done = '';
13064         var btn_text_cancel = '';
13065         
13066         if (this.btn_text_show) {
13067             btn_text_select = 'Select';
13068             btn_text_done = 'Done';
13069             btn_text_cancel = 'Cancel'; 
13070         }
13071         
13072         var buttons = {
13073             tag : 'div',
13074             cls : 'tickable-buttons',
13075             cn : [
13076                 {
13077                     tag : 'button',
13078                     type : 'button',
13079                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13080                     //html : this.triggerText
13081                     html: btn_text_select
13082                 },
13083                 {
13084                     tag : 'button',
13085                     type : 'button',
13086                     name : 'ok',
13087                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13088                     //html : 'Done'
13089                     html: btn_text_done
13090                 },
13091                 {
13092                     tag : 'button',
13093                     type : 'button',
13094                     name : 'cancel',
13095                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13096                     //html : 'Cancel'
13097                     html: btn_text_cancel
13098                 }
13099             ]
13100         };
13101         
13102         if(this.editable){
13103             buttons.cn.unshift({
13104                 tag: 'input',
13105                 cls: 'roo-select2-search-field-input'
13106             });
13107         }
13108         
13109         var _this = this;
13110         
13111         Roo.each(buttons.cn, function(c){
13112             if (_this.size) {
13113                 c.cls += ' btn-' + _this.size;
13114             }
13115
13116             if (_this.disabled) {
13117                 c.disabled = true;
13118             }
13119         });
13120         
13121         var box = {
13122             tag: 'div',
13123             cn: [
13124                 {
13125                     tag: 'input',
13126                     type : 'hidden',
13127                     cls: 'form-hidden-field'
13128                 },
13129                 {
13130                     tag: 'ul',
13131                     cls: 'roo-select2-choices',
13132                     cn:[
13133                         {
13134                             tag: 'li',
13135                             cls: 'roo-select2-search-field',
13136                             cn: [
13137                                 buttons
13138                             ]
13139                         }
13140                     ]
13141                 }
13142             ]
13143         };
13144         
13145         var combobox = {
13146             cls: 'roo-select2-container input-group roo-select2-container-multi',
13147             cn: [
13148                 box
13149 //                {
13150 //                    tag: 'ul',
13151 //                    cls: 'typeahead typeahead-long dropdown-menu',
13152 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13153 //                }
13154             ]
13155         };
13156         
13157         if(this.hasFeedback && !this.allowBlank){
13158             
13159             var feedback = {
13160                 tag: 'span',
13161                 cls: 'glyphicon form-control-feedback'
13162             };
13163
13164             combobox.cn.push(feedback);
13165         }
13166         
13167         
13168         if (align ==='left' && this.fieldLabel.length) {
13169             
13170             cfg.cls += ' roo-form-group-label-left';
13171             
13172             cfg.cn = [
13173                 {
13174                     tag : 'i',
13175                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13176                     tooltip : 'This field is required'
13177                 },
13178                 {
13179                     tag: 'label',
13180                     'for' :  id,
13181                     cls : 'control-label',
13182                     html : this.fieldLabel
13183
13184                 },
13185                 {
13186                     cls : "", 
13187                     cn: [
13188                         combobox
13189                     ]
13190                 }
13191
13192             ];
13193             
13194             var labelCfg = cfg.cn[1];
13195             var contentCfg = cfg.cn[2];
13196             
13197
13198             if(this.indicatorpos == 'right'){
13199                 
13200                 cfg.cn = [
13201                     {
13202                         tag: 'label',
13203                         'for' :  id,
13204                         cls : 'control-label',
13205                         cn : [
13206                             {
13207                                 tag : 'span',
13208                                 html : this.fieldLabel
13209                             },
13210                             {
13211                                 tag : 'i',
13212                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13213                                 tooltip : 'This field is required'
13214                             }
13215                         ]
13216                     },
13217                     {
13218                         cls : "",
13219                         cn: [
13220                             combobox
13221                         ]
13222                     }
13223
13224                 ];
13225                 
13226                 
13227                 
13228                 labelCfg = cfg.cn[0];
13229                 contentCfg = cfg.cn[1];
13230             
13231             }
13232             
13233             if(this.labelWidth > 12){
13234                 labelCfg.style = "width: " + this.labelWidth + 'px';
13235             }
13236             
13237             if(this.labelWidth < 13 && this.labelmd == 0){
13238                 this.labelmd = this.labelWidth;
13239             }
13240             
13241             if(this.labellg > 0){
13242                 labelCfg.cls += ' col-lg-' + this.labellg;
13243                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13244             }
13245             
13246             if(this.labelmd > 0){
13247                 labelCfg.cls += ' col-md-' + this.labelmd;
13248                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13249             }
13250             
13251             if(this.labelsm > 0){
13252                 labelCfg.cls += ' col-sm-' + this.labelsm;
13253                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13254             }
13255             
13256             if(this.labelxs > 0){
13257                 labelCfg.cls += ' col-xs-' + this.labelxs;
13258                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13259             }
13260                 
13261                 
13262         } else if ( this.fieldLabel.length) {
13263 //                Roo.log(" label");
13264                  cfg.cn = [
13265                     {
13266                         tag : 'i',
13267                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13268                         tooltip : 'This field is required'
13269                     },
13270                     {
13271                         tag: 'label',
13272                         //cls : 'input-group-addon',
13273                         html : this.fieldLabel
13274                     },
13275                     combobox
13276                 ];
13277                 
13278                 if(this.indicatorpos == 'right'){
13279                     cfg.cn = [
13280                         {
13281                             tag: 'label',
13282                             //cls : 'input-group-addon',
13283                             html : this.fieldLabel
13284                         },
13285                         {
13286                             tag : 'i',
13287                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13288                             tooltip : 'This field is required'
13289                         },
13290                         combobox
13291                     ];
13292                     
13293                 }
13294
13295         } else {
13296             
13297 //                Roo.log(" no label && no align");
13298                 cfg = combobox
13299                      
13300                 
13301         }
13302          
13303         var settings=this;
13304         ['xs','sm','md','lg'].map(function(size){
13305             if (settings[size]) {
13306                 cfg.cls += ' col-' + size + '-' + settings[size];
13307             }
13308         });
13309         
13310         return cfg;
13311         
13312     },
13313     
13314     _initEventsCalled : false,
13315     
13316     // private
13317     initEvents: function()
13318     {   
13319         if (this._initEventsCalled) { // as we call render... prevent looping...
13320             return;
13321         }
13322         this._initEventsCalled = true;
13323         
13324         if (!this.store) {
13325             throw "can not find store for combo";
13326         }
13327         
13328         this.indicator = this.indicatorEl();
13329         
13330         this.store = Roo.factory(this.store, Roo.data);
13331         this.store.parent = this;
13332         
13333         // if we are building from html. then this element is so complex, that we can not really
13334         // use the rendered HTML.
13335         // so we have to trash and replace the previous code.
13336         if (Roo.XComponent.build_from_html) {
13337             // remove this element....
13338             var e = this.el.dom, k=0;
13339             while (e ) { e = e.previousSibling;  ++k;}
13340
13341             this.el.remove();
13342             
13343             this.el=false;
13344             this.rendered = false;
13345             
13346             this.render(this.parent().getChildContainer(true), k);
13347         }
13348         
13349         if(Roo.isIOS && this.useNativeIOS){
13350             this.initIOSView();
13351             return;
13352         }
13353         
13354         /*
13355          * Touch Devices
13356          */
13357         
13358         if(Roo.isTouch && this.mobileTouchView){
13359             this.initTouchView();
13360             return;
13361         }
13362         
13363         if(this.tickable){
13364             this.initTickableEvents();
13365             return;
13366         }
13367         
13368         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13369         
13370         if(this.hiddenName){
13371             
13372             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13373             
13374             this.hiddenField.dom.value =
13375                 this.hiddenValue !== undefined ? this.hiddenValue :
13376                 this.value !== undefined ? this.value : '';
13377
13378             // prevent input submission
13379             this.el.dom.removeAttribute('name');
13380             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13381              
13382              
13383         }
13384         //if(Roo.isGecko){
13385         //    this.el.dom.setAttribute('autocomplete', 'off');
13386         //}
13387         
13388         var cls = 'x-combo-list';
13389         
13390         //this.list = new Roo.Layer({
13391         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13392         //});
13393         
13394         var _this = this;
13395         
13396         (function(){
13397             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13398             _this.list.setWidth(lw);
13399         }).defer(100);
13400         
13401         this.list.on('mouseover', this.onViewOver, this);
13402         this.list.on('mousemove', this.onViewMove, this);
13403         this.list.on('scroll', this.onViewScroll, this);
13404         
13405         /*
13406         this.list.swallowEvent('mousewheel');
13407         this.assetHeight = 0;
13408
13409         if(this.title){
13410             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13411             this.assetHeight += this.header.getHeight();
13412         }
13413
13414         this.innerList = this.list.createChild({cls:cls+'-inner'});
13415         this.innerList.on('mouseover', this.onViewOver, this);
13416         this.innerList.on('mousemove', this.onViewMove, this);
13417         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13418         
13419         if(this.allowBlank && !this.pageSize && !this.disableClear){
13420             this.footer = this.list.createChild({cls:cls+'-ft'});
13421             this.pageTb = new Roo.Toolbar(this.footer);
13422            
13423         }
13424         if(this.pageSize){
13425             this.footer = this.list.createChild({cls:cls+'-ft'});
13426             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13427                     {pageSize: this.pageSize});
13428             
13429         }
13430         
13431         if (this.pageTb && this.allowBlank && !this.disableClear) {
13432             var _this = this;
13433             this.pageTb.add(new Roo.Toolbar.Fill(), {
13434                 cls: 'x-btn-icon x-btn-clear',
13435                 text: '&#160;',
13436                 handler: function()
13437                 {
13438                     _this.collapse();
13439                     _this.clearValue();
13440                     _this.onSelect(false, -1);
13441                 }
13442             });
13443         }
13444         if (this.footer) {
13445             this.assetHeight += this.footer.getHeight();
13446         }
13447         */
13448             
13449         if(!this.tpl){
13450             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13451         }
13452
13453         this.view = new Roo.View(this.list, this.tpl, {
13454             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13455         });
13456         //this.view.wrapEl.setDisplayed(false);
13457         this.view.on('click', this.onViewClick, this);
13458         
13459         
13460         this.store.on('beforeload', this.onBeforeLoad, this);
13461         this.store.on('load', this.onLoad, this);
13462         this.store.on('loadexception', this.onLoadException, this);
13463         /*
13464         if(this.resizable){
13465             this.resizer = new Roo.Resizable(this.list,  {
13466                pinned:true, handles:'se'
13467             });
13468             this.resizer.on('resize', function(r, w, h){
13469                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13470                 this.listWidth = w;
13471                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13472                 this.restrictHeight();
13473             }, this);
13474             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13475         }
13476         */
13477         if(!this.editable){
13478             this.editable = true;
13479             this.setEditable(false);
13480         }
13481         
13482         /*
13483         
13484         if (typeof(this.events.add.listeners) != 'undefined') {
13485             
13486             this.addicon = this.wrap.createChild(
13487                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13488        
13489             this.addicon.on('click', function(e) {
13490                 this.fireEvent('add', this);
13491             }, this);
13492         }
13493         if (typeof(this.events.edit.listeners) != 'undefined') {
13494             
13495             this.editicon = this.wrap.createChild(
13496                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13497             if (this.addicon) {
13498                 this.editicon.setStyle('margin-left', '40px');
13499             }
13500             this.editicon.on('click', function(e) {
13501                 
13502                 // we fire even  if inothing is selected..
13503                 this.fireEvent('edit', this, this.lastData );
13504                 
13505             }, this);
13506         }
13507         */
13508         
13509         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13510             "up" : function(e){
13511                 this.inKeyMode = true;
13512                 this.selectPrev();
13513             },
13514
13515             "down" : function(e){
13516                 if(!this.isExpanded()){
13517                     this.onTriggerClick();
13518                 }else{
13519                     this.inKeyMode = true;
13520                     this.selectNext();
13521                 }
13522             },
13523
13524             "enter" : function(e){
13525 //                this.onViewClick();
13526                 //return true;
13527                 this.collapse();
13528                 
13529                 if(this.fireEvent("specialkey", this, e)){
13530                     this.onViewClick(false);
13531                 }
13532                 
13533                 return true;
13534             },
13535
13536             "esc" : function(e){
13537                 this.collapse();
13538             },
13539
13540             "tab" : function(e){
13541                 this.collapse();
13542                 
13543                 if(this.fireEvent("specialkey", this, e)){
13544                     this.onViewClick(false);
13545                 }
13546                 
13547                 return true;
13548             },
13549
13550             scope : this,
13551
13552             doRelay : function(foo, bar, hname){
13553                 if(hname == 'down' || this.scope.isExpanded()){
13554                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13555                 }
13556                 return true;
13557             },
13558
13559             forceKeyDown: true
13560         });
13561         
13562         
13563         this.queryDelay = Math.max(this.queryDelay || 10,
13564                 this.mode == 'local' ? 10 : 250);
13565         
13566         
13567         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13568         
13569         if(this.typeAhead){
13570             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13571         }
13572         if(this.editable !== false){
13573             this.inputEl().on("keyup", this.onKeyUp, this);
13574         }
13575         if(this.forceSelection){
13576             this.inputEl().on('blur', this.doForce, this);
13577         }
13578         
13579         if(this.multiple){
13580             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13581             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13582         }
13583     },
13584     
13585     initTickableEvents: function()
13586     {   
13587         this.createList();
13588         
13589         if(this.hiddenName){
13590             
13591             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13592             
13593             this.hiddenField.dom.value =
13594                 this.hiddenValue !== undefined ? this.hiddenValue :
13595                 this.value !== undefined ? this.value : '';
13596
13597             // prevent input submission
13598             this.el.dom.removeAttribute('name');
13599             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13600              
13601              
13602         }
13603         
13604 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13605         
13606         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13607         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13608         if(this.triggerList){
13609             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13610         }
13611          
13612         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13613         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13614         
13615         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13616         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13617         
13618         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13619         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13620         
13621         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13622         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13623         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13624         
13625         this.okBtn.hide();
13626         this.cancelBtn.hide();
13627         
13628         var _this = this;
13629         
13630         (function(){
13631             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13632             _this.list.setWidth(lw);
13633         }).defer(100);
13634         
13635         this.list.on('mouseover', this.onViewOver, this);
13636         this.list.on('mousemove', this.onViewMove, this);
13637         
13638         this.list.on('scroll', this.onViewScroll, this);
13639         
13640         if(!this.tpl){
13641             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13642                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13643         }
13644
13645         this.view = new Roo.View(this.list, this.tpl, {
13646             singleSelect:true,
13647             tickable:true,
13648             parent:this,
13649             store: this.store,
13650             selectedClass: this.selectedClass
13651         });
13652         
13653         //this.view.wrapEl.setDisplayed(false);
13654         this.view.on('click', this.onViewClick, this);
13655         
13656         
13657         
13658         this.store.on('beforeload', this.onBeforeLoad, this);
13659         this.store.on('load', this.onLoad, this);
13660         this.store.on('loadexception', this.onLoadException, this);
13661         
13662         if(this.editable){
13663             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13664                 "up" : function(e){
13665                     this.inKeyMode = true;
13666                     this.selectPrev();
13667                 },
13668
13669                 "down" : function(e){
13670                     this.inKeyMode = true;
13671                     this.selectNext();
13672                 },
13673
13674                 "enter" : function(e){
13675                     if(this.fireEvent("specialkey", this, e)){
13676                         this.onViewClick(false);
13677                     }
13678                     
13679                     return true;
13680                 },
13681
13682                 "esc" : function(e){
13683                     this.onTickableFooterButtonClick(e, false, false);
13684                 },
13685
13686                 "tab" : function(e){
13687                     this.fireEvent("specialkey", this, e);
13688                     
13689                     this.onTickableFooterButtonClick(e, false, false);
13690                     
13691                     return true;
13692                 },
13693
13694                 scope : this,
13695
13696                 doRelay : function(e, fn, key){
13697                     if(this.scope.isExpanded()){
13698                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13699                     }
13700                     return true;
13701                 },
13702
13703                 forceKeyDown: true
13704             });
13705         }
13706         
13707         this.queryDelay = Math.max(this.queryDelay || 10,
13708                 this.mode == 'local' ? 10 : 250);
13709         
13710         
13711         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13712         
13713         if(this.typeAhead){
13714             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13715         }
13716         
13717         if(this.editable !== false){
13718             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13719         }
13720         
13721         this.indicator = this.indicatorEl();
13722         
13723         if(this.indicator){
13724             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13725             this.indicator.hide();
13726         }
13727         
13728     },
13729
13730     onDestroy : function(){
13731         if(this.view){
13732             this.view.setStore(null);
13733             this.view.el.removeAllListeners();
13734             this.view.el.remove();
13735             this.view.purgeListeners();
13736         }
13737         if(this.list){
13738             this.list.dom.innerHTML  = '';
13739         }
13740         
13741         if(this.store){
13742             this.store.un('beforeload', this.onBeforeLoad, this);
13743             this.store.un('load', this.onLoad, this);
13744             this.store.un('loadexception', this.onLoadException, this);
13745         }
13746         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13747     },
13748
13749     // private
13750     fireKey : function(e){
13751         if(e.isNavKeyPress() && !this.list.isVisible()){
13752             this.fireEvent("specialkey", this, e);
13753         }
13754     },
13755
13756     // private
13757     onResize: function(w, h){
13758 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13759 //        
13760 //        if(typeof w != 'number'){
13761 //            // we do not handle it!?!?
13762 //            return;
13763 //        }
13764 //        var tw = this.trigger.getWidth();
13765 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13766 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13767 //        var x = w - tw;
13768 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13769 //            
13770 //        //this.trigger.setStyle('left', x+'px');
13771 //        
13772 //        if(this.list && this.listWidth === undefined){
13773 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13774 //            this.list.setWidth(lw);
13775 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13776 //        }
13777         
13778     
13779         
13780     },
13781
13782     /**
13783      * Allow or prevent the user from directly editing the field text.  If false is passed,
13784      * the user will only be able to select from the items defined in the dropdown list.  This method
13785      * is the runtime equivalent of setting the 'editable' config option at config time.
13786      * @param {Boolean} value True to allow the user to directly edit the field text
13787      */
13788     setEditable : function(value){
13789         if(value == this.editable){
13790             return;
13791         }
13792         this.editable = value;
13793         if(!value){
13794             this.inputEl().dom.setAttribute('readOnly', true);
13795             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13796             this.inputEl().addClass('x-combo-noedit');
13797         }else{
13798             this.inputEl().dom.setAttribute('readOnly', false);
13799             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13800             this.inputEl().removeClass('x-combo-noedit');
13801         }
13802     },
13803
13804     // private
13805     
13806     onBeforeLoad : function(combo,opts){
13807         if(!this.hasFocus){
13808             return;
13809         }
13810          if (!opts.add) {
13811             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13812          }
13813         this.restrictHeight();
13814         this.selectedIndex = -1;
13815     },
13816
13817     // private
13818     onLoad : function(){
13819         
13820         this.hasQuery = false;
13821         
13822         if(!this.hasFocus){
13823             return;
13824         }
13825         
13826         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13827             this.loading.hide();
13828         }
13829         
13830         if(this.store.getCount() > 0){
13831             
13832             this.expand();
13833             this.restrictHeight();
13834             if(this.lastQuery == this.allQuery){
13835                 if(this.editable && !this.tickable){
13836                     this.inputEl().dom.select();
13837                 }
13838                 
13839                 if(
13840                     !this.selectByValue(this.value, true) &&
13841                     this.autoFocus && 
13842                     (
13843                         !this.store.lastOptions ||
13844                         typeof(this.store.lastOptions.add) == 'undefined' || 
13845                         this.store.lastOptions.add != true
13846                     )
13847                 ){
13848                     this.select(0, true);
13849                 }
13850             }else{
13851                 if(this.autoFocus){
13852                     this.selectNext();
13853                 }
13854                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13855                     this.taTask.delay(this.typeAheadDelay);
13856                 }
13857             }
13858         }else{
13859             this.onEmptyResults();
13860         }
13861         
13862         //this.el.focus();
13863     },
13864     // private
13865     onLoadException : function()
13866     {
13867         this.hasQuery = false;
13868         
13869         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13870             this.loading.hide();
13871         }
13872         
13873         if(this.tickable && this.editable){
13874             return;
13875         }
13876         
13877         this.collapse();
13878         // only causes errors at present
13879         //Roo.log(this.store.reader.jsonData);
13880         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13881             // fixme
13882             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13883         //}
13884         
13885         
13886     },
13887     // private
13888     onTypeAhead : function(){
13889         if(this.store.getCount() > 0){
13890             var r = this.store.getAt(0);
13891             var newValue = r.data[this.displayField];
13892             var len = newValue.length;
13893             var selStart = this.getRawValue().length;
13894             
13895             if(selStart != len){
13896                 this.setRawValue(newValue);
13897                 this.selectText(selStart, newValue.length);
13898             }
13899         }
13900     },
13901
13902     // private
13903     onSelect : function(record, index){
13904         
13905         if(this.fireEvent('beforeselect', this, record, index) !== false){
13906         
13907             this.setFromData(index > -1 ? record.data : false);
13908             
13909             this.collapse();
13910             this.fireEvent('select', this, record, index);
13911         }
13912     },
13913
13914     /**
13915      * Returns the currently selected field value or empty string if no value is set.
13916      * @return {String} value The selected value
13917      */
13918     getValue : function()
13919     {
13920         if(Roo.isIOS && this.useNativeIOS){
13921             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13922         }
13923         
13924         if(this.multiple){
13925             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13926         }
13927         
13928         if(this.valueField){
13929             return typeof this.value != 'undefined' ? this.value : '';
13930         }else{
13931             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13932         }
13933     },
13934     
13935     getRawValue : function()
13936     {
13937         if(Roo.isIOS && this.useNativeIOS){
13938             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13939         }
13940         
13941         var v = this.inputEl().getValue();
13942         
13943         return v;
13944     },
13945
13946     /**
13947      * Clears any text/value currently set in the field
13948      */
13949     clearValue : function(){
13950         
13951         if(this.hiddenField){
13952             this.hiddenField.dom.value = '';
13953         }
13954         this.value = '';
13955         this.setRawValue('');
13956         this.lastSelectionText = '';
13957         this.lastData = false;
13958         
13959         var close = this.closeTriggerEl();
13960         
13961         if(close){
13962             close.hide();
13963         }
13964         
13965         this.validate();
13966         
13967     },
13968
13969     /**
13970      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13971      * will be displayed in the field.  If the value does not match the data value of an existing item,
13972      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13973      * Otherwise the field will be blank (although the value will still be set).
13974      * @param {String} value The value to match
13975      */
13976     setValue : function(v)
13977     {
13978         if(Roo.isIOS && this.useNativeIOS){
13979             this.setIOSValue(v);
13980             return;
13981         }
13982         
13983         if(this.multiple){
13984             this.syncValue();
13985             return;
13986         }
13987         
13988         var text = v;
13989         if(this.valueField){
13990             var r = this.findRecord(this.valueField, v);
13991             if(r){
13992                 text = r.data[this.displayField];
13993             }else if(this.valueNotFoundText !== undefined){
13994                 text = this.valueNotFoundText;
13995             }
13996         }
13997         this.lastSelectionText = text;
13998         if(this.hiddenField){
13999             this.hiddenField.dom.value = v;
14000         }
14001         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14002         this.value = v;
14003         
14004         var close = this.closeTriggerEl();
14005         
14006         if(close){
14007             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14008         }
14009         
14010         this.validate();
14011     },
14012     /**
14013      * @property {Object} the last set data for the element
14014      */
14015     
14016     lastData : false,
14017     /**
14018      * Sets the value of the field based on a object which is related to the record format for the store.
14019      * @param {Object} value the value to set as. or false on reset?
14020      */
14021     setFromData : function(o){
14022         
14023         if(this.multiple){
14024             this.addItem(o);
14025             return;
14026         }
14027             
14028         var dv = ''; // display value
14029         var vv = ''; // value value..
14030         this.lastData = o;
14031         if (this.displayField) {
14032             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14033         } else {
14034             // this is an error condition!!!
14035             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14036         }
14037         
14038         if(this.valueField){
14039             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14040         }
14041         
14042         var close = this.closeTriggerEl();
14043         
14044         if(close){
14045             if(dv.length || vv * 1 > 0){
14046                 close.show() ;
14047                 this.blockFocus=true;
14048             } else {
14049                 close.hide();
14050             }             
14051         }
14052         
14053         if(this.hiddenField){
14054             this.hiddenField.dom.value = vv;
14055             
14056             this.lastSelectionText = dv;
14057             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14058             this.value = vv;
14059             return;
14060         }
14061         // no hidden field.. - we store the value in 'value', but still display
14062         // display field!!!!
14063         this.lastSelectionText = dv;
14064         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14065         this.value = vv;
14066         
14067         
14068         
14069     },
14070     // private
14071     reset : function(){
14072         // overridden so that last data is reset..
14073         
14074         if(this.multiple){
14075             this.clearItem();
14076             return;
14077         }
14078         
14079         this.setValue(this.originalValue);
14080         //this.clearInvalid();
14081         this.lastData = false;
14082         if (this.view) {
14083             this.view.clearSelections();
14084         }
14085         
14086         this.validate();
14087     },
14088     // private
14089     findRecord : function(prop, value){
14090         var record;
14091         if(this.store.getCount() > 0){
14092             this.store.each(function(r){
14093                 if(r.data[prop] == value){
14094                     record = r;
14095                     return false;
14096                 }
14097                 return true;
14098             });
14099         }
14100         return record;
14101     },
14102     
14103     getName: function()
14104     {
14105         // returns hidden if it's set..
14106         if (!this.rendered) {return ''};
14107         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14108         
14109     },
14110     // private
14111     onViewMove : function(e, t){
14112         this.inKeyMode = false;
14113     },
14114
14115     // private
14116     onViewOver : function(e, t){
14117         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14118             return;
14119         }
14120         var item = this.view.findItemFromChild(t);
14121         
14122         if(item){
14123             var index = this.view.indexOf(item);
14124             this.select(index, false);
14125         }
14126     },
14127
14128     // private
14129     onViewClick : function(view, doFocus, el, e)
14130     {
14131         var index = this.view.getSelectedIndexes()[0];
14132         
14133         var r = this.store.getAt(index);
14134         
14135         if(this.tickable){
14136             
14137             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14138                 return;
14139             }
14140             
14141             var rm = false;
14142             var _this = this;
14143             
14144             Roo.each(this.tickItems, function(v,k){
14145                 
14146                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14147                     Roo.log(v);
14148                     _this.tickItems.splice(k, 1);
14149                     
14150                     if(typeof(e) == 'undefined' && view == false){
14151                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14152                     }
14153                     
14154                     rm = true;
14155                     return;
14156                 }
14157             });
14158             
14159             if(rm){
14160                 return;
14161             }
14162             
14163             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14164                 this.tickItems.push(r.data);
14165             }
14166             
14167             if(typeof(e) == 'undefined' && view == false){
14168                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14169             }
14170                     
14171             return;
14172         }
14173         
14174         if(r){
14175             this.onSelect(r, index);
14176         }
14177         if(doFocus !== false && !this.blockFocus){
14178             this.inputEl().focus();
14179         }
14180     },
14181
14182     // private
14183     restrictHeight : function(){
14184         //this.innerList.dom.style.height = '';
14185         //var inner = this.innerList.dom;
14186         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14187         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14188         //this.list.beginUpdate();
14189         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14190         this.list.alignTo(this.inputEl(), this.listAlign);
14191         this.list.alignTo(this.inputEl(), this.listAlign);
14192         //this.list.endUpdate();
14193     },
14194
14195     // private
14196     onEmptyResults : function(){
14197         
14198         if(this.tickable && this.editable){
14199             this.hasFocus = false;
14200             this.restrictHeight();
14201             return;
14202         }
14203         
14204         this.collapse();
14205     },
14206
14207     /**
14208      * Returns true if the dropdown list is expanded, else false.
14209      */
14210     isExpanded : function(){
14211         return this.list.isVisible();
14212     },
14213
14214     /**
14215      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14216      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14217      * @param {String} value The data value of the item to select
14218      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14219      * selected item if it is not currently in view (defaults to true)
14220      * @return {Boolean} True if the value matched an item in the list, else false
14221      */
14222     selectByValue : function(v, scrollIntoView){
14223         if(v !== undefined && v !== null){
14224             var r = this.findRecord(this.valueField || this.displayField, v);
14225             if(r){
14226                 this.select(this.store.indexOf(r), scrollIntoView);
14227                 return true;
14228             }
14229         }
14230         return false;
14231     },
14232
14233     /**
14234      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14235      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14236      * @param {Number} index The zero-based index of the list item to select
14237      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14238      * selected item if it is not currently in view (defaults to true)
14239      */
14240     select : function(index, scrollIntoView){
14241         this.selectedIndex = index;
14242         this.view.select(index);
14243         if(scrollIntoView !== false){
14244             var el = this.view.getNode(index);
14245             /*
14246              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14247              */
14248             if(el){
14249                 this.list.scrollChildIntoView(el, false);
14250             }
14251         }
14252     },
14253
14254     // private
14255     selectNext : function(){
14256         var ct = this.store.getCount();
14257         if(ct > 0){
14258             if(this.selectedIndex == -1){
14259                 this.select(0);
14260             }else if(this.selectedIndex < ct-1){
14261                 this.select(this.selectedIndex+1);
14262             }
14263         }
14264     },
14265
14266     // private
14267     selectPrev : function(){
14268         var ct = this.store.getCount();
14269         if(ct > 0){
14270             if(this.selectedIndex == -1){
14271                 this.select(0);
14272             }else if(this.selectedIndex != 0){
14273                 this.select(this.selectedIndex-1);
14274             }
14275         }
14276     },
14277
14278     // private
14279     onKeyUp : function(e){
14280         if(this.editable !== false && !e.isSpecialKey()){
14281             this.lastKey = e.getKey();
14282             this.dqTask.delay(this.queryDelay);
14283         }
14284     },
14285
14286     // private
14287     validateBlur : function(){
14288         return !this.list || !this.list.isVisible();   
14289     },
14290
14291     // private
14292     initQuery : function(){
14293         
14294         var v = this.getRawValue();
14295         
14296         if(this.tickable && this.editable){
14297             v = this.tickableInputEl().getValue();
14298         }
14299         
14300         this.doQuery(v);
14301     },
14302
14303     // private
14304     doForce : function(){
14305         if(this.inputEl().dom.value.length > 0){
14306             this.inputEl().dom.value =
14307                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14308              
14309         }
14310     },
14311
14312     /**
14313      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14314      * query allowing the query action to be canceled if needed.
14315      * @param {String} query The SQL query to execute
14316      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14317      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14318      * saved in the current store (defaults to false)
14319      */
14320     doQuery : function(q, forceAll){
14321         
14322         if(q === undefined || q === null){
14323             q = '';
14324         }
14325         var qe = {
14326             query: q,
14327             forceAll: forceAll,
14328             combo: this,
14329             cancel:false
14330         };
14331         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14332             return false;
14333         }
14334         q = qe.query;
14335         
14336         forceAll = qe.forceAll;
14337         if(forceAll === true || (q.length >= this.minChars)){
14338             
14339             this.hasQuery = true;
14340             
14341             if(this.lastQuery != q || this.alwaysQuery){
14342                 this.lastQuery = q;
14343                 if(this.mode == 'local'){
14344                     this.selectedIndex = -1;
14345                     if(forceAll){
14346                         this.store.clearFilter();
14347                     }else{
14348                         
14349                         if(this.specialFilter){
14350                             this.fireEvent('specialfilter', this);
14351                             this.onLoad();
14352                             return;
14353                         }
14354                         
14355                         this.store.filter(this.displayField, q);
14356                     }
14357                     
14358                     this.store.fireEvent("datachanged", this.store);
14359                     
14360                     this.onLoad();
14361                     
14362                     
14363                 }else{
14364                     
14365                     this.store.baseParams[this.queryParam] = q;
14366                     
14367                     var options = {params : this.getParams(q)};
14368                     
14369                     if(this.loadNext){
14370                         options.add = true;
14371                         options.params.start = this.page * this.pageSize;
14372                     }
14373                     
14374                     this.store.load(options);
14375                     
14376                     /*
14377                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14378                      *  we should expand the list on onLoad
14379                      *  so command out it
14380                      */
14381 //                    this.expand();
14382                 }
14383             }else{
14384                 this.selectedIndex = -1;
14385                 this.onLoad();   
14386             }
14387         }
14388         
14389         this.loadNext = false;
14390     },
14391     
14392     // private
14393     getParams : function(q){
14394         var p = {};
14395         //p[this.queryParam] = q;
14396         
14397         if(this.pageSize){
14398             p.start = 0;
14399             p.limit = this.pageSize;
14400         }
14401         return p;
14402     },
14403
14404     /**
14405      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14406      */
14407     collapse : function(){
14408         if(!this.isExpanded()){
14409             return;
14410         }
14411         
14412         this.list.hide();
14413         
14414         this.hasFocus = false;
14415         
14416         if(this.tickable){
14417             this.okBtn.hide();
14418             this.cancelBtn.hide();
14419             this.trigger.show();
14420             
14421             if(this.editable){
14422                 this.tickableInputEl().dom.value = '';
14423                 this.tickableInputEl().blur();
14424             }
14425             
14426         }
14427         
14428         Roo.get(document).un('mousedown', this.collapseIf, this);
14429         Roo.get(document).un('mousewheel', this.collapseIf, this);
14430         if (!this.editable) {
14431             Roo.get(document).un('keydown', this.listKeyPress, this);
14432         }
14433         this.fireEvent('collapse', this);
14434         
14435         this.validate();
14436     },
14437
14438     // private
14439     collapseIf : function(e){
14440         var in_combo  = e.within(this.el);
14441         var in_list =  e.within(this.list);
14442         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14443         
14444         if (in_combo || in_list || is_list) {
14445             //e.stopPropagation();
14446             return;
14447         }
14448         
14449         if(this.tickable){
14450             this.onTickableFooterButtonClick(e, false, false);
14451         }
14452
14453         this.collapse();
14454         
14455     },
14456
14457     /**
14458      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14459      */
14460     expand : function(){
14461        
14462         if(this.isExpanded() || !this.hasFocus){
14463             return;
14464         }
14465         
14466         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14467         this.list.setWidth(lw);
14468         
14469         Roo.log('expand');
14470         
14471         this.list.show();
14472         
14473         this.restrictHeight();
14474         
14475         if(this.tickable){
14476             
14477             this.tickItems = Roo.apply([], this.item);
14478             
14479             this.okBtn.show();
14480             this.cancelBtn.show();
14481             this.trigger.hide();
14482             
14483             if(this.editable){
14484                 this.tickableInputEl().focus();
14485             }
14486             
14487         }
14488         
14489         Roo.get(document).on('mousedown', this.collapseIf, this);
14490         Roo.get(document).on('mousewheel', this.collapseIf, this);
14491         if (!this.editable) {
14492             Roo.get(document).on('keydown', this.listKeyPress, this);
14493         }
14494         
14495         this.fireEvent('expand', this);
14496     },
14497
14498     // private
14499     // Implements the default empty TriggerField.onTriggerClick function
14500     onTriggerClick : function(e)
14501     {
14502         Roo.log('trigger click');
14503         
14504         if(this.disabled || !this.triggerList){
14505             return;
14506         }
14507         
14508         this.page = 0;
14509         this.loadNext = false;
14510         
14511         if(this.isExpanded()){
14512             this.collapse();
14513             if (!this.blockFocus) {
14514                 this.inputEl().focus();
14515             }
14516             
14517         }else {
14518             this.hasFocus = true;
14519             if(this.triggerAction == 'all') {
14520                 this.doQuery(this.allQuery, true);
14521             } else {
14522                 this.doQuery(this.getRawValue());
14523             }
14524             if (!this.blockFocus) {
14525                 this.inputEl().focus();
14526             }
14527         }
14528     },
14529     
14530     onTickableTriggerClick : function(e)
14531     {
14532         if(this.disabled){
14533             return;
14534         }
14535         
14536         this.page = 0;
14537         this.loadNext = false;
14538         this.hasFocus = true;
14539         
14540         if(this.triggerAction == 'all') {
14541             this.doQuery(this.allQuery, true);
14542         } else {
14543             this.doQuery(this.getRawValue());
14544         }
14545     },
14546     
14547     onSearchFieldClick : function(e)
14548     {
14549         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14550             this.onTickableFooterButtonClick(e, false, false);
14551             return;
14552         }
14553         
14554         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14555             return;
14556         }
14557         
14558         this.page = 0;
14559         this.loadNext = false;
14560         this.hasFocus = true;
14561         
14562         if(this.triggerAction == 'all') {
14563             this.doQuery(this.allQuery, true);
14564         } else {
14565             this.doQuery(this.getRawValue());
14566         }
14567     },
14568     
14569     listKeyPress : function(e)
14570     {
14571         //Roo.log('listkeypress');
14572         // scroll to first matching element based on key pres..
14573         if (e.isSpecialKey()) {
14574             return false;
14575         }
14576         var k = String.fromCharCode(e.getKey()).toUpperCase();
14577         //Roo.log(k);
14578         var match  = false;
14579         var csel = this.view.getSelectedNodes();
14580         var cselitem = false;
14581         if (csel.length) {
14582             var ix = this.view.indexOf(csel[0]);
14583             cselitem  = this.store.getAt(ix);
14584             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14585                 cselitem = false;
14586             }
14587             
14588         }
14589         
14590         this.store.each(function(v) { 
14591             if (cselitem) {
14592                 // start at existing selection.
14593                 if (cselitem.id == v.id) {
14594                     cselitem = false;
14595                 }
14596                 return true;
14597             }
14598                 
14599             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14600                 match = this.store.indexOf(v);
14601                 return false;
14602             }
14603             return true;
14604         }, this);
14605         
14606         if (match === false) {
14607             return true; // no more action?
14608         }
14609         // scroll to?
14610         this.view.select(match);
14611         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14612         sn.scrollIntoView(sn.dom.parentNode, false);
14613     },
14614     
14615     onViewScroll : function(e, t){
14616         
14617         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){
14618             return;
14619         }
14620         
14621         this.hasQuery = true;
14622         
14623         this.loading = this.list.select('.loading', true).first();
14624         
14625         if(this.loading === null){
14626             this.list.createChild({
14627                 tag: 'div',
14628                 cls: 'loading roo-select2-more-results roo-select2-active',
14629                 html: 'Loading more results...'
14630             });
14631             
14632             this.loading = this.list.select('.loading', true).first();
14633             
14634             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14635             
14636             this.loading.hide();
14637         }
14638         
14639         this.loading.show();
14640         
14641         var _combo = this;
14642         
14643         this.page++;
14644         this.loadNext = true;
14645         
14646         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14647         
14648         return;
14649     },
14650     
14651     addItem : function(o)
14652     {   
14653         var dv = ''; // display value
14654         
14655         if (this.displayField) {
14656             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14657         } else {
14658             // this is an error condition!!!
14659             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14660         }
14661         
14662         if(!dv.length){
14663             return;
14664         }
14665         
14666         var choice = this.choices.createChild({
14667             tag: 'li',
14668             cls: 'roo-select2-search-choice',
14669             cn: [
14670                 {
14671                     tag: 'div',
14672                     html: dv
14673                 },
14674                 {
14675                     tag: 'a',
14676                     href: '#',
14677                     cls: 'roo-select2-search-choice-close fa fa-times',
14678                     tabindex: '-1'
14679                 }
14680             ]
14681             
14682         }, this.searchField);
14683         
14684         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14685         
14686         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14687         
14688         this.item.push(o);
14689         
14690         this.lastData = o;
14691         
14692         this.syncValue();
14693         
14694         this.inputEl().dom.value = '';
14695         
14696         this.validate();
14697     },
14698     
14699     onRemoveItem : function(e, _self, o)
14700     {
14701         e.preventDefault();
14702         
14703         this.lastItem = Roo.apply([], this.item);
14704         
14705         var index = this.item.indexOf(o.data) * 1;
14706         
14707         if( index < 0){
14708             Roo.log('not this item?!');
14709             return;
14710         }
14711         
14712         this.item.splice(index, 1);
14713         o.item.remove();
14714         
14715         this.syncValue();
14716         
14717         this.fireEvent('remove', this, e);
14718         
14719         this.validate();
14720         
14721     },
14722     
14723     syncValue : function()
14724     {
14725         if(!this.item.length){
14726             this.clearValue();
14727             return;
14728         }
14729             
14730         var value = [];
14731         var _this = this;
14732         Roo.each(this.item, function(i){
14733             if(_this.valueField){
14734                 value.push(i[_this.valueField]);
14735                 return;
14736             }
14737
14738             value.push(i);
14739         });
14740
14741         this.value = value.join(',');
14742
14743         if(this.hiddenField){
14744             this.hiddenField.dom.value = this.value;
14745         }
14746         
14747         this.store.fireEvent("datachanged", this.store);
14748         
14749         this.validate();
14750     },
14751     
14752     clearItem : function()
14753     {
14754         if(!this.multiple){
14755             return;
14756         }
14757         
14758         this.item = [];
14759         
14760         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14761            c.remove();
14762         });
14763         
14764         this.syncValue();
14765         
14766         this.validate();
14767         
14768         if(this.tickable && !Roo.isTouch){
14769             this.view.refresh();
14770         }
14771     },
14772     
14773     inputEl: function ()
14774     {
14775         if(Roo.isIOS && this.useNativeIOS){
14776             return this.el.select('select.roo-ios-select', true).first();
14777         }
14778         
14779         if(Roo.isTouch && this.mobileTouchView){
14780             return this.el.select('input.form-control',true).first();
14781         }
14782         
14783         if(this.tickable){
14784             return this.searchField;
14785         }
14786         
14787         return this.el.select('input.form-control',true).first();
14788     },
14789     
14790     onTickableFooterButtonClick : function(e, btn, el)
14791     {
14792         e.preventDefault();
14793         
14794         this.lastItem = Roo.apply([], this.item);
14795         
14796         if(btn && btn.name == 'cancel'){
14797             this.tickItems = Roo.apply([], this.item);
14798             this.collapse();
14799             return;
14800         }
14801         
14802         this.clearItem();
14803         
14804         var _this = this;
14805         
14806         Roo.each(this.tickItems, function(o){
14807             _this.addItem(o);
14808         });
14809         
14810         this.collapse();
14811         
14812     },
14813     
14814     validate : function()
14815     {
14816         if(this.getVisibilityEl().hasClass('hidden')){
14817             return true;
14818         }
14819         
14820         var v = this.getRawValue();
14821         
14822         if(this.multiple){
14823             v = this.getValue();
14824         }
14825         
14826         if(this.disabled || this.allowBlank || v.length){
14827             this.markValid();
14828             return true;
14829         }
14830         
14831         this.markInvalid();
14832         return false;
14833     },
14834     
14835     tickableInputEl : function()
14836     {
14837         if(!this.tickable || !this.editable){
14838             return this.inputEl();
14839         }
14840         
14841         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14842     },
14843     
14844     
14845     getAutoCreateTouchView : function()
14846     {
14847         var id = Roo.id();
14848         
14849         var cfg = {
14850             cls: 'form-group' //input-group
14851         };
14852         
14853         var input =  {
14854             tag: 'input',
14855             id : id,
14856             type : this.inputType,
14857             cls : 'form-control x-combo-noedit',
14858             autocomplete: 'new-password',
14859             placeholder : this.placeholder || '',
14860             readonly : true
14861         };
14862         
14863         if (this.name) {
14864             input.name = this.name;
14865         }
14866         
14867         if (this.size) {
14868             input.cls += ' input-' + this.size;
14869         }
14870         
14871         if (this.disabled) {
14872             input.disabled = true;
14873         }
14874         
14875         var inputblock = {
14876             cls : '',
14877             cn : [
14878                 input
14879             ]
14880         };
14881         
14882         if(this.before){
14883             inputblock.cls += ' input-group';
14884             
14885             inputblock.cn.unshift({
14886                 tag :'span',
14887                 cls : 'input-group-addon',
14888                 html : this.before
14889             });
14890         }
14891         
14892         if(this.removable && !this.multiple){
14893             inputblock.cls += ' roo-removable';
14894             
14895             inputblock.cn.push({
14896                 tag: 'button',
14897                 html : 'x',
14898                 cls : 'roo-combo-removable-btn close'
14899             });
14900         }
14901
14902         if(this.hasFeedback && !this.allowBlank){
14903             
14904             inputblock.cls += ' has-feedback';
14905             
14906             inputblock.cn.push({
14907                 tag: 'span',
14908                 cls: 'glyphicon form-control-feedback'
14909             });
14910             
14911         }
14912         
14913         if (this.after) {
14914             
14915             inputblock.cls += (this.before) ? '' : ' input-group';
14916             
14917             inputblock.cn.push({
14918                 tag :'span',
14919                 cls : 'input-group-addon',
14920                 html : this.after
14921             });
14922         }
14923
14924         var box = {
14925             tag: 'div',
14926             cn: [
14927                 {
14928                     tag: 'input',
14929                     type : 'hidden',
14930                     cls: 'form-hidden-field'
14931                 },
14932                 inputblock
14933             ]
14934             
14935         };
14936         
14937         if(this.multiple){
14938             box = {
14939                 tag: 'div',
14940                 cn: [
14941                     {
14942                         tag: 'input',
14943                         type : 'hidden',
14944                         cls: 'form-hidden-field'
14945                     },
14946                     {
14947                         tag: 'ul',
14948                         cls: 'roo-select2-choices',
14949                         cn:[
14950                             {
14951                                 tag: 'li',
14952                                 cls: 'roo-select2-search-field',
14953                                 cn: [
14954
14955                                     inputblock
14956                                 ]
14957                             }
14958                         ]
14959                     }
14960                 ]
14961             }
14962         };
14963         
14964         var combobox = {
14965             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14966             cn: [
14967                 box
14968             ]
14969         };
14970         
14971         if(!this.multiple && this.showToggleBtn){
14972             
14973             var caret = {
14974                         tag: 'span',
14975                         cls: 'caret'
14976             };
14977             
14978             if (this.caret != false) {
14979                 caret = {
14980                      tag: 'i',
14981                      cls: 'fa fa-' + this.caret
14982                 };
14983                 
14984             }
14985             
14986             combobox.cn.push({
14987                 tag :'span',
14988                 cls : 'input-group-addon btn dropdown-toggle',
14989                 cn : [
14990                     caret,
14991                     {
14992                         tag: 'span',
14993                         cls: 'combobox-clear',
14994                         cn  : [
14995                             {
14996                                 tag : 'i',
14997                                 cls: 'icon-remove'
14998                             }
14999                         ]
15000                     }
15001                 ]
15002
15003             })
15004         }
15005         
15006         if(this.multiple){
15007             combobox.cls += ' roo-select2-container-multi';
15008         }
15009         
15010         var align = this.labelAlign || this.parentLabelAlign();
15011         
15012         if (align ==='left' && this.fieldLabel.length) {
15013
15014             cfg.cn = [
15015                 {
15016                    tag : 'i',
15017                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15018                    tooltip : 'This field is required'
15019                 },
15020                 {
15021                     tag: 'label',
15022                     cls : 'control-label',
15023                     html : this.fieldLabel
15024
15025                 },
15026                 {
15027                     cls : '', 
15028                     cn: [
15029                         combobox
15030                     ]
15031                 }
15032             ];
15033             
15034             var labelCfg = cfg.cn[1];
15035             var contentCfg = cfg.cn[2];
15036             
15037
15038             if(this.indicatorpos == 'right'){
15039                 cfg.cn = [
15040                     {
15041                         tag: 'label',
15042                         'for' :  id,
15043                         cls : 'control-label',
15044                         cn : [
15045                             {
15046                                 tag : 'span',
15047                                 html : this.fieldLabel
15048                             },
15049                             {
15050                                 tag : 'i',
15051                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15052                                 tooltip : 'This field is required'
15053                             }
15054                         ]
15055                     },
15056                     {
15057                         cls : "",
15058                         cn: [
15059                             combobox
15060                         ]
15061                     }
15062
15063                 ];
15064                 
15065                 labelCfg = cfg.cn[0];
15066                 contentCfg = cfg.cn[1];
15067             }
15068             
15069            
15070             
15071             if(this.labelWidth > 12){
15072                 labelCfg.style = "width: " + this.labelWidth + 'px';
15073             }
15074             
15075             if(this.labelWidth < 13 && this.labelmd == 0){
15076                 this.labelmd = this.labelWidth;
15077             }
15078             
15079             if(this.labellg > 0){
15080                 labelCfg.cls += ' col-lg-' + this.labellg;
15081                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15082             }
15083             
15084             if(this.labelmd > 0){
15085                 labelCfg.cls += ' col-md-' + this.labelmd;
15086                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15087             }
15088             
15089             if(this.labelsm > 0){
15090                 labelCfg.cls += ' col-sm-' + this.labelsm;
15091                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15092             }
15093             
15094             if(this.labelxs > 0){
15095                 labelCfg.cls += ' col-xs-' + this.labelxs;
15096                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15097             }
15098                 
15099                 
15100         } else if ( this.fieldLabel.length) {
15101             cfg.cn = [
15102                 {
15103                    tag : 'i',
15104                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15105                    tooltip : 'This field is required'
15106                 },
15107                 {
15108                     tag: 'label',
15109                     cls : 'control-label',
15110                     html : this.fieldLabel
15111
15112                 },
15113                 {
15114                     cls : '', 
15115                     cn: [
15116                         combobox
15117                     ]
15118                 }
15119             ];
15120             
15121             if(this.indicatorpos == 'right'){
15122                 cfg.cn = [
15123                     {
15124                         tag: 'label',
15125                         cls : 'control-label',
15126                         html : this.fieldLabel,
15127                         cn : [
15128                             {
15129                                tag : 'i',
15130                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15131                                tooltip : 'This field is required'
15132                             }
15133                         ]
15134                     },
15135                     {
15136                         cls : '', 
15137                         cn: [
15138                             combobox
15139                         ]
15140                     }
15141                 ];
15142             }
15143         } else {
15144             cfg.cn = combobox;    
15145         }
15146         
15147         
15148         var settings = this;
15149         
15150         ['xs','sm','md','lg'].map(function(size){
15151             if (settings[size]) {
15152                 cfg.cls += ' col-' + size + '-' + settings[size];
15153             }
15154         });
15155         
15156         return cfg;
15157     },
15158     
15159     initTouchView : function()
15160     {
15161         this.renderTouchView();
15162         
15163         this.touchViewEl.on('scroll', function(){
15164             this.el.dom.scrollTop = 0;
15165         }, this);
15166         
15167         this.originalValue = this.getValue();
15168         
15169         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15170         
15171         this.inputEl().on("click", this.showTouchView, this);
15172         if (this.triggerEl) {
15173             this.triggerEl.on("click", this.showTouchView, this);
15174         }
15175         
15176         
15177         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15178         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15179         
15180         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15181         
15182         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15183         this.store.on('load', this.onTouchViewLoad, this);
15184         this.store.on('loadexception', this.onTouchViewLoadException, this);
15185         
15186         if(this.hiddenName){
15187             
15188             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15189             
15190             this.hiddenField.dom.value =
15191                 this.hiddenValue !== undefined ? this.hiddenValue :
15192                 this.value !== undefined ? this.value : '';
15193         
15194             this.el.dom.removeAttribute('name');
15195             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15196         }
15197         
15198         if(this.multiple){
15199             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15200             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15201         }
15202         
15203         if(this.removable && !this.multiple){
15204             var close = this.closeTriggerEl();
15205             if(close){
15206                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15207                 close.on('click', this.removeBtnClick, this, close);
15208             }
15209         }
15210         /*
15211          * fix the bug in Safari iOS8
15212          */
15213         this.inputEl().on("focus", function(e){
15214             document.activeElement.blur();
15215         }, this);
15216         
15217         return;
15218         
15219         
15220     },
15221     
15222     renderTouchView : function()
15223     {
15224         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15225         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15226         
15227         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15228         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15229         
15230         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15231         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15232         this.touchViewBodyEl.setStyle('overflow', 'auto');
15233         
15234         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15235         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15236         
15237         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15238         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15239         
15240     },
15241     
15242     showTouchView : function()
15243     {
15244         if(this.disabled){
15245             return;
15246         }
15247         
15248         this.touchViewHeaderEl.hide();
15249
15250         if(this.modalTitle.length){
15251             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15252             this.touchViewHeaderEl.show();
15253         }
15254
15255         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15256         this.touchViewEl.show();
15257
15258         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15259         
15260         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15261         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15262
15263         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15264
15265         if(this.modalTitle.length){
15266             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15267         }
15268         
15269         this.touchViewBodyEl.setHeight(bodyHeight);
15270
15271         if(this.animate){
15272             var _this = this;
15273             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15274         }else{
15275             this.touchViewEl.addClass('in');
15276         }
15277
15278         this.doTouchViewQuery();
15279         
15280     },
15281     
15282     hideTouchView : function()
15283     {
15284         this.touchViewEl.removeClass('in');
15285
15286         if(this.animate){
15287             var _this = this;
15288             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15289         }else{
15290             this.touchViewEl.setStyle('display', 'none');
15291         }
15292         
15293     },
15294     
15295     setTouchViewValue : function()
15296     {
15297         if(this.multiple){
15298             this.clearItem();
15299         
15300             var _this = this;
15301
15302             Roo.each(this.tickItems, function(o){
15303                 this.addItem(o);
15304             }, this);
15305         }
15306         
15307         this.hideTouchView();
15308     },
15309     
15310     doTouchViewQuery : function()
15311     {
15312         var qe = {
15313             query: '',
15314             forceAll: true,
15315             combo: this,
15316             cancel:false
15317         };
15318         
15319         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15320             return false;
15321         }
15322         
15323         if(!this.alwaysQuery || this.mode == 'local'){
15324             this.onTouchViewLoad();
15325             return;
15326         }
15327         
15328         this.store.load();
15329     },
15330     
15331     onTouchViewBeforeLoad : function(combo,opts)
15332     {
15333         return;
15334     },
15335
15336     // private
15337     onTouchViewLoad : function()
15338     {
15339         if(this.store.getCount() < 1){
15340             this.onTouchViewEmptyResults();
15341             return;
15342         }
15343         
15344         this.clearTouchView();
15345         
15346         var rawValue = this.getRawValue();
15347         
15348         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15349         
15350         this.tickItems = [];
15351         
15352         this.store.data.each(function(d, rowIndex){
15353             var row = this.touchViewListGroup.createChild(template);
15354             
15355             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15356                 row.addClass(d.data.cls);
15357             }
15358             
15359             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15360                 var cfg = {
15361                     data : d.data,
15362                     html : d.data[this.displayField]
15363                 };
15364                 
15365                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15366                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15367                 }
15368             }
15369             row.removeClass('selected');
15370             if(!this.multiple && this.valueField &&
15371                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15372             {
15373                 // radio buttons..
15374                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15375                 row.addClass('selected');
15376             }
15377             
15378             if(this.multiple && this.valueField &&
15379                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15380             {
15381                 
15382                 // checkboxes...
15383                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15384                 this.tickItems.push(d.data);
15385             }
15386             
15387             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15388             
15389         }, this);
15390         
15391         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15392         
15393         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15394
15395         if(this.modalTitle.length){
15396             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15397         }
15398
15399         var listHeight = this.touchViewListGroup.getHeight();
15400         
15401         var _this = this;
15402         
15403         if(firstChecked && listHeight > bodyHeight){
15404             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15405         }
15406         
15407     },
15408     
15409     onTouchViewLoadException : function()
15410     {
15411         this.hideTouchView();
15412     },
15413     
15414     onTouchViewEmptyResults : function()
15415     {
15416         this.clearTouchView();
15417         
15418         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15419         
15420         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15421         
15422     },
15423     
15424     clearTouchView : function()
15425     {
15426         this.touchViewListGroup.dom.innerHTML = '';
15427     },
15428     
15429     onTouchViewClick : function(e, el, o)
15430     {
15431         e.preventDefault();
15432         
15433         var row = o.row;
15434         var rowIndex = o.rowIndex;
15435         
15436         var r = this.store.getAt(rowIndex);
15437         
15438         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15439             
15440             if(!this.multiple){
15441                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15442                     c.dom.removeAttribute('checked');
15443                 }, this);
15444
15445                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15446
15447                 this.setFromData(r.data);
15448
15449                 var close = this.closeTriggerEl();
15450
15451                 if(close){
15452                     close.show();
15453                 }
15454
15455                 this.hideTouchView();
15456
15457                 this.fireEvent('select', this, r, rowIndex);
15458
15459                 return;
15460             }
15461
15462             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15463                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15464                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15465                 return;
15466             }
15467
15468             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15469             this.addItem(r.data);
15470             this.tickItems.push(r.data);
15471         }
15472     },
15473     
15474     getAutoCreateNativeIOS : function()
15475     {
15476         var cfg = {
15477             cls: 'form-group' //input-group,
15478         };
15479         
15480         var combobox =  {
15481             tag: 'select',
15482             cls : 'roo-ios-select'
15483         };
15484         
15485         if (this.name) {
15486             combobox.name = this.name;
15487         }
15488         
15489         if (this.disabled) {
15490             combobox.disabled = true;
15491         }
15492         
15493         var settings = this;
15494         
15495         ['xs','sm','md','lg'].map(function(size){
15496             if (settings[size]) {
15497                 cfg.cls += ' col-' + size + '-' + settings[size];
15498             }
15499         });
15500         
15501         cfg.cn = combobox;
15502         
15503         return cfg;
15504         
15505     },
15506     
15507     initIOSView : function()
15508     {
15509         this.store.on('load', this.onIOSViewLoad, this);
15510         
15511         return;
15512     },
15513     
15514     onIOSViewLoad : function()
15515     {
15516         if(this.store.getCount() < 1){
15517             return;
15518         }
15519         
15520         this.clearIOSView();
15521         
15522         if(this.allowBlank) {
15523             
15524             var default_text = '-- SELECT --';
15525             
15526             if(this.placeholder.length){
15527                 default_text = this.placeholder;
15528             }
15529             
15530             if(this.emptyTitle.length){
15531                 default_text += ' - ' + this.emptyTitle + ' -';
15532             }
15533             
15534             var opt = this.inputEl().createChild({
15535                 tag: 'option',
15536                 value : 0,
15537                 html : default_text
15538             });
15539             
15540             var o = {};
15541             o[this.valueField] = 0;
15542             o[this.displayField] = default_text;
15543             
15544             this.ios_options.push({
15545                 data : o,
15546                 el : opt
15547             });
15548             
15549         }
15550         
15551         this.store.data.each(function(d, rowIndex){
15552             
15553             var html = '';
15554             
15555             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15556                 html = d.data[this.displayField];
15557             }
15558             
15559             var value = '';
15560             
15561             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15562                 value = d.data[this.valueField];
15563             }
15564             
15565             var option = {
15566                 tag: 'option',
15567                 value : value,
15568                 html : html
15569             };
15570             
15571             if(this.value == d.data[this.valueField]){
15572                 option['selected'] = true;
15573             }
15574             
15575             var opt = this.inputEl().createChild(option);
15576             
15577             this.ios_options.push({
15578                 data : d.data,
15579                 el : opt
15580             });
15581             
15582         }, this);
15583         
15584         this.inputEl().on('change', function(){
15585            this.fireEvent('select', this);
15586         }, this);
15587         
15588     },
15589     
15590     clearIOSView: function()
15591     {
15592         this.inputEl().dom.innerHTML = '';
15593         
15594         this.ios_options = [];
15595     },
15596     
15597     setIOSValue: function(v)
15598     {
15599         this.value = v;
15600         
15601         if(!this.ios_options){
15602             return;
15603         }
15604         
15605         Roo.each(this.ios_options, function(opts){
15606            
15607            opts.el.dom.removeAttribute('selected');
15608            
15609            if(opts.data[this.valueField] != v){
15610                return;
15611            }
15612            
15613            opts.el.dom.setAttribute('selected', true);
15614            
15615         }, this);
15616     }
15617
15618     /** 
15619     * @cfg {Boolean} grow 
15620     * @hide 
15621     */
15622     /** 
15623     * @cfg {Number} growMin 
15624     * @hide 
15625     */
15626     /** 
15627     * @cfg {Number} growMax 
15628     * @hide 
15629     */
15630     /**
15631      * @hide
15632      * @method autoSize
15633      */
15634 });
15635
15636 Roo.apply(Roo.bootstrap.ComboBox,  {
15637     
15638     header : {
15639         tag: 'div',
15640         cls: 'modal-header',
15641         cn: [
15642             {
15643                 tag: 'h4',
15644                 cls: 'modal-title'
15645             }
15646         ]
15647     },
15648     
15649     body : {
15650         tag: 'div',
15651         cls: 'modal-body',
15652         cn: [
15653             {
15654                 tag: 'ul',
15655                 cls: 'list-group'
15656             }
15657         ]
15658     },
15659     
15660     listItemRadio : {
15661         tag: 'li',
15662         cls: 'list-group-item',
15663         cn: [
15664             {
15665                 tag: 'span',
15666                 cls: 'roo-combobox-list-group-item-value'
15667             },
15668             {
15669                 tag: 'div',
15670                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15671                 cn: [
15672                     {
15673                         tag: 'input',
15674                         type: 'radio'
15675                     },
15676                     {
15677                         tag: 'label'
15678                     }
15679                 ]
15680             }
15681         ]
15682     },
15683     
15684     listItemCheckbox : {
15685         tag: 'li',
15686         cls: 'list-group-item',
15687         cn: [
15688             {
15689                 tag: 'span',
15690                 cls: 'roo-combobox-list-group-item-value'
15691             },
15692             {
15693                 tag: 'div',
15694                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15695                 cn: [
15696                     {
15697                         tag: 'input',
15698                         type: 'checkbox'
15699                     },
15700                     {
15701                         tag: 'label'
15702                     }
15703                 ]
15704             }
15705         ]
15706     },
15707     
15708     emptyResult : {
15709         tag: 'div',
15710         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15711     },
15712     
15713     footer : {
15714         tag: 'div',
15715         cls: 'modal-footer',
15716         cn: [
15717             {
15718                 tag: 'div',
15719                 cls: 'row',
15720                 cn: [
15721                     {
15722                         tag: 'div',
15723                         cls: 'col-xs-6 text-left',
15724                         cn: {
15725                             tag: 'button',
15726                             cls: 'btn btn-danger roo-touch-view-cancel',
15727                             html: 'Cancel'
15728                         }
15729                     },
15730                     {
15731                         tag: 'div',
15732                         cls: 'col-xs-6 text-right',
15733                         cn: {
15734                             tag: 'button',
15735                             cls: 'btn btn-success roo-touch-view-ok',
15736                             html: 'OK'
15737                         }
15738                     }
15739                 ]
15740             }
15741         ]
15742         
15743     }
15744 });
15745
15746 Roo.apply(Roo.bootstrap.ComboBox,  {
15747     
15748     touchViewTemplate : {
15749         tag: 'div',
15750         cls: 'modal fade roo-combobox-touch-view',
15751         cn: [
15752             {
15753                 tag: 'div',
15754                 cls: 'modal-dialog',
15755                 style : 'position:fixed', // we have to fix position....
15756                 cn: [
15757                     {
15758                         tag: 'div',
15759                         cls: 'modal-content',
15760                         cn: [
15761                             Roo.bootstrap.ComboBox.header,
15762                             Roo.bootstrap.ComboBox.body,
15763                             Roo.bootstrap.ComboBox.footer
15764                         ]
15765                     }
15766                 ]
15767             }
15768         ]
15769     }
15770 });/*
15771  * Based on:
15772  * Ext JS Library 1.1.1
15773  * Copyright(c) 2006-2007, Ext JS, LLC.
15774  *
15775  * Originally Released Under LGPL - original licence link has changed is not relivant.
15776  *
15777  * Fork - LGPL
15778  * <script type="text/javascript">
15779  */
15780
15781 /**
15782  * @class Roo.View
15783  * @extends Roo.util.Observable
15784  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15785  * This class also supports single and multi selection modes. <br>
15786  * Create a data model bound view:
15787  <pre><code>
15788  var store = new Roo.data.Store(...);
15789
15790  var view = new Roo.View({
15791     el : "my-element",
15792     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15793  
15794     singleSelect: true,
15795     selectedClass: "ydataview-selected",
15796     store: store
15797  });
15798
15799  // listen for node click?
15800  view.on("click", function(vw, index, node, e){
15801  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15802  });
15803
15804  // load XML data
15805  dataModel.load("foobar.xml");
15806  </code></pre>
15807  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15808  * <br><br>
15809  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15810  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15811  * 
15812  * Note: old style constructor is still suported (container, template, config)
15813  * 
15814  * @constructor
15815  * Create a new View
15816  * @param {Object} config The config object
15817  * 
15818  */
15819 Roo.View = function(config, depreciated_tpl, depreciated_config){
15820     
15821     this.parent = false;
15822     
15823     if (typeof(depreciated_tpl) == 'undefined') {
15824         // new way.. - universal constructor.
15825         Roo.apply(this, config);
15826         this.el  = Roo.get(this.el);
15827     } else {
15828         // old format..
15829         this.el  = Roo.get(config);
15830         this.tpl = depreciated_tpl;
15831         Roo.apply(this, depreciated_config);
15832     }
15833     this.wrapEl  = this.el.wrap().wrap();
15834     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15835     
15836     
15837     if(typeof(this.tpl) == "string"){
15838         this.tpl = new Roo.Template(this.tpl);
15839     } else {
15840         // support xtype ctors..
15841         this.tpl = new Roo.factory(this.tpl, Roo);
15842     }
15843     
15844     
15845     this.tpl.compile();
15846     
15847     /** @private */
15848     this.addEvents({
15849         /**
15850          * @event beforeclick
15851          * Fires before a click is processed. Returns false to cancel the default action.
15852          * @param {Roo.View} this
15853          * @param {Number} index The index of the target node
15854          * @param {HTMLElement} node The target node
15855          * @param {Roo.EventObject} e The raw event object
15856          */
15857             "beforeclick" : true,
15858         /**
15859          * @event click
15860          * Fires when a template node is clicked.
15861          * @param {Roo.View} this
15862          * @param {Number} index The index of the target node
15863          * @param {HTMLElement} node The target node
15864          * @param {Roo.EventObject} e The raw event object
15865          */
15866             "click" : true,
15867         /**
15868          * @event dblclick
15869          * Fires when a template node is double clicked.
15870          * @param {Roo.View} this
15871          * @param {Number} index The index of the target node
15872          * @param {HTMLElement} node The target node
15873          * @param {Roo.EventObject} e The raw event object
15874          */
15875             "dblclick" : true,
15876         /**
15877          * @event contextmenu
15878          * Fires when a template node is right clicked.
15879          * @param {Roo.View} this
15880          * @param {Number} index The index of the target node
15881          * @param {HTMLElement} node The target node
15882          * @param {Roo.EventObject} e The raw event object
15883          */
15884             "contextmenu" : true,
15885         /**
15886          * @event selectionchange
15887          * Fires when the selected nodes change.
15888          * @param {Roo.View} this
15889          * @param {Array} selections Array of the selected nodes
15890          */
15891             "selectionchange" : true,
15892     
15893         /**
15894          * @event beforeselect
15895          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15896          * @param {Roo.View} this
15897          * @param {HTMLElement} node The node to be selected
15898          * @param {Array} selections Array of currently selected nodes
15899          */
15900             "beforeselect" : true,
15901         /**
15902          * @event preparedata
15903          * Fires on every row to render, to allow you to change the data.
15904          * @param {Roo.View} this
15905          * @param {Object} data to be rendered (change this)
15906          */
15907           "preparedata" : true
15908           
15909           
15910         });
15911
15912
15913
15914     this.el.on({
15915         "click": this.onClick,
15916         "dblclick": this.onDblClick,
15917         "contextmenu": this.onContextMenu,
15918         scope:this
15919     });
15920
15921     this.selections = [];
15922     this.nodes = [];
15923     this.cmp = new Roo.CompositeElementLite([]);
15924     if(this.store){
15925         this.store = Roo.factory(this.store, Roo.data);
15926         this.setStore(this.store, true);
15927     }
15928     
15929     if ( this.footer && this.footer.xtype) {
15930            
15931          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15932         
15933         this.footer.dataSource = this.store;
15934         this.footer.container = fctr;
15935         this.footer = Roo.factory(this.footer, Roo);
15936         fctr.insertFirst(this.el);
15937         
15938         // this is a bit insane - as the paging toolbar seems to detach the el..
15939 //        dom.parentNode.parentNode.parentNode
15940          // they get detached?
15941     }
15942     
15943     
15944     Roo.View.superclass.constructor.call(this);
15945     
15946     
15947 };
15948
15949 Roo.extend(Roo.View, Roo.util.Observable, {
15950     
15951      /**
15952      * @cfg {Roo.data.Store} store Data store to load data from.
15953      */
15954     store : false,
15955     
15956     /**
15957      * @cfg {String|Roo.Element} el The container element.
15958      */
15959     el : '',
15960     
15961     /**
15962      * @cfg {String|Roo.Template} tpl The template used by this View 
15963      */
15964     tpl : false,
15965     /**
15966      * @cfg {String} dataName the named area of the template to use as the data area
15967      *                          Works with domtemplates roo-name="name"
15968      */
15969     dataName: false,
15970     /**
15971      * @cfg {String} selectedClass The css class to add to selected nodes
15972      */
15973     selectedClass : "x-view-selected",
15974      /**
15975      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15976      */
15977     emptyText : "",
15978     
15979     /**
15980      * @cfg {String} text to display on mask (default Loading)
15981      */
15982     mask : false,
15983     /**
15984      * @cfg {Boolean} multiSelect Allow multiple selection
15985      */
15986     multiSelect : false,
15987     /**
15988      * @cfg {Boolean} singleSelect Allow single selection
15989      */
15990     singleSelect:  false,
15991     
15992     /**
15993      * @cfg {Boolean} toggleSelect - selecting 
15994      */
15995     toggleSelect : false,
15996     
15997     /**
15998      * @cfg {Boolean} tickable - selecting 
15999      */
16000     tickable : false,
16001     
16002     /**
16003      * Returns the element this view is bound to.
16004      * @return {Roo.Element}
16005      */
16006     getEl : function(){
16007         return this.wrapEl;
16008     },
16009     
16010     
16011
16012     /**
16013      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16014      */
16015     refresh : function(){
16016         //Roo.log('refresh');
16017         var t = this.tpl;
16018         
16019         // if we are using something like 'domtemplate', then
16020         // the what gets used is:
16021         // t.applySubtemplate(NAME, data, wrapping data..)
16022         // the outer template then get' applied with
16023         //     the store 'extra data'
16024         // and the body get's added to the
16025         //      roo-name="data" node?
16026         //      <span class='roo-tpl-{name}'></span> ?????
16027         
16028         
16029         
16030         this.clearSelections();
16031         this.el.update("");
16032         var html = [];
16033         var records = this.store.getRange();
16034         if(records.length < 1) {
16035             
16036             // is this valid??  = should it render a template??
16037             
16038             this.el.update(this.emptyText);
16039             return;
16040         }
16041         var el = this.el;
16042         if (this.dataName) {
16043             this.el.update(t.apply(this.store.meta)); //????
16044             el = this.el.child('.roo-tpl-' + this.dataName);
16045         }
16046         
16047         for(var i = 0, len = records.length; i < len; i++){
16048             var data = this.prepareData(records[i].data, i, records[i]);
16049             this.fireEvent("preparedata", this, data, i, records[i]);
16050             
16051             var d = Roo.apply({}, data);
16052             
16053             if(this.tickable){
16054                 Roo.apply(d, {'roo-id' : Roo.id()});
16055                 
16056                 var _this = this;
16057             
16058                 Roo.each(this.parent.item, function(item){
16059                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16060                         return;
16061                     }
16062                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16063                 });
16064             }
16065             
16066             html[html.length] = Roo.util.Format.trim(
16067                 this.dataName ?
16068                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16069                     t.apply(d)
16070             );
16071         }
16072         
16073         
16074         
16075         el.update(html.join(""));
16076         this.nodes = el.dom.childNodes;
16077         this.updateIndexes(0);
16078     },
16079     
16080
16081     /**
16082      * Function to override to reformat the data that is sent to
16083      * the template for each node.
16084      * DEPRICATED - use the preparedata event handler.
16085      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16086      * a JSON object for an UpdateManager bound view).
16087      */
16088     prepareData : function(data, index, record)
16089     {
16090         this.fireEvent("preparedata", this, data, index, record);
16091         return data;
16092     },
16093
16094     onUpdate : function(ds, record){
16095         // Roo.log('on update');   
16096         this.clearSelections();
16097         var index = this.store.indexOf(record);
16098         var n = this.nodes[index];
16099         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16100         n.parentNode.removeChild(n);
16101         this.updateIndexes(index, index);
16102     },
16103
16104     
16105     
16106 // --------- FIXME     
16107     onAdd : function(ds, records, index)
16108     {
16109         //Roo.log(['on Add', ds, records, index] );        
16110         this.clearSelections();
16111         if(this.nodes.length == 0){
16112             this.refresh();
16113             return;
16114         }
16115         var n = this.nodes[index];
16116         for(var i = 0, len = records.length; i < len; i++){
16117             var d = this.prepareData(records[i].data, i, records[i]);
16118             if(n){
16119                 this.tpl.insertBefore(n, d);
16120             }else{
16121                 
16122                 this.tpl.append(this.el, d);
16123             }
16124         }
16125         this.updateIndexes(index);
16126     },
16127
16128     onRemove : function(ds, record, index){
16129        // Roo.log('onRemove');
16130         this.clearSelections();
16131         var el = this.dataName  ?
16132             this.el.child('.roo-tpl-' + this.dataName) :
16133             this.el; 
16134         
16135         el.dom.removeChild(this.nodes[index]);
16136         this.updateIndexes(index);
16137     },
16138
16139     /**
16140      * Refresh an individual node.
16141      * @param {Number} index
16142      */
16143     refreshNode : function(index){
16144         this.onUpdate(this.store, this.store.getAt(index));
16145     },
16146
16147     updateIndexes : function(startIndex, endIndex){
16148         var ns = this.nodes;
16149         startIndex = startIndex || 0;
16150         endIndex = endIndex || ns.length - 1;
16151         for(var i = startIndex; i <= endIndex; i++){
16152             ns[i].nodeIndex = i;
16153         }
16154     },
16155
16156     /**
16157      * Changes the data store this view uses and refresh the view.
16158      * @param {Store} store
16159      */
16160     setStore : function(store, initial){
16161         if(!initial && this.store){
16162             this.store.un("datachanged", this.refresh);
16163             this.store.un("add", this.onAdd);
16164             this.store.un("remove", this.onRemove);
16165             this.store.un("update", this.onUpdate);
16166             this.store.un("clear", this.refresh);
16167             this.store.un("beforeload", this.onBeforeLoad);
16168             this.store.un("load", this.onLoad);
16169             this.store.un("loadexception", this.onLoad);
16170         }
16171         if(store){
16172           
16173             store.on("datachanged", this.refresh, this);
16174             store.on("add", this.onAdd, this);
16175             store.on("remove", this.onRemove, this);
16176             store.on("update", this.onUpdate, this);
16177             store.on("clear", this.refresh, this);
16178             store.on("beforeload", this.onBeforeLoad, this);
16179             store.on("load", this.onLoad, this);
16180             store.on("loadexception", this.onLoad, this);
16181         }
16182         
16183         if(store){
16184             this.refresh();
16185         }
16186     },
16187     /**
16188      * onbeforeLoad - masks the loading area.
16189      *
16190      */
16191     onBeforeLoad : function(store,opts)
16192     {
16193          //Roo.log('onBeforeLoad');   
16194         if (!opts.add) {
16195             this.el.update("");
16196         }
16197         this.el.mask(this.mask ? this.mask : "Loading" ); 
16198     },
16199     onLoad : function ()
16200     {
16201         this.el.unmask();
16202     },
16203     
16204
16205     /**
16206      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16207      * @param {HTMLElement} node
16208      * @return {HTMLElement} The template node
16209      */
16210     findItemFromChild : function(node){
16211         var el = this.dataName  ?
16212             this.el.child('.roo-tpl-' + this.dataName,true) :
16213             this.el.dom; 
16214         
16215         if(!node || node.parentNode == el){
16216                     return node;
16217             }
16218             var p = node.parentNode;
16219             while(p && p != el){
16220             if(p.parentNode == el){
16221                 return p;
16222             }
16223             p = p.parentNode;
16224         }
16225             return null;
16226     },
16227
16228     /** @ignore */
16229     onClick : function(e){
16230         var item = this.findItemFromChild(e.getTarget());
16231         if(item){
16232             var index = this.indexOf(item);
16233             if(this.onItemClick(item, index, e) !== false){
16234                 this.fireEvent("click", this, index, item, e);
16235             }
16236         }else{
16237             this.clearSelections();
16238         }
16239     },
16240
16241     /** @ignore */
16242     onContextMenu : function(e){
16243         var item = this.findItemFromChild(e.getTarget());
16244         if(item){
16245             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16246         }
16247     },
16248
16249     /** @ignore */
16250     onDblClick : function(e){
16251         var item = this.findItemFromChild(e.getTarget());
16252         if(item){
16253             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16254         }
16255     },
16256
16257     onItemClick : function(item, index, e)
16258     {
16259         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16260             return false;
16261         }
16262         if (this.toggleSelect) {
16263             var m = this.isSelected(item) ? 'unselect' : 'select';
16264             //Roo.log(m);
16265             var _t = this;
16266             _t[m](item, true, false);
16267             return true;
16268         }
16269         if(this.multiSelect || this.singleSelect){
16270             if(this.multiSelect && e.shiftKey && this.lastSelection){
16271                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16272             }else{
16273                 this.select(item, this.multiSelect && e.ctrlKey);
16274                 this.lastSelection = item;
16275             }
16276             
16277             if(!this.tickable){
16278                 e.preventDefault();
16279             }
16280             
16281         }
16282         return true;
16283     },
16284
16285     /**
16286      * Get the number of selected nodes.
16287      * @return {Number}
16288      */
16289     getSelectionCount : function(){
16290         return this.selections.length;
16291     },
16292
16293     /**
16294      * Get the currently selected nodes.
16295      * @return {Array} An array of HTMLElements
16296      */
16297     getSelectedNodes : function(){
16298         return this.selections;
16299     },
16300
16301     /**
16302      * Get the indexes of the selected nodes.
16303      * @return {Array}
16304      */
16305     getSelectedIndexes : function(){
16306         var indexes = [], s = this.selections;
16307         for(var i = 0, len = s.length; i < len; i++){
16308             indexes.push(s[i].nodeIndex);
16309         }
16310         return indexes;
16311     },
16312
16313     /**
16314      * Clear all selections
16315      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16316      */
16317     clearSelections : function(suppressEvent){
16318         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16319             this.cmp.elements = this.selections;
16320             this.cmp.removeClass(this.selectedClass);
16321             this.selections = [];
16322             if(!suppressEvent){
16323                 this.fireEvent("selectionchange", this, this.selections);
16324             }
16325         }
16326     },
16327
16328     /**
16329      * Returns true if the passed node is selected
16330      * @param {HTMLElement/Number} node The node or node index
16331      * @return {Boolean}
16332      */
16333     isSelected : function(node){
16334         var s = this.selections;
16335         if(s.length < 1){
16336             return false;
16337         }
16338         node = this.getNode(node);
16339         return s.indexOf(node) !== -1;
16340     },
16341
16342     /**
16343      * Selects nodes.
16344      * @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
16345      * @param {Boolean} keepExisting (optional) true to keep existing selections
16346      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16347      */
16348     select : function(nodeInfo, keepExisting, suppressEvent){
16349         if(nodeInfo instanceof Array){
16350             if(!keepExisting){
16351                 this.clearSelections(true);
16352             }
16353             for(var i = 0, len = nodeInfo.length; i < len; i++){
16354                 this.select(nodeInfo[i], true, true);
16355             }
16356             return;
16357         } 
16358         var node = this.getNode(nodeInfo);
16359         if(!node || this.isSelected(node)){
16360             return; // already selected.
16361         }
16362         if(!keepExisting){
16363             this.clearSelections(true);
16364         }
16365         
16366         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16367             Roo.fly(node).addClass(this.selectedClass);
16368             this.selections.push(node);
16369             if(!suppressEvent){
16370                 this.fireEvent("selectionchange", this, this.selections);
16371             }
16372         }
16373         
16374         
16375     },
16376       /**
16377      * Unselects nodes.
16378      * @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
16379      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16380      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16381      */
16382     unselect : function(nodeInfo, keepExisting, suppressEvent)
16383     {
16384         if(nodeInfo instanceof Array){
16385             Roo.each(this.selections, function(s) {
16386                 this.unselect(s, nodeInfo);
16387             }, this);
16388             return;
16389         }
16390         var node = this.getNode(nodeInfo);
16391         if(!node || !this.isSelected(node)){
16392             //Roo.log("not selected");
16393             return; // not selected.
16394         }
16395         // fireevent???
16396         var ns = [];
16397         Roo.each(this.selections, function(s) {
16398             if (s == node ) {
16399                 Roo.fly(node).removeClass(this.selectedClass);
16400
16401                 return;
16402             }
16403             ns.push(s);
16404         },this);
16405         
16406         this.selections= ns;
16407         this.fireEvent("selectionchange", this, this.selections);
16408     },
16409
16410     /**
16411      * Gets a template node.
16412      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16413      * @return {HTMLElement} The node or null if it wasn't found
16414      */
16415     getNode : function(nodeInfo){
16416         if(typeof nodeInfo == "string"){
16417             return document.getElementById(nodeInfo);
16418         }else if(typeof nodeInfo == "number"){
16419             return this.nodes[nodeInfo];
16420         }
16421         return nodeInfo;
16422     },
16423
16424     /**
16425      * Gets a range template nodes.
16426      * @param {Number} startIndex
16427      * @param {Number} endIndex
16428      * @return {Array} An array of nodes
16429      */
16430     getNodes : function(start, end){
16431         var ns = this.nodes;
16432         start = start || 0;
16433         end = typeof end == "undefined" ? ns.length - 1 : end;
16434         var nodes = [];
16435         if(start <= end){
16436             for(var i = start; i <= end; i++){
16437                 nodes.push(ns[i]);
16438             }
16439         } else{
16440             for(var i = start; i >= end; i--){
16441                 nodes.push(ns[i]);
16442             }
16443         }
16444         return nodes;
16445     },
16446
16447     /**
16448      * Finds the index of the passed node
16449      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16450      * @return {Number} The index of the node or -1
16451      */
16452     indexOf : function(node){
16453         node = this.getNode(node);
16454         if(typeof node.nodeIndex == "number"){
16455             return node.nodeIndex;
16456         }
16457         var ns = this.nodes;
16458         for(var i = 0, len = ns.length; i < len; i++){
16459             if(ns[i] == node){
16460                 return i;
16461             }
16462         }
16463         return -1;
16464     }
16465 });
16466 /*
16467  * - LGPL
16468  *
16469  * based on jquery fullcalendar
16470  * 
16471  */
16472
16473 Roo.bootstrap = Roo.bootstrap || {};
16474 /**
16475  * @class Roo.bootstrap.Calendar
16476  * @extends Roo.bootstrap.Component
16477  * Bootstrap Calendar class
16478  * @cfg {Boolean} loadMask (true|false) default false
16479  * @cfg {Object} header generate the user specific header of the calendar, default false
16480
16481  * @constructor
16482  * Create a new Container
16483  * @param {Object} config The config object
16484  */
16485
16486
16487
16488 Roo.bootstrap.Calendar = function(config){
16489     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16490      this.addEvents({
16491         /**
16492              * @event select
16493              * Fires when a date is selected
16494              * @param {DatePicker} this
16495              * @param {Date} date The selected date
16496              */
16497         'select': true,
16498         /**
16499              * @event monthchange
16500              * Fires when the displayed month changes 
16501              * @param {DatePicker} this
16502              * @param {Date} date The selected month
16503              */
16504         'monthchange': true,
16505         /**
16506              * @event evententer
16507              * Fires when mouse over an event
16508              * @param {Calendar} this
16509              * @param {event} Event
16510              */
16511         'evententer': true,
16512         /**
16513              * @event eventleave
16514              * Fires when the mouse leaves an
16515              * @param {Calendar} this
16516              * @param {event}
16517              */
16518         'eventleave': true,
16519         /**
16520              * @event eventclick
16521              * Fires when the mouse click an
16522              * @param {Calendar} this
16523              * @param {event}
16524              */
16525         'eventclick': true
16526         
16527     });
16528
16529 };
16530
16531 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16532     
16533      /**
16534      * @cfg {Number} startDay
16535      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16536      */
16537     startDay : 0,
16538     
16539     loadMask : false,
16540     
16541     header : false,
16542       
16543     getAutoCreate : function(){
16544         
16545         
16546         var fc_button = function(name, corner, style, content ) {
16547             return Roo.apply({},{
16548                 tag : 'span',
16549                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16550                          (corner.length ?
16551                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16552                             ''
16553                         ),
16554                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16555                 unselectable: 'on'
16556             });
16557         };
16558         
16559         var header = {};
16560         
16561         if(!this.header){
16562             header = {
16563                 tag : 'table',
16564                 cls : 'fc-header',
16565                 style : 'width:100%',
16566                 cn : [
16567                     {
16568                         tag: 'tr',
16569                         cn : [
16570                             {
16571                                 tag : 'td',
16572                                 cls : 'fc-header-left',
16573                                 cn : [
16574                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16575                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16576                                     { tag: 'span', cls: 'fc-header-space' },
16577                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16578
16579
16580                                 ]
16581                             },
16582
16583                             {
16584                                 tag : 'td',
16585                                 cls : 'fc-header-center',
16586                                 cn : [
16587                                     {
16588                                         tag: 'span',
16589                                         cls: 'fc-header-title',
16590                                         cn : {
16591                                             tag: 'H2',
16592                                             html : 'month / year'
16593                                         }
16594                                     }
16595
16596                                 ]
16597                             },
16598                             {
16599                                 tag : 'td',
16600                                 cls : 'fc-header-right',
16601                                 cn : [
16602                               /*      fc_button('month', 'left', '', 'month' ),
16603                                     fc_button('week', '', '', 'week' ),
16604                                     fc_button('day', 'right', '', 'day' )
16605                                 */    
16606
16607                                 ]
16608                             }
16609
16610                         ]
16611                     }
16612                 ]
16613             };
16614         }
16615         
16616         header = this.header;
16617         
16618        
16619         var cal_heads = function() {
16620             var ret = [];
16621             // fixme - handle this.
16622             
16623             for (var i =0; i < Date.dayNames.length; i++) {
16624                 var d = Date.dayNames[i];
16625                 ret.push({
16626                     tag: 'th',
16627                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16628                     html : d.substring(0,3)
16629                 });
16630                 
16631             }
16632             ret[0].cls += ' fc-first';
16633             ret[6].cls += ' fc-last';
16634             return ret;
16635         };
16636         var cal_cell = function(n) {
16637             return  {
16638                 tag: 'td',
16639                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16640                 cn : [
16641                     {
16642                         cn : [
16643                             {
16644                                 cls: 'fc-day-number',
16645                                 html: 'D'
16646                             },
16647                             {
16648                                 cls: 'fc-day-content',
16649                              
16650                                 cn : [
16651                                      {
16652                                         style: 'position: relative;' // height: 17px;
16653                                     }
16654                                 ]
16655                             }
16656                             
16657                             
16658                         ]
16659                     }
16660                 ]
16661                 
16662             }
16663         };
16664         var cal_rows = function() {
16665             
16666             var ret = [];
16667             for (var r = 0; r < 6; r++) {
16668                 var row= {
16669                     tag : 'tr',
16670                     cls : 'fc-week',
16671                     cn : []
16672                 };
16673                 
16674                 for (var i =0; i < Date.dayNames.length; i++) {
16675                     var d = Date.dayNames[i];
16676                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16677
16678                 }
16679                 row.cn[0].cls+=' fc-first';
16680                 row.cn[0].cn[0].style = 'min-height:90px';
16681                 row.cn[6].cls+=' fc-last';
16682                 ret.push(row);
16683                 
16684             }
16685             ret[0].cls += ' fc-first';
16686             ret[4].cls += ' fc-prev-last';
16687             ret[5].cls += ' fc-last';
16688             return ret;
16689             
16690         };
16691         
16692         var cal_table = {
16693             tag: 'table',
16694             cls: 'fc-border-separate',
16695             style : 'width:100%',
16696             cellspacing  : 0,
16697             cn : [
16698                 { 
16699                     tag: 'thead',
16700                     cn : [
16701                         { 
16702                             tag: 'tr',
16703                             cls : 'fc-first fc-last',
16704                             cn : cal_heads()
16705                         }
16706                     ]
16707                 },
16708                 { 
16709                     tag: 'tbody',
16710                     cn : cal_rows()
16711                 }
16712                   
16713             ]
16714         };
16715          
16716          var cfg = {
16717             cls : 'fc fc-ltr',
16718             cn : [
16719                 header,
16720                 {
16721                     cls : 'fc-content',
16722                     style : "position: relative;",
16723                     cn : [
16724                         {
16725                             cls : 'fc-view fc-view-month fc-grid',
16726                             style : 'position: relative',
16727                             unselectable : 'on',
16728                             cn : [
16729                                 {
16730                                     cls : 'fc-event-container',
16731                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16732                                 },
16733                                 cal_table
16734                             ]
16735                         }
16736                     ]
16737     
16738                 }
16739            ] 
16740             
16741         };
16742         
16743          
16744         
16745         return cfg;
16746     },
16747     
16748     
16749     initEvents : function()
16750     {
16751         if(!this.store){
16752             throw "can not find store for calendar";
16753         }
16754         
16755         var mark = {
16756             tag: "div",
16757             cls:"x-dlg-mask",
16758             style: "text-align:center",
16759             cn: [
16760                 {
16761                     tag: "div",
16762                     style: "background-color:white;width:50%;margin:250 auto",
16763                     cn: [
16764                         {
16765                             tag: "img",
16766                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16767                         },
16768                         {
16769                             tag: "span",
16770                             html: "Loading"
16771                         }
16772                         
16773                     ]
16774                 }
16775             ]
16776         };
16777         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16778         
16779         var size = this.el.select('.fc-content', true).first().getSize();
16780         this.maskEl.setSize(size.width, size.height);
16781         this.maskEl.enableDisplayMode("block");
16782         if(!this.loadMask){
16783             this.maskEl.hide();
16784         }
16785         
16786         this.store = Roo.factory(this.store, Roo.data);
16787         this.store.on('load', this.onLoad, this);
16788         this.store.on('beforeload', this.onBeforeLoad, this);
16789         
16790         this.resize();
16791         
16792         this.cells = this.el.select('.fc-day',true);
16793         //Roo.log(this.cells);
16794         this.textNodes = this.el.query('.fc-day-number');
16795         this.cells.addClassOnOver('fc-state-hover');
16796         
16797         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16798         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16799         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16800         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16801         
16802         this.on('monthchange', this.onMonthChange, this);
16803         
16804         this.update(new Date().clearTime());
16805     },
16806     
16807     resize : function() {
16808         var sz  = this.el.getSize();
16809         
16810         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16811         this.el.select('.fc-day-content div',true).setHeight(34);
16812     },
16813     
16814     
16815     // private
16816     showPrevMonth : function(e){
16817         this.update(this.activeDate.add("mo", -1));
16818     },
16819     showToday : function(e){
16820         this.update(new Date().clearTime());
16821     },
16822     // private
16823     showNextMonth : function(e){
16824         this.update(this.activeDate.add("mo", 1));
16825     },
16826
16827     // private
16828     showPrevYear : function(){
16829         this.update(this.activeDate.add("y", -1));
16830     },
16831
16832     // private
16833     showNextYear : function(){
16834         this.update(this.activeDate.add("y", 1));
16835     },
16836
16837     
16838    // private
16839     update : function(date)
16840     {
16841         var vd = this.activeDate;
16842         this.activeDate = date;
16843 //        if(vd && this.el){
16844 //            var t = date.getTime();
16845 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16846 //                Roo.log('using add remove');
16847 //                
16848 //                this.fireEvent('monthchange', this, date);
16849 //                
16850 //                this.cells.removeClass("fc-state-highlight");
16851 //                this.cells.each(function(c){
16852 //                   if(c.dateValue == t){
16853 //                       c.addClass("fc-state-highlight");
16854 //                       setTimeout(function(){
16855 //                            try{c.dom.firstChild.focus();}catch(e){}
16856 //                       }, 50);
16857 //                       return false;
16858 //                   }
16859 //                   return true;
16860 //                });
16861 //                return;
16862 //            }
16863 //        }
16864         
16865         var days = date.getDaysInMonth();
16866         
16867         var firstOfMonth = date.getFirstDateOfMonth();
16868         var startingPos = firstOfMonth.getDay()-this.startDay;
16869         
16870         if(startingPos < this.startDay){
16871             startingPos += 7;
16872         }
16873         
16874         var pm = date.add(Date.MONTH, -1);
16875         var prevStart = pm.getDaysInMonth()-startingPos;
16876 //        
16877         this.cells = this.el.select('.fc-day',true);
16878         this.textNodes = this.el.query('.fc-day-number');
16879         this.cells.addClassOnOver('fc-state-hover');
16880         
16881         var cells = this.cells.elements;
16882         var textEls = this.textNodes;
16883         
16884         Roo.each(cells, function(cell){
16885             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16886         });
16887         
16888         days += startingPos;
16889
16890         // convert everything to numbers so it's fast
16891         var day = 86400000;
16892         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16893         //Roo.log(d);
16894         //Roo.log(pm);
16895         //Roo.log(prevStart);
16896         
16897         var today = new Date().clearTime().getTime();
16898         var sel = date.clearTime().getTime();
16899         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16900         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16901         var ddMatch = this.disabledDatesRE;
16902         var ddText = this.disabledDatesText;
16903         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16904         var ddaysText = this.disabledDaysText;
16905         var format = this.format;
16906         
16907         var setCellClass = function(cal, cell){
16908             cell.row = 0;
16909             cell.events = [];
16910             cell.more = [];
16911             //Roo.log('set Cell Class');
16912             cell.title = "";
16913             var t = d.getTime();
16914             
16915             //Roo.log(d);
16916             
16917             cell.dateValue = t;
16918             if(t == today){
16919                 cell.className += " fc-today";
16920                 cell.className += " fc-state-highlight";
16921                 cell.title = cal.todayText;
16922             }
16923             if(t == sel){
16924                 // disable highlight in other month..
16925                 //cell.className += " fc-state-highlight";
16926                 
16927             }
16928             // disabling
16929             if(t < min) {
16930                 cell.className = " fc-state-disabled";
16931                 cell.title = cal.minText;
16932                 return;
16933             }
16934             if(t > max) {
16935                 cell.className = " fc-state-disabled";
16936                 cell.title = cal.maxText;
16937                 return;
16938             }
16939             if(ddays){
16940                 if(ddays.indexOf(d.getDay()) != -1){
16941                     cell.title = ddaysText;
16942                     cell.className = " fc-state-disabled";
16943                 }
16944             }
16945             if(ddMatch && format){
16946                 var fvalue = d.dateFormat(format);
16947                 if(ddMatch.test(fvalue)){
16948                     cell.title = ddText.replace("%0", fvalue);
16949                     cell.className = " fc-state-disabled";
16950                 }
16951             }
16952             
16953             if (!cell.initialClassName) {
16954                 cell.initialClassName = cell.dom.className;
16955             }
16956             
16957             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16958         };
16959
16960         var i = 0;
16961         
16962         for(; i < startingPos; i++) {
16963             textEls[i].innerHTML = (++prevStart);
16964             d.setDate(d.getDate()+1);
16965             
16966             cells[i].className = "fc-past fc-other-month";
16967             setCellClass(this, cells[i]);
16968         }
16969         
16970         var intDay = 0;
16971         
16972         for(; i < days; i++){
16973             intDay = i - startingPos + 1;
16974             textEls[i].innerHTML = (intDay);
16975             d.setDate(d.getDate()+1);
16976             
16977             cells[i].className = ''; // "x-date-active";
16978             setCellClass(this, cells[i]);
16979         }
16980         var extraDays = 0;
16981         
16982         for(; i < 42; i++) {
16983             textEls[i].innerHTML = (++extraDays);
16984             d.setDate(d.getDate()+1);
16985             
16986             cells[i].className = "fc-future fc-other-month";
16987             setCellClass(this, cells[i]);
16988         }
16989         
16990         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16991         
16992         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16993         
16994         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16995         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16996         
16997         if(totalRows != 6){
16998             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16999             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17000         }
17001         
17002         this.fireEvent('monthchange', this, date);
17003         
17004         
17005         /*
17006         if(!this.internalRender){
17007             var main = this.el.dom.firstChild;
17008             var w = main.offsetWidth;
17009             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17010             Roo.fly(main).setWidth(w);
17011             this.internalRender = true;
17012             // opera does not respect the auto grow header center column
17013             // then, after it gets a width opera refuses to recalculate
17014             // without a second pass
17015             if(Roo.isOpera && !this.secondPass){
17016                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17017                 this.secondPass = true;
17018                 this.update.defer(10, this, [date]);
17019             }
17020         }
17021         */
17022         
17023     },
17024     
17025     findCell : function(dt) {
17026         dt = dt.clearTime().getTime();
17027         var ret = false;
17028         this.cells.each(function(c){
17029             //Roo.log("check " +c.dateValue + '?=' + dt);
17030             if(c.dateValue == dt){
17031                 ret = c;
17032                 return false;
17033             }
17034             return true;
17035         });
17036         
17037         return ret;
17038     },
17039     
17040     findCells : function(ev) {
17041         var s = ev.start.clone().clearTime().getTime();
17042        // Roo.log(s);
17043         var e= ev.end.clone().clearTime().getTime();
17044        // Roo.log(e);
17045         var ret = [];
17046         this.cells.each(function(c){
17047              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17048             
17049             if(c.dateValue > e){
17050                 return ;
17051             }
17052             if(c.dateValue < s){
17053                 return ;
17054             }
17055             ret.push(c);
17056         });
17057         
17058         return ret;    
17059     },
17060     
17061 //    findBestRow: function(cells)
17062 //    {
17063 //        var ret = 0;
17064 //        
17065 //        for (var i =0 ; i < cells.length;i++) {
17066 //            ret  = Math.max(cells[i].rows || 0,ret);
17067 //        }
17068 //        return ret;
17069 //        
17070 //    },
17071     
17072     
17073     addItem : function(ev)
17074     {
17075         // look for vertical location slot in
17076         var cells = this.findCells(ev);
17077         
17078 //        ev.row = this.findBestRow(cells);
17079         
17080         // work out the location.
17081         
17082         var crow = false;
17083         var rows = [];
17084         for(var i =0; i < cells.length; i++) {
17085             
17086             cells[i].row = cells[0].row;
17087             
17088             if(i == 0){
17089                 cells[i].row = cells[i].row + 1;
17090             }
17091             
17092             if (!crow) {
17093                 crow = {
17094                     start : cells[i],
17095                     end :  cells[i]
17096                 };
17097                 continue;
17098             }
17099             if (crow.start.getY() == cells[i].getY()) {
17100                 // on same row.
17101                 crow.end = cells[i];
17102                 continue;
17103             }
17104             // different row.
17105             rows.push(crow);
17106             crow = {
17107                 start: cells[i],
17108                 end : cells[i]
17109             };
17110             
17111         }
17112         
17113         rows.push(crow);
17114         ev.els = [];
17115         ev.rows = rows;
17116         ev.cells = cells;
17117         
17118         cells[0].events.push(ev);
17119         
17120         this.calevents.push(ev);
17121     },
17122     
17123     clearEvents: function() {
17124         
17125         if(!this.calevents){
17126             return;
17127         }
17128         
17129         Roo.each(this.cells.elements, function(c){
17130             c.row = 0;
17131             c.events = [];
17132             c.more = [];
17133         });
17134         
17135         Roo.each(this.calevents, function(e) {
17136             Roo.each(e.els, function(el) {
17137                 el.un('mouseenter' ,this.onEventEnter, this);
17138                 el.un('mouseleave' ,this.onEventLeave, this);
17139                 el.remove();
17140             },this);
17141         },this);
17142         
17143         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17144             e.remove();
17145         });
17146         
17147     },
17148     
17149     renderEvents: function()
17150     {   
17151         var _this = this;
17152         
17153         this.cells.each(function(c) {
17154             
17155             if(c.row < 5){
17156                 return;
17157             }
17158             
17159             var ev = c.events;
17160             
17161             var r = 4;
17162             if(c.row != c.events.length){
17163                 r = 4 - (4 - (c.row - c.events.length));
17164             }
17165             
17166             c.events = ev.slice(0, r);
17167             c.more = ev.slice(r);
17168             
17169             if(c.more.length && c.more.length == 1){
17170                 c.events.push(c.more.pop());
17171             }
17172             
17173             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17174             
17175         });
17176             
17177         this.cells.each(function(c) {
17178             
17179             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17180             
17181             
17182             for (var e = 0; e < c.events.length; e++){
17183                 var ev = c.events[e];
17184                 var rows = ev.rows;
17185                 
17186                 for(var i = 0; i < rows.length; i++) {
17187                 
17188                     // how many rows should it span..
17189
17190                     var  cfg = {
17191                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17192                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17193
17194                         unselectable : "on",
17195                         cn : [
17196                             {
17197                                 cls: 'fc-event-inner',
17198                                 cn : [
17199     //                                {
17200     //                                  tag:'span',
17201     //                                  cls: 'fc-event-time',
17202     //                                  html : cells.length > 1 ? '' : ev.time
17203     //                                },
17204                                     {
17205                                       tag:'span',
17206                                       cls: 'fc-event-title',
17207                                       html : String.format('{0}', ev.title)
17208                                     }
17209
17210
17211                                 ]
17212                             },
17213                             {
17214                                 cls: 'ui-resizable-handle ui-resizable-e',
17215                                 html : '&nbsp;&nbsp;&nbsp'
17216                             }
17217
17218                         ]
17219                     };
17220
17221                     if (i == 0) {
17222                         cfg.cls += ' fc-event-start';
17223                     }
17224                     if ((i+1) == rows.length) {
17225                         cfg.cls += ' fc-event-end';
17226                     }
17227
17228                     var ctr = _this.el.select('.fc-event-container',true).first();
17229                     var cg = ctr.createChild(cfg);
17230
17231                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17232                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17233
17234                     var r = (c.more.length) ? 1 : 0;
17235                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17236                     cg.setWidth(ebox.right - sbox.x -2);
17237
17238                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17239                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17240                     cg.on('click', _this.onEventClick, _this, ev);
17241
17242                     ev.els.push(cg);
17243                     
17244                 }
17245                 
17246             }
17247             
17248             
17249             if(c.more.length){
17250                 var  cfg = {
17251                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17252                     style : 'position: absolute',
17253                     unselectable : "on",
17254                     cn : [
17255                         {
17256                             cls: 'fc-event-inner',
17257                             cn : [
17258                                 {
17259                                   tag:'span',
17260                                   cls: 'fc-event-title',
17261                                   html : 'More'
17262                                 }
17263
17264
17265                             ]
17266                         },
17267                         {
17268                             cls: 'ui-resizable-handle ui-resizable-e',
17269                             html : '&nbsp;&nbsp;&nbsp'
17270                         }
17271
17272                     ]
17273                 };
17274
17275                 var ctr = _this.el.select('.fc-event-container',true).first();
17276                 var cg = ctr.createChild(cfg);
17277
17278                 var sbox = c.select('.fc-day-content',true).first().getBox();
17279                 var ebox = c.select('.fc-day-content',true).first().getBox();
17280                 //Roo.log(cg);
17281                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17282                 cg.setWidth(ebox.right - sbox.x -2);
17283
17284                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17285                 
17286             }
17287             
17288         });
17289         
17290         
17291         
17292     },
17293     
17294     onEventEnter: function (e, el,event,d) {
17295         this.fireEvent('evententer', this, el, event);
17296     },
17297     
17298     onEventLeave: function (e, el,event,d) {
17299         this.fireEvent('eventleave', this, el, event);
17300     },
17301     
17302     onEventClick: function (e, el,event,d) {
17303         this.fireEvent('eventclick', this, el, event);
17304     },
17305     
17306     onMonthChange: function () {
17307         this.store.load();
17308     },
17309     
17310     onMoreEventClick: function(e, el, more)
17311     {
17312         var _this = this;
17313         
17314         this.calpopover.placement = 'right';
17315         this.calpopover.setTitle('More');
17316         
17317         this.calpopover.setContent('');
17318         
17319         var ctr = this.calpopover.el.select('.popover-content', true).first();
17320         
17321         Roo.each(more, function(m){
17322             var cfg = {
17323                 cls : 'fc-event-hori fc-event-draggable',
17324                 html : m.title
17325             };
17326             var cg = ctr.createChild(cfg);
17327             
17328             cg.on('click', _this.onEventClick, _this, m);
17329         });
17330         
17331         this.calpopover.show(el);
17332         
17333         
17334     },
17335     
17336     onLoad: function () 
17337     {   
17338         this.calevents = [];
17339         var cal = this;
17340         
17341         if(this.store.getCount() > 0){
17342             this.store.data.each(function(d){
17343                cal.addItem({
17344                     id : d.data.id,
17345                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17346                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17347                     time : d.data.start_time,
17348                     title : d.data.title,
17349                     description : d.data.description,
17350                     venue : d.data.venue
17351                 });
17352             });
17353         }
17354         
17355         this.renderEvents();
17356         
17357         if(this.calevents.length && this.loadMask){
17358             this.maskEl.hide();
17359         }
17360     },
17361     
17362     onBeforeLoad: function()
17363     {
17364         this.clearEvents();
17365         if(this.loadMask){
17366             this.maskEl.show();
17367         }
17368     }
17369 });
17370
17371  
17372  /*
17373  * - LGPL
17374  *
17375  * element
17376  * 
17377  */
17378
17379 /**
17380  * @class Roo.bootstrap.Popover
17381  * @extends Roo.bootstrap.Component
17382  * Bootstrap Popover class
17383  * @cfg {String} html contents of the popover   (or false to use children..)
17384  * @cfg {String} title of popover (or false to hide)
17385  * @cfg {String} placement how it is placed
17386  * @cfg {String} trigger click || hover (or false to trigger manually)
17387  * @cfg {String} over what (parent or false to trigger manually.)
17388  * @cfg {Number} delay - delay before showing
17389  
17390  * @constructor
17391  * Create a new Popover
17392  * @param {Object} config The config object
17393  */
17394
17395 Roo.bootstrap.Popover = function(config){
17396     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17397     
17398     this.addEvents({
17399         // raw events
17400          /**
17401          * @event show
17402          * After the popover show
17403          * 
17404          * @param {Roo.bootstrap.Popover} this
17405          */
17406         "show" : true,
17407         /**
17408          * @event hide
17409          * After the popover hide
17410          * 
17411          * @param {Roo.bootstrap.Popover} this
17412          */
17413         "hide" : true
17414     });
17415 };
17416
17417 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17418     
17419     title: 'Fill in a title',
17420     html: false,
17421     
17422     placement : 'right',
17423     trigger : 'hover', // hover
17424     
17425     delay : 0,
17426     
17427     over: 'parent',
17428     
17429     can_build_overlaid : false,
17430     
17431     getChildContainer : function()
17432     {
17433         return this.el.select('.popover-content',true).first();
17434     },
17435     
17436     getAutoCreate : function(){
17437          
17438         var cfg = {
17439            cls : 'popover roo-dynamic',
17440            style: 'display:block',
17441            cn : [
17442                 {
17443                     cls : 'arrow'
17444                 },
17445                 {
17446                     cls : 'popover-inner',
17447                     cn : [
17448                         {
17449                             tag: 'h3',
17450                             cls: 'popover-title',
17451                             html : this.title
17452                         },
17453                         {
17454                             cls : 'popover-content',
17455                             html : this.html
17456                         }
17457                     ]
17458                     
17459                 }
17460            ]
17461         };
17462         
17463         return cfg;
17464     },
17465     setTitle: function(str)
17466     {
17467         this.title = str;
17468         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17469     },
17470     setContent: function(str)
17471     {
17472         this.html = str;
17473         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17474     },
17475     // as it get's added to the bottom of the page.
17476     onRender : function(ct, position)
17477     {
17478         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17479         if(!this.el){
17480             var cfg = Roo.apply({},  this.getAutoCreate());
17481             cfg.id = Roo.id();
17482             
17483             if (this.cls) {
17484                 cfg.cls += ' ' + this.cls;
17485             }
17486             if (this.style) {
17487                 cfg.style = this.style;
17488             }
17489             //Roo.log("adding to ");
17490             this.el = Roo.get(document.body).createChild(cfg, position);
17491 //            Roo.log(this.el);
17492         }
17493         this.initEvents();
17494     },
17495     
17496     initEvents : function()
17497     {
17498         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17499         this.el.enableDisplayMode('block');
17500         this.el.hide();
17501         if (this.over === false) {
17502             return; 
17503         }
17504         if (this.triggers === false) {
17505             return;
17506         }
17507         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17508         var triggers = this.trigger ? this.trigger.split(' ') : [];
17509         Roo.each(triggers, function(trigger) {
17510         
17511             if (trigger == 'click') {
17512                 on_el.on('click', this.toggle, this);
17513             } else if (trigger != 'manual') {
17514                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17515                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17516       
17517                 on_el.on(eventIn  ,this.enter, this);
17518                 on_el.on(eventOut, this.leave, this);
17519             }
17520         }, this);
17521         
17522     },
17523     
17524     
17525     // private
17526     timeout : null,
17527     hoverState : null,
17528     
17529     toggle : function () {
17530         this.hoverState == 'in' ? this.leave() : this.enter();
17531     },
17532     
17533     enter : function () {
17534         
17535         clearTimeout(this.timeout);
17536     
17537         this.hoverState = 'in';
17538     
17539         if (!this.delay || !this.delay.show) {
17540             this.show();
17541             return;
17542         }
17543         var _t = this;
17544         this.timeout = setTimeout(function () {
17545             if (_t.hoverState == 'in') {
17546                 _t.show();
17547             }
17548         }, this.delay.show)
17549     },
17550     
17551     leave : function() {
17552         clearTimeout(this.timeout);
17553     
17554         this.hoverState = 'out';
17555     
17556         if (!this.delay || !this.delay.hide) {
17557             this.hide();
17558             return;
17559         }
17560         var _t = this;
17561         this.timeout = setTimeout(function () {
17562             if (_t.hoverState == 'out') {
17563                 _t.hide();
17564             }
17565         }, this.delay.hide)
17566     },
17567     
17568     show : function (on_el)
17569     {
17570         if (!on_el) {
17571             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17572         }
17573         
17574         // set content.
17575         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17576         if (this.html !== false) {
17577             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17578         }
17579         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17580         if (!this.title.length) {
17581             this.el.select('.popover-title',true).hide();
17582         }
17583         
17584         var placement = typeof this.placement == 'function' ?
17585             this.placement.call(this, this.el, on_el) :
17586             this.placement;
17587             
17588         var autoToken = /\s?auto?\s?/i;
17589         var autoPlace = autoToken.test(placement);
17590         if (autoPlace) {
17591             placement = placement.replace(autoToken, '') || 'top';
17592         }
17593         
17594         //this.el.detach()
17595         //this.el.setXY([0,0]);
17596         this.el.show();
17597         this.el.dom.style.display='block';
17598         this.el.addClass(placement);
17599         
17600         //this.el.appendTo(on_el);
17601         
17602         var p = this.getPosition();
17603         var box = this.el.getBox();
17604         
17605         if (autoPlace) {
17606             // fixme..
17607         }
17608         var align = Roo.bootstrap.Popover.alignment[placement];
17609         
17610 //        Roo.log(align);
17611         this.el.alignTo(on_el, align[0],align[1]);
17612         //var arrow = this.el.select('.arrow',true).first();
17613         //arrow.set(align[2], 
17614         
17615         this.el.addClass('in');
17616         
17617         
17618         if (this.el.hasClass('fade')) {
17619             // fade it?
17620         }
17621         
17622         this.hoverState = 'in';
17623         
17624         this.fireEvent('show', this);
17625         
17626     },
17627     hide : function()
17628     {
17629         this.el.setXY([0,0]);
17630         this.el.removeClass('in');
17631         this.el.hide();
17632         this.hoverState = null;
17633         
17634         this.fireEvent('hide', this);
17635     }
17636     
17637 });
17638
17639 Roo.bootstrap.Popover.alignment = {
17640     'left' : ['r-l', [-10,0], 'right'],
17641     'right' : ['l-r', [10,0], 'left'],
17642     'bottom' : ['t-b', [0,10], 'top'],
17643     'top' : [ 'b-t', [0,-10], 'bottom']
17644 };
17645
17646  /*
17647  * - LGPL
17648  *
17649  * Progress
17650  * 
17651  */
17652
17653 /**
17654  * @class Roo.bootstrap.Progress
17655  * @extends Roo.bootstrap.Component
17656  * Bootstrap Progress class
17657  * @cfg {Boolean} striped striped of the progress bar
17658  * @cfg {Boolean} active animated of the progress bar
17659  * 
17660  * 
17661  * @constructor
17662  * Create a new Progress
17663  * @param {Object} config The config object
17664  */
17665
17666 Roo.bootstrap.Progress = function(config){
17667     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17668 };
17669
17670 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17671     
17672     striped : false,
17673     active: false,
17674     
17675     getAutoCreate : function(){
17676         var cfg = {
17677             tag: 'div',
17678             cls: 'progress'
17679         };
17680         
17681         
17682         if(this.striped){
17683             cfg.cls += ' progress-striped';
17684         }
17685       
17686         if(this.active){
17687             cfg.cls += ' active';
17688         }
17689         
17690         
17691         return cfg;
17692     }
17693    
17694 });
17695
17696  
17697
17698  /*
17699  * - LGPL
17700  *
17701  * ProgressBar
17702  * 
17703  */
17704
17705 /**
17706  * @class Roo.bootstrap.ProgressBar
17707  * @extends Roo.bootstrap.Component
17708  * Bootstrap ProgressBar class
17709  * @cfg {Number} aria_valuenow aria-value now
17710  * @cfg {Number} aria_valuemin aria-value min
17711  * @cfg {Number} aria_valuemax aria-value max
17712  * @cfg {String} label label for the progress bar
17713  * @cfg {String} panel (success | info | warning | danger )
17714  * @cfg {String} role role of the progress bar
17715  * @cfg {String} sr_only text
17716  * 
17717  * 
17718  * @constructor
17719  * Create a new ProgressBar
17720  * @param {Object} config The config object
17721  */
17722
17723 Roo.bootstrap.ProgressBar = function(config){
17724     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17725 };
17726
17727 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17728     
17729     aria_valuenow : 0,
17730     aria_valuemin : 0,
17731     aria_valuemax : 100,
17732     label : false,
17733     panel : false,
17734     role : false,
17735     sr_only: false,
17736     
17737     getAutoCreate : function()
17738     {
17739         
17740         var cfg = {
17741             tag: 'div',
17742             cls: 'progress-bar',
17743             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17744         };
17745         
17746         if(this.sr_only){
17747             cfg.cn = {
17748                 tag: 'span',
17749                 cls: 'sr-only',
17750                 html: this.sr_only
17751             }
17752         }
17753         
17754         if(this.role){
17755             cfg.role = this.role;
17756         }
17757         
17758         if(this.aria_valuenow){
17759             cfg['aria-valuenow'] = this.aria_valuenow;
17760         }
17761         
17762         if(this.aria_valuemin){
17763             cfg['aria-valuemin'] = this.aria_valuemin;
17764         }
17765         
17766         if(this.aria_valuemax){
17767             cfg['aria-valuemax'] = this.aria_valuemax;
17768         }
17769         
17770         if(this.label && !this.sr_only){
17771             cfg.html = this.label;
17772         }
17773         
17774         if(this.panel){
17775             cfg.cls += ' progress-bar-' + this.panel;
17776         }
17777         
17778         return cfg;
17779     },
17780     
17781     update : function(aria_valuenow)
17782     {
17783         this.aria_valuenow = aria_valuenow;
17784         
17785         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17786     }
17787    
17788 });
17789
17790  
17791
17792  /*
17793  * - LGPL
17794  *
17795  * column
17796  * 
17797  */
17798
17799 /**
17800  * @class Roo.bootstrap.TabGroup
17801  * @extends Roo.bootstrap.Column
17802  * Bootstrap Column class
17803  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17804  * @cfg {Boolean} carousel true to make the group behave like a carousel
17805  * @cfg {Boolean} bullets show bullets for the panels
17806  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17807  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17808  * @cfg {Boolean} showarrow (true|false) show arrow default true
17809  * 
17810  * @constructor
17811  * Create a new TabGroup
17812  * @param {Object} config The config object
17813  */
17814
17815 Roo.bootstrap.TabGroup = function(config){
17816     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17817     if (!this.navId) {
17818         this.navId = Roo.id();
17819     }
17820     this.tabs = [];
17821     Roo.bootstrap.TabGroup.register(this);
17822     
17823 };
17824
17825 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17826     
17827     carousel : false,
17828     transition : false,
17829     bullets : 0,
17830     timer : 0,
17831     autoslide : false,
17832     slideFn : false,
17833     slideOnTouch : false,
17834     showarrow : true,
17835     
17836     getAutoCreate : function()
17837     {
17838         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17839         
17840         cfg.cls += ' tab-content';
17841         
17842         if (this.carousel) {
17843             cfg.cls += ' carousel slide';
17844             
17845             cfg.cn = [{
17846                cls : 'carousel-inner',
17847                cn : []
17848             }];
17849         
17850             if(this.bullets  && !Roo.isTouch){
17851                 
17852                 var bullets = {
17853                     cls : 'carousel-bullets',
17854                     cn : []
17855                 };
17856                
17857                 if(this.bullets_cls){
17858                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17859                 }
17860                 
17861                 bullets.cn.push({
17862                     cls : 'clear'
17863                 });
17864                 
17865                 cfg.cn[0].cn.push(bullets);
17866             }
17867             
17868             if(this.showarrow){
17869                 cfg.cn[0].cn.push({
17870                     tag : 'div',
17871                     class : 'carousel-arrow',
17872                     cn : [
17873                         {
17874                             tag : 'div',
17875                             class : 'carousel-prev',
17876                             cn : [
17877                                 {
17878                                     tag : 'i',
17879                                     class : 'fa fa-chevron-left'
17880                                 }
17881                             ]
17882                         },
17883                         {
17884                             tag : 'div',
17885                             class : 'carousel-next',
17886                             cn : [
17887                                 {
17888                                     tag : 'i',
17889                                     class : 'fa fa-chevron-right'
17890                                 }
17891                             ]
17892                         }
17893                     ]
17894                 });
17895             }
17896             
17897         }
17898         
17899         return cfg;
17900     },
17901     
17902     initEvents:  function()
17903     {
17904 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17905 //            this.el.on("touchstart", this.onTouchStart, this);
17906 //        }
17907         
17908         if(this.autoslide){
17909             var _this = this;
17910             
17911             this.slideFn = window.setInterval(function() {
17912                 _this.showPanelNext();
17913             }, this.timer);
17914         }
17915         
17916         if(this.showarrow){
17917             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17918             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17919         }
17920         
17921         
17922     },
17923     
17924 //    onTouchStart : function(e, el, o)
17925 //    {
17926 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17927 //            return;
17928 //        }
17929 //        
17930 //        this.showPanelNext();
17931 //    },
17932     
17933     
17934     getChildContainer : function()
17935     {
17936         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17937     },
17938     
17939     /**
17940     * register a Navigation item
17941     * @param {Roo.bootstrap.NavItem} the navitem to add
17942     */
17943     register : function(item)
17944     {
17945         this.tabs.push( item);
17946         item.navId = this.navId; // not really needed..
17947         this.addBullet();
17948     
17949     },
17950     
17951     getActivePanel : function()
17952     {
17953         var r = false;
17954         Roo.each(this.tabs, function(t) {
17955             if (t.active) {
17956                 r = t;
17957                 return false;
17958             }
17959             return null;
17960         });
17961         return r;
17962         
17963     },
17964     getPanelByName : function(n)
17965     {
17966         var r = false;
17967         Roo.each(this.tabs, function(t) {
17968             if (t.tabId == n) {
17969                 r = t;
17970                 return false;
17971             }
17972             return null;
17973         });
17974         return r;
17975     },
17976     indexOfPanel : function(p)
17977     {
17978         var r = false;
17979         Roo.each(this.tabs, function(t,i) {
17980             if (t.tabId == p.tabId) {
17981                 r = i;
17982                 return false;
17983             }
17984             return null;
17985         });
17986         return r;
17987     },
17988     /**
17989      * show a specific panel
17990      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17991      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17992      */
17993     showPanel : function (pan)
17994     {
17995         if(this.transition || typeof(pan) == 'undefined'){
17996             Roo.log("waiting for the transitionend");
17997             return;
17998         }
17999         
18000         if (typeof(pan) == 'number') {
18001             pan = this.tabs[pan];
18002         }
18003         
18004         if (typeof(pan) == 'string') {
18005             pan = this.getPanelByName(pan);
18006         }
18007         
18008         var cur = this.getActivePanel();
18009         
18010         if(!pan || !cur){
18011             Roo.log('pan or acitve pan is undefined');
18012             return false;
18013         }
18014         
18015         if (pan.tabId == this.getActivePanel().tabId) {
18016             return true;
18017         }
18018         
18019         if (false === cur.fireEvent('beforedeactivate')) {
18020             return false;
18021         }
18022         
18023         if(this.bullets > 0 && !Roo.isTouch){
18024             this.setActiveBullet(this.indexOfPanel(pan));
18025         }
18026         
18027         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18028             
18029             this.transition = true;
18030             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18031             var lr = dir == 'next' ? 'left' : 'right';
18032             pan.el.addClass(dir); // or prev
18033             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18034             cur.el.addClass(lr); // or right
18035             pan.el.addClass(lr);
18036             
18037             var _this = this;
18038             cur.el.on('transitionend', function() {
18039                 Roo.log("trans end?");
18040                 
18041                 pan.el.removeClass([lr,dir]);
18042                 pan.setActive(true);
18043                 
18044                 cur.el.removeClass([lr]);
18045                 cur.setActive(false);
18046                 
18047                 _this.transition = false;
18048                 
18049             }, this, { single:  true } );
18050             
18051             return true;
18052         }
18053         
18054         cur.setActive(false);
18055         pan.setActive(true);
18056         
18057         return true;
18058         
18059     },
18060     showPanelNext : function()
18061     {
18062         var i = this.indexOfPanel(this.getActivePanel());
18063         
18064         if (i >= this.tabs.length - 1 && !this.autoslide) {
18065             return;
18066         }
18067         
18068         if (i >= this.tabs.length - 1 && this.autoslide) {
18069             i = -1;
18070         }
18071         
18072         this.showPanel(this.tabs[i+1]);
18073     },
18074     
18075     showPanelPrev : function()
18076     {
18077         var i = this.indexOfPanel(this.getActivePanel());
18078         
18079         if (i  < 1 && !this.autoslide) {
18080             return;
18081         }
18082         
18083         if (i < 1 && this.autoslide) {
18084             i = this.tabs.length;
18085         }
18086         
18087         this.showPanel(this.tabs[i-1]);
18088     },
18089     
18090     
18091     addBullet: function()
18092     {
18093         if(!this.bullets || Roo.isTouch){
18094             return;
18095         }
18096         var ctr = this.el.select('.carousel-bullets',true).first();
18097         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18098         var bullet = ctr.createChild({
18099             cls : 'bullet bullet-' + i
18100         },ctr.dom.lastChild);
18101         
18102         
18103         var _this = this;
18104         
18105         bullet.on('click', (function(e, el, o, ii, t){
18106
18107             e.preventDefault();
18108
18109             this.showPanel(ii);
18110
18111             if(this.autoslide && this.slideFn){
18112                 clearInterval(this.slideFn);
18113                 this.slideFn = window.setInterval(function() {
18114                     _this.showPanelNext();
18115                 }, this.timer);
18116             }
18117
18118         }).createDelegate(this, [i, bullet], true));
18119                 
18120         
18121     },
18122      
18123     setActiveBullet : function(i)
18124     {
18125         if(Roo.isTouch){
18126             return;
18127         }
18128         
18129         Roo.each(this.el.select('.bullet', true).elements, function(el){
18130             el.removeClass('selected');
18131         });
18132
18133         var bullet = this.el.select('.bullet-' + i, true).first();
18134         
18135         if(!bullet){
18136             return;
18137         }
18138         
18139         bullet.addClass('selected');
18140     }
18141     
18142     
18143   
18144 });
18145
18146  
18147
18148  
18149  
18150 Roo.apply(Roo.bootstrap.TabGroup, {
18151     
18152     groups: {},
18153      /**
18154     * register a Navigation Group
18155     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18156     */
18157     register : function(navgrp)
18158     {
18159         this.groups[navgrp.navId] = navgrp;
18160         
18161     },
18162     /**
18163     * fetch a Navigation Group based on the navigation ID
18164     * if one does not exist , it will get created.
18165     * @param {string} the navgroup to add
18166     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18167     */
18168     get: function(navId) {
18169         if (typeof(this.groups[navId]) == 'undefined') {
18170             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18171         }
18172         return this.groups[navId] ;
18173     }
18174     
18175     
18176     
18177 });
18178
18179  /*
18180  * - LGPL
18181  *
18182  * TabPanel
18183  * 
18184  */
18185
18186 /**
18187  * @class Roo.bootstrap.TabPanel
18188  * @extends Roo.bootstrap.Component
18189  * Bootstrap TabPanel class
18190  * @cfg {Boolean} active panel active
18191  * @cfg {String} html panel content
18192  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18193  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18194  * @cfg {String} href click to link..
18195  * 
18196  * 
18197  * @constructor
18198  * Create a new TabPanel
18199  * @param {Object} config The config object
18200  */
18201
18202 Roo.bootstrap.TabPanel = function(config){
18203     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18204     this.addEvents({
18205         /**
18206              * @event changed
18207              * Fires when the active status changes
18208              * @param {Roo.bootstrap.TabPanel} this
18209              * @param {Boolean} state the new state
18210             
18211          */
18212         'changed': true,
18213         /**
18214              * @event beforedeactivate
18215              * Fires before a tab is de-activated - can be used to do validation on a form.
18216              * @param {Roo.bootstrap.TabPanel} this
18217              * @return {Boolean} false if there is an error
18218             
18219          */
18220         'beforedeactivate': true
18221      });
18222     
18223     this.tabId = this.tabId || Roo.id();
18224   
18225 };
18226
18227 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18228     
18229     active: false,
18230     html: false,
18231     tabId: false,
18232     navId : false,
18233     href : '',
18234     
18235     getAutoCreate : function(){
18236         var cfg = {
18237             tag: 'div',
18238             // item is needed for carousel - not sure if it has any effect otherwise
18239             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18240             html: this.html || ''
18241         };
18242         
18243         if(this.active){
18244             cfg.cls += ' active';
18245         }
18246         
18247         if(this.tabId){
18248             cfg.tabId = this.tabId;
18249         }
18250         
18251         
18252         return cfg;
18253     },
18254     
18255     initEvents:  function()
18256     {
18257         var p = this.parent();
18258         
18259         this.navId = this.navId || p.navId;
18260         
18261         if (typeof(this.navId) != 'undefined') {
18262             // not really needed.. but just in case.. parent should be a NavGroup.
18263             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18264             
18265             tg.register(this);
18266             
18267             var i = tg.tabs.length - 1;
18268             
18269             if(this.active && tg.bullets > 0 && i < tg.bullets){
18270                 tg.setActiveBullet(i);
18271             }
18272         }
18273         
18274         this.el.on('click', this.onClick, this);
18275         
18276         if(Roo.isTouch){
18277             this.el.on("touchstart", this.onTouchStart, this);
18278             this.el.on("touchmove", this.onTouchMove, this);
18279             this.el.on("touchend", this.onTouchEnd, this);
18280         }
18281         
18282     },
18283     
18284     onRender : function(ct, position)
18285     {
18286         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18287     },
18288     
18289     setActive : function(state)
18290     {
18291         Roo.log("panel - set active " + this.tabId + "=" + state);
18292         
18293         this.active = state;
18294         if (!state) {
18295             this.el.removeClass('active');
18296             
18297         } else  if (!this.el.hasClass('active')) {
18298             this.el.addClass('active');
18299         }
18300         
18301         this.fireEvent('changed', this, state);
18302     },
18303     
18304     onClick : function(e)
18305     {
18306         e.preventDefault();
18307         
18308         if(!this.href.length){
18309             return;
18310         }
18311         
18312         window.location.href = this.href;
18313     },
18314     
18315     startX : 0,
18316     startY : 0,
18317     endX : 0,
18318     endY : 0,
18319     swiping : false,
18320     
18321     onTouchStart : function(e)
18322     {
18323         this.swiping = false;
18324         
18325         this.startX = e.browserEvent.touches[0].clientX;
18326         this.startY = e.browserEvent.touches[0].clientY;
18327     },
18328     
18329     onTouchMove : function(e)
18330     {
18331         this.swiping = true;
18332         
18333         this.endX = e.browserEvent.touches[0].clientX;
18334         this.endY = e.browserEvent.touches[0].clientY;
18335     },
18336     
18337     onTouchEnd : function(e)
18338     {
18339         if(!this.swiping){
18340             this.onClick(e);
18341             return;
18342         }
18343         
18344         var tabGroup = this.parent();
18345         
18346         if(this.endX > this.startX){ // swiping right
18347             tabGroup.showPanelPrev();
18348             return;
18349         }
18350         
18351         if(this.startX > this.endX){ // swiping left
18352             tabGroup.showPanelNext();
18353             return;
18354         }
18355     }
18356     
18357     
18358 });
18359  
18360
18361  
18362
18363  /*
18364  * - LGPL
18365  *
18366  * DateField
18367  * 
18368  */
18369
18370 /**
18371  * @class Roo.bootstrap.DateField
18372  * @extends Roo.bootstrap.Input
18373  * Bootstrap DateField class
18374  * @cfg {Number} weekStart default 0
18375  * @cfg {String} viewMode default empty, (months|years)
18376  * @cfg {String} minViewMode default empty, (months|years)
18377  * @cfg {Number} startDate default -Infinity
18378  * @cfg {Number} endDate default Infinity
18379  * @cfg {Boolean} todayHighlight default false
18380  * @cfg {Boolean} todayBtn default false
18381  * @cfg {Boolean} calendarWeeks default false
18382  * @cfg {Object} daysOfWeekDisabled default empty
18383  * @cfg {Boolean} singleMode default false (true | false)
18384  * 
18385  * @cfg {Boolean} keyboardNavigation default true
18386  * @cfg {String} language default en
18387  * 
18388  * @constructor
18389  * Create a new DateField
18390  * @param {Object} config The config object
18391  */
18392
18393 Roo.bootstrap.DateField = function(config){
18394     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18395      this.addEvents({
18396             /**
18397              * @event show
18398              * Fires when this field show.
18399              * @param {Roo.bootstrap.DateField} this
18400              * @param {Mixed} date The date value
18401              */
18402             show : true,
18403             /**
18404              * @event show
18405              * Fires when this field hide.
18406              * @param {Roo.bootstrap.DateField} this
18407              * @param {Mixed} date The date value
18408              */
18409             hide : true,
18410             /**
18411              * @event select
18412              * Fires when select a date.
18413              * @param {Roo.bootstrap.DateField} this
18414              * @param {Mixed} date The date value
18415              */
18416             select : true,
18417             /**
18418              * @event beforeselect
18419              * Fires when before select a date.
18420              * @param {Roo.bootstrap.DateField} this
18421              * @param {Mixed} date The date value
18422              */
18423             beforeselect : true
18424         });
18425 };
18426
18427 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18428     
18429     /**
18430      * @cfg {String} format
18431      * The default date format string which can be overriden for localization support.  The format must be
18432      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18433      */
18434     format : "m/d/y",
18435     /**
18436      * @cfg {String} altFormats
18437      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18438      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18439      */
18440     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18441     
18442     weekStart : 0,
18443     
18444     viewMode : '',
18445     
18446     minViewMode : '',
18447     
18448     todayHighlight : false,
18449     
18450     todayBtn: false,
18451     
18452     language: 'en',
18453     
18454     keyboardNavigation: true,
18455     
18456     calendarWeeks: false,
18457     
18458     startDate: -Infinity,
18459     
18460     endDate: Infinity,
18461     
18462     daysOfWeekDisabled: [],
18463     
18464     _events: [],
18465     
18466     singleMode : false,
18467     
18468     UTCDate: function()
18469     {
18470         return new Date(Date.UTC.apply(Date, arguments));
18471     },
18472     
18473     UTCToday: function()
18474     {
18475         var today = new Date();
18476         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18477     },
18478     
18479     getDate: function() {
18480             var d = this.getUTCDate();
18481             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18482     },
18483     
18484     getUTCDate: function() {
18485             return this.date;
18486     },
18487     
18488     setDate: function(d) {
18489             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18490     },
18491     
18492     setUTCDate: function(d) {
18493             this.date = d;
18494             this.setValue(this.formatDate(this.date));
18495     },
18496         
18497     onRender: function(ct, position)
18498     {
18499         
18500         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18501         
18502         this.language = this.language || 'en';
18503         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18504         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18505         
18506         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18507         this.format = this.format || 'm/d/y';
18508         this.isInline = false;
18509         this.isInput = true;
18510         this.component = this.el.select('.add-on', true).first() || false;
18511         this.component = (this.component && this.component.length === 0) ? false : this.component;
18512         this.hasInput = this.component && this.inputEl().length;
18513         
18514         if (typeof(this.minViewMode === 'string')) {
18515             switch (this.minViewMode) {
18516                 case 'months':
18517                     this.minViewMode = 1;
18518                     break;
18519                 case 'years':
18520                     this.minViewMode = 2;
18521                     break;
18522                 default:
18523                     this.minViewMode = 0;
18524                     break;
18525             }
18526         }
18527         
18528         if (typeof(this.viewMode === 'string')) {
18529             switch (this.viewMode) {
18530                 case 'months':
18531                     this.viewMode = 1;
18532                     break;
18533                 case 'years':
18534                     this.viewMode = 2;
18535                     break;
18536                 default:
18537                     this.viewMode = 0;
18538                     break;
18539             }
18540         }
18541                 
18542         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18543         
18544 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18545         
18546         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18547         
18548         this.picker().on('mousedown', this.onMousedown, this);
18549         this.picker().on('click', this.onClick, this);
18550         
18551         this.picker().addClass('datepicker-dropdown');
18552         
18553         this.startViewMode = this.viewMode;
18554         
18555         if(this.singleMode){
18556             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18557                 v.setVisibilityMode(Roo.Element.DISPLAY);
18558                 v.hide();
18559             });
18560             
18561             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18562                 v.setStyle('width', '189px');
18563             });
18564         }
18565         
18566         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18567             if(!this.calendarWeeks){
18568                 v.remove();
18569                 return;
18570             }
18571             
18572             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18573             v.attr('colspan', function(i, val){
18574                 return parseInt(val) + 1;
18575             });
18576         });
18577                         
18578         
18579         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18580         
18581         this.setStartDate(this.startDate);
18582         this.setEndDate(this.endDate);
18583         
18584         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18585         
18586         this.fillDow();
18587         this.fillMonths();
18588         this.update();
18589         this.showMode();
18590         
18591         if(this.isInline) {
18592             this.showPopup();
18593         }
18594     },
18595     
18596     picker : function()
18597     {
18598         return this.pickerEl;
18599 //        return this.el.select('.datepicker', true).first();
18600     },
18601     
18602     fillDow: function()
18603     {
18604         var dowCnt = this.weekStart;
18605         
18606         var dow = {
18607             tag: 'tr',
18608             cn: [
18609                 
18610             ]
18611         };
18612         
18613         if(this.calendarWeeks){
18614             dow.cn.push({
18615                 tag: 'th',
18616                 cls: 'cw',
18617                 html: '&nbsp;'
18618             })
18619         }
18620         
18621         while (dowCnt < this.weekStart + 7) {
18622             dow.cn.push({
18623                 tag: 'th',
18624                 cls: 'dow',
18625                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18626             });
18627         }
18628         
18629         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18630     },
18631     
18632     fillMonths: function()
18633     {    
18634         var i = 0;
18635         var months = this.picker().select('>.datepicker-months td', true).first();
18636         
18637         months.dom.innerHTML = '';
18638         
18639         while (i < 12) {
18640             var month = {
18641                 tag: 'span',
18642                 cls: 'month',
18643                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18644             };
18645             
18646             months.createChild(month);
18647         }
18648         
18649     },
18650     
18651     update: function()
18652     {
18653         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;
18654         
18655         if (this.date < this.startDate) {
18656             this.viewDate = new Date(this.startDate);
18657         } else if (this.date > this.endDate) {
18658             this.viewDate = new Date(this.endDate);
18659         } else {
18660             this.viewDate = new Date(this.date);
18661         }
18662         
18663         this.fill();
18664     },
18665     
18666     fill: function() 
18667     {
18668         var d = new Date(this.viewDate),
18669                 year = d.getUTCFullYear(),
18670                 month = d.getUTCMonth(),
18671                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18672                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18673                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18674                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18675                 currentDate = this.date && this.date.valueOf(),
18676                 today = this.UTCToday();
18677         
18678         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18679         
18680 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18681         
18682 //        this.picker.select('>tfoot th.today').
18683 //                                              .text(dates[this.language].today)
18684 //                                              .toggle(this.todayBtn !== false);
18685     
18686         this.updateNavArrows();
18687         this.fillMonths();
18688                                                 
18689         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18690         
18691         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18692          
18693         prevMonth.setUTCDate(day);
18694         
18695         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18696         
18697         var nextMonth = new Date(prevMonth);
18698         
18699         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18700         
18701         nextMonth = nextMonth.valueOf();
18702         
18703         var fillMonths = false;
18704         
18705         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18706         
18707         while(prevMonth.valueOf() <= nextMonth) {
18708             var clsName = '';
18709             
18710             if (prevMonth.getUTCDay() === this.weekStart) {
18711                 if(fillMonths){
18712                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18713                 }
18714                     
18715                 fillMonths = {
18716                     tag: 'tr',
18717                     cn: []
18718                 };
18719                 
18720                 if(this.calendarWeeks){
18721                     // ISO 8601: First week contains first thursday.
18722                     // ISO also states week starts on Monday, but we can be more abstract here.
18723                     var
18724                     // Start of current week: based on weekstart/current date
18725                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18726                     // Thursday of this week
18727                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18728                     // First Thursday of year, year from thursday
18729                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18730                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18731                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18732                     
18733                     fillMonths.cn.push({
18734                         tag: 'td',
18735                         cls: 'cw',
18736                         html: calWeek
18737                     });
18738                 }
18739             }
18740             
18741             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18742                 clsName += ' old';
18743             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18744                 clsName += ' new';
18745             }
18746             if (this.todayHighlight &&
18747                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18748                 prevMonth.getUTCMonth() == today.getMonth() &&
18749                 prevMonth.getUTCDate() == today.getDate()) {
18750                 clsName += ' today';
18751             }
18752             
18753             if (currentDate && prevMonth.valueOf() === currentDate) {
18754                 clsName += ' active';
18755             }
18756             
18757             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18758                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18759                     clsName += ' disabled';
18760             }
18761             
18762             fillMonths.cn.push({
18763                 tag: 'td',
18764                 cls: 'day ' + clsName,
18765                 html: prevMonth.getDate()
18766             });
18767             
18768             prevMonth.setDate(prevMonth.getDate()+1);
18769         }
18770           
18771         var currentYear = this.date && this.date.getUTCFullYear();
18772         var currentMonth = this.date && this.date.getUTCMonth();
18773         
18774         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18775         
18776         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18777             v.removeClass('active');
18778             
18779             if(currentYear === year && k === currentMonth){
18780                 v.addClass('active');
18781             }
18782             
18783             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18784                 v.addClass('disabled');
18785             }
18786             
18787         });
18788         
18789         
18790         year = parseInt(year/10, 10) * 10;
18791         
18792         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18793         
18794         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18795         
18796         year -= 1;
18797         for (var i = -1; i < 11; i++) {
18798             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18799                 tag: 'span',
18800                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18801                 html: year
18802             });
18803             
18804             year += 1;
18805         }
18806     },
18807     
18808     showMode: function(dir) 
18809     {
18810         if (dir) {
18811             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18812         }
18813         
18814         Roo.each(this.picker().select('>div',true).elements, function(v){
18815             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18816             v.hide();
18817         });
18818         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18819     },
18820     
18821     place: function()
18822     {
18823         if(this.isInline) {
18824             return;
18825         }
18826         
18827         this.picker().removeClass(['bottom', 'top']);
18828         
18829         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18830             /*
18831              * place to the top of element!
18832              *
18833              */
18834             
18835             this.picker().addClass('top');
18836             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18837             
18838             return;
18839         }
18840         
18841         this.picker().addClass('bottom');
18842         
18843         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18844     },
18845     
18846     parseDate : function(value)
18847     {
18848         if(!value || value instanceof Date){
18849             return value;
18850         }
18851         var v = Date.parseDate(value, this.format);
18852         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18853             v = Date.parseDate(value, 'Y-m-d');
18854         }
18855         if(!v && this.altFormats){
18856             if(!this.altFormatsArray){
18857                 this.altFormatsArray = this.altFormats.split("|");
18858             }
18859             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18860                 v = Date.parseDate(value, this.altFormatsArray[i]);
18861             }
18862         }
18863         return v;
18864     },
18865     
18866     formatDate : function(date, fmt)
18867     {   
18868         return (!date || !(date instanceof Date)) ?
18869         date : date.dateFormat(fmt || this.format);
18870     },
18871     
18872     onFocus : function()
18873     {
18874         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18875         this.showPopup();
18876     },
18877     
18878     onBlur : function()
18879     {
18880         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18881         
18882         var d = this.inputEl().getValue();
18883         
18884         this.setValue(d);
18885                 
18886         this.hidePopup();
18887     },
18888     
18889     showPopup : function()
18890     {
18891         this.picker().show();
18892         this.update();
18893         this.place();
18894         
18895         this.fireEvent('showpopup', this, this.date);
18896     },
18897     
18898     hidePopup : function()
18899     {
18900         if(this.isInline) {
18901             return;
18902         }
18903         this.picker().hide();
18904         this.viewMode = this.startViewMode;
18905         this.showMode();
18906         
18907         this.fireEvent('hidepopup', this, this.date);
18908         
18909     },
18910     
18911     onMousedown: function(e)
18912     {
18913         e.stopPropagation();
18914         e.preventDefault();
18915     },
18916     
18917     keyup: function(e)
18918     {
18919         Roo.bootstrap.DateField.superclass.keyup.call(this);
18920         this.update();
18921     },
18922
18923     setValue: function(v)
18924     {
18925         if(this.fireEvent('beforeselect', this, v) !== false){
18926             var d = new Date(this.parseDate(v) ).clearTime();
18927         
18928             if(isNaN(d.getTime())){
18929                 this.date = this.viewDate = '';
18930                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18931                 return;
18932             }
18933
18934             v = this.formatDate(d);
18935
18936             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18937
18938             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18939
18940             this.update();
18941
18942             this.fireEvent('select', this, this.date);
18943         }
18944     },
18945     
18946     getValue: function()
18947     {
18948         return this.formatDate(this.date);
18949     },
18950     
18951     fireKey: function(e)
18952     {
18953         if (!this.picker().isVisible()){
18954             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18955                 this.showPopup();
18956             }
18957             return;
18958         }
18959         
18960         var dateChanged = false,
18961         dir, day, month,
18962         newDate, newViewDate;
18963         
18964         switch(e.keyCode){
18965             case 27: // escape
18966                 this.hidePopup();
18967                 e.preventDefault();
18968                 break;
18969             case 37: // left
18970             case 39: // right
18971                 if (!this.keyboardNavigation) {
18972                     break;
18973                 }
18974                 dir = e.keyCode == 37 ? -1 : 1;
18975                 
18976                 if (e.ctrlKey){
18977                     newDate = this.moveYear(this.date, dir);
18978                     newViewDate = this.moveYear(this.viewDate, dir);
18979                 } else if (e.shiftKey){
18980                     newDate = this.moveMonth(this.date, dir);
18981                     newViewDate = this.moveMonth(this.viewDate, dir);
18982                 } else {
18983                     newDate = new Date(this.date);
18984                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18985                     newViewDate = new Date(this.viewDate);
18986                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18987                 }
18988                 if (this.dateWithinRange(newDate)){
18989                     this.date = newDate;
18990                     this.viewDate = newViewDate;
18991                     this.setValue(this.formatDate(this.date));
18992 //                    this.update();
18993                     e.preventDefault();
18994                     dateChanged = true;
18995                 }
18996                 break;
18997             case 38: // up
18998             case 40: // down
18999                 if (!this.keyboardNavigation) {
19000                     break;
19001                 }
19002                 dir = e.keyCode == 38 ? -1 : 1;
19003                 if (e.ctrlKey){
19004                     newDate = this.moveYear(this.date, dir);
19005                     newViewDate = this.moveYear(this.viewDate, dir);
19006                 } else if (e.shiftKey){
19007                     newDate = this.moveMonth(this.date, dir);
19008                     newViewDate = this.moveMonth(this.viewDate, dir);
19009                 } else {
19010                     newDate = new Date(this.date);
19011                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19012                     newViewDate = new Date(this.viewDate);
19013                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19014                 }
19015                 if (this.dateWithinRange(newDate)){
19016                     this.date = newDate;
19017                     this.viewDate = newViewDate;
19018                     this.setValue(this.formatDate(this.date));
19019 //                    this.update();
19020                     e.preventDefault();
19021                     dateChanged = true;
19022                 }
19023                 break;
19024             case 13: // enter
19025                 this.setValue(this.formatDate(this.date));
19026                 this.hidePopup();
19027                 e.preventDefault();
19028                 break;
19029             case 9: // tab
19030                 this.setValue(this.formatDate(this.date));
19031                 this.hidePopup();
19032                 break;
19033             case 16: // shift
19034             case 17: // ctrl
19035             case 18: // alt
19036                 break;
19037             default :
19038                 this.hide();
19039                 
19040         }
19041     },
19042     
19043     
19044     onClick: function(e) 
19045     {
19046         e.stopPropagation();
19047         e.preventDefault();
19048         
19049         var target = e.getTarget();
19050         
19051         if(target.nodeName.toLowerCase() === 'i'){
19052             target = Roo.get(target).dom.parentNode;
19053         }
19054         
19055         var nodeName = target.nodeName;
19056         var className = target.className;
19057         var html = target.innerHTML;
19058         //Roo.log(nodeName);
19059         
19060         switch(nodeName.toLowerCase()) {
19061             case 'th':
19062                 switch(className) {
19063                     case 'switch':
19064                         this.showMode(1);
19065                         break;
19066                     case 'prev':
19067                     case 'next':
19068                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19069                         switch(this.viewMode){
19070                                 case 0:
19071                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19072                                         break;
19073                                 case 1:
19074                                 case 2:
19075                                         this.viewDate = this.moveYear(this.viewDate, dir);
19076                                         break;
19077                         }
19078                         this.fill();
19079                         break;
19080                     case 'today':
19081                         var date = new Date();
19082                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19083 //                        this.fill()
19084                         this.setValue(this.formatDate(this.date));
19085                         
19086                         this.hidePopup();
19087                         break;
19088                 }
19089                 break;
19090             case 'span':
19091                 if (className.indexOf('disabled') < 0) {
19092                     this.viewDate.setUTCDate(1);
19093                     if (className.indexOf('month') > -1) {
19094                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19095                     } else {
19096                         var year = parseInt(html, 10) || 0;
19097                         this.viewDate.setUTCFullYear(year);
19098                         
19099                     }
19100                     
19101                     if(this.singleMode){
19102                         this.setValue(this.formatDate(this.viewDate));
19103                         this.hidePopup();
19104                         return;
19105                     }
19106                     
19107                     this.showMode(-1);
19108                     this.fill();
19109                 }
19110                 break;
19111                 
19112             case 'td':
19113                 //Roo.log(className);
19114                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19115                     var day = parseInt(html, 10) || 1;
19116                     var year = this.viewDate.getUTCFullYear(),
19117                         month = this.viewDate.getUTCMonth();
19118
19119                     if (className.indexOf('old') > -1) {
19120                         if(month === 0 ){
19121                             month = 11;
19122                             year -= 1;
19123                         }else{
19124                             month -= 1;
19125                         }
19126                     } else if (className.indexOf('new') > -1) {
19127                         if (month == 11) {
19128                             month = 0;
19129                             year += 1;
19130                         } else {
19131                             month += 1;
19132                         }
19133                     }
19134                     //Roo.log([year,month,day]);
19135                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19136                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19137 //                    this.fill();
19138                     //Roo.log(this.formatDate(this.date));
19139                     this.setValue(this.formatDate(this.date));
19140                     this.hidePopup();
19141                 }
19142                 break;
19143         }
19144     },
19145     
19146     setStartDate: function(startDate)
19147     {
19148         this.startDate = startDate || -Infinity;
19149         if (this.startDate !== -Infinity) {
19150             this.startDate = this.parseDate(this.startDate);
19151         }
19152         this.update();
19153         this.updateNavArrows();
19154     },
19155
19156     setEndDate: function(endDate)
19157     {
19158         this.endDate = endDate || Infinity;
19159         if (this.endDate !== Infinity) {
19160             this.endDate = this.parseDate(this.endDate);
19161         }
19162         this.update();
19163         this.updateNavArrows();
19164     },
19165     
19166     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19167     {
19168         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19169         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19170             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19171         }
19172         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19173             return parseInt(d, 10);
19174         });
19175         this.update();
19176         this.updateNavArrows();
19177     },
19178     
19179     updateNavArrows: function() 
19180     {
19181         if(this.singleMode){
19182             return;
19183         }
19184         
19185         var d = new Date(this.viewDate),
19186         year = d.getUTCFullYear(),
19187         month = d.getUTCMonth();
19188         
19189         Roo.each(this.picker().select('.prev', true).elements, function(v){
19190             v.show();
19191             switch (this.viewMode) {
19192                 case 0:
19193
19194                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19195                         v.hide();
19196                     }
19197                     break;
19198                 case 1:
19199                 case 2:
19200                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19201                         v.hide();
19202                     }
19203                     break;
19204             }
19205         });
19206         
19207         Roo.each(this.picker().select('.next', true).elements, function(v){
19208             v.show();
19209             switch (this.viewMode) {
19210                 case 0:
19211
19212                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19213                         v.hide();
19214                     }
19215                     break;
19216                 case 1:
19217                 case 2:
19218                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19219                         v.hide();
19220                     }
19221                     break;
19222             }
19223         })
19224     },
19225     
19226     moveMonth: function(date, dir)
19227     {
19228         if (!dir) {
19229             return date;
19230         }
19231         var new_date = new Date(date.valueOf()),
19232         day = new_date.getUTCDate(),
19233         month = new_date.getUTCMonth(),
19234         mag = Math.abs(dir),
19235         new_month, test;
19236         dir = dir > 0 ? 1 : -1;
19237         if (mag == 1){
19238             test = dir == -1
19239             // If going back one month, make sure month is not current month
19240             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19241             ? function(){
19242                 return new_date.getUTCMonth() == month;
19243             }
19244             // If going forward one month, make sure month is as expected
19245             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19246             : function(){
19247                 return new_date.getUTCMonth() != new_month;
19248             };
19249             new_month = month + dir;
19250             new_date.setUTCMonth(new_month);
19251             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19252             if (new_month < 0 || new_month > 11) {
19253                 new_month = (new_month + 12) % 12;
19254             }
19255         } else {
19256             // For magnitudes >1, move one month at a time...
19257             for (var i=0; i<mag; i++) {
19258                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19259                 new_date = this.moveMonth(new_date, dir);
19260             }
19261             // ...then reset the day, keeping it in the new month
19262             new_month = new_date.getUTCMonth();
19263             new_date.setUTCDate(day);
19264             test = function(){
19265                 return new_month != new_date.getUTCMonth();
19266             };
19267         }
19268         // Common date-resetting loop -- if date is beyond end of month, make it
19269         // end of month
19270         while (test()){
19271             new_date.setUTCDate(--day);
19272             new_date.setUTCMonth(new_month);
19273         }
19274         return new_date;
19275     },
19276
19277     moveYear: function(date, dir)
19278     {
19279         return this.moveMonth(date, dir*12);
19280     },
19281
19282     dateWithinRange: function(date)
19283     {
19284         return date >= this.startDate && date <= this.endDate;
19285     },
19286
19287     
19288     remove: function() 
19289     {
19290         this.picker().remove();
19291     },
19292     
19293     validateValue : function(value)
19294     {
19295         if(this.getVisibilityEl().hasClass('hidden')){
19296             return true;
19297         }
19298         
19299         if(value.length < 1)  {
19300             if(this.allowBlank){
19301                 return true;
19302             }
19303             return false;
19304         }
19305         
19306         if(value.length < this.minLength){
19307             return false;
19308         }
19309         if(value.length > this.maxLength){
19310             return false;
19311         }
19312         if(this.vtype){
19313             var vt = Roo.form.VTypes;
19314             if(!vt[this.vtype](value, this)){
19315                 return false;
19316             }
19317         }
19318         if(typeof this.validator == "function"){
19319             var msg = this.validator(value);
19320             if(msg !== true){
19321                 return false;
19322             }
19323         }
19324         
19325         if(this.regex && !this.regex.test(value)){
19326             return false;
19327         }
19328         
19329         if(typeof(this.parseDate(value)) == 'undefined'){
19330             return false;
19331         }
19332         
19333         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19334             return false;
19335         }      
19336         
19337         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19338             return false;
19339         } 
19340         
19341         
19342         return true;
19343     },
19344     
19345     reset : function()
19346     {
19347         this.date = this.viewDate = '';
19348         
19349         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19350     }
19351    
19352 });
19353
19354 Roo.apply(Roo.bootstrap.DateField,  {
19355     
19356     head : {
19357         tag: 'thead',
19358         cn: [
19359         {
19360             tag: 'tr',
19361             cn: [
19362             {
19363                 tag: 'th',
19364                 cls: 'prev',
19365                 html: '<i class="fa fa-arrow-left"/>'
19366             },
19367             {
19368                 tag: 'th',
19369                 cls: 'switch',
19370                 colspan: '5'
19371             },
19372             {
19373                 tag: 'th',
19374                 cls: 'next',
19375                 html: '<i class="fa fa-arrow-right"/>'
19376             }
19377
19378             ]
19379         }
19380         ]
19381     },
19382     
19383     content : {
19384         tag: 'tbody',
19385         cn: [
19386         {
19387             tag: 'tr',
19388             cn: [
19389             {
19390                 tag: 'td',
19391                 colspan: '7'
19392             }
19393             ]
19394         }
19395         ]
19396     },
19397     
19398     footer : {
19399         tag: 'tfoot',
19400         cn: [
19401         {
19402             tag: 'tr',
19403             cn: [
19404             {
19405                 tag: 'th',
19406                 colspan: '7',
19407                 cls: 'today'
19408             }
19409                     
19410             ]
19411         }
19412         ]
19413     },
19414     
19415     dates:{
19416         en: {
19417             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19418             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19419             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19420             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19421             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19422             today: "Today"
19423         }
19424     },
19425     
19426     modes: [
19427     {
19428         clsName: 'days',
19429         navFnc: 'Month',
19430         navStep: 1
19431     },
19432     {
19433         clsName: 'months',
19434         navFnc: 'FullYear',
19435         navStep: 1
19436     },
19437     {
19438         clsName: 'years',
19439         navFnc: 'FullYear',
19440         navStep: 10
19441     }]
19442 });
19443
19444 Roo.apply(Roo.bootstrap.DateField,  {
19445   
19446     template : {
19447         tag: 'div',
19448         cls: 'datepicker dropdown-menu roo-dynamic',
19449         cn: [
19450         {
19451             tag: 'div',
19452             cls: 'datepicker-days',
19453             cn: [
19454             {
19455                 tag: 'table',
19456                 cls: 'table-condensed',
19457                 cn:[
19458                 Roo.bootstrap.DateField.head,
19459                 {
19460                     tag: 'tbody'
19461                 },
19462                 Roo.bootstrap.DateField.footer
19463                 ]
19464             }
19465             ]
19466         },
19467         {
19468             tag: 'div',
19469             cls: 'datepicker-months',
19470             cn: [
19471             {
19472                 tag: 'table',
19473                 cls: 'table-condensed',
19474                 cn:[
19475                 Roo.bootstrap.DateField.head,
19476                 Roo.bootstrap.DateField.content,
19477                 Roo.bootstrap.DateField.footer
19478                 ]
19479             }
19480             ]
19481         },
19482         {
19483             tag: 'div',
19484             cls: 'datepicker-years',
19485             cn: [
19486             {
19487                 tag: 'table',
19488                 cls: 'table-condensed',
19489                 cn:[
19490                 Roo.bootstrap.DateField.head,
19491                 Roo.bootstrap.DateField.content,
19492                 Roo.bootstrap.DateField.footer
19493                 ]
19494             }
19495             ]
19496         }
19497         ]
19498     }
19499 });
19500
19501  
19502
19503  /*
19504  * - LGPL
19505  *
19506  * TimeField
19507  * 
19508  */
19509
19510 /**
19511  * @class Roo.bootstrap.TimeField
19512  * @extends Roo.bootstrap.Input
19513  * Bootstrap DateField class
19514  * 
19515  * 
19516  * @constructor
19517  * Create a new TimeField
19518  * @param {Object} config The config object
19519  */
19520
19521 Roo.bootstrap.TimeField = function(config){
19522     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19523     this.addEvents({
19524             /**
19525              * @event show
19526              * Fires when this field show.
19527              * @param {Roo.bootstrap.DateField} thisthis
19528              * @param {Mixed} date The date value
19529              */
19530             show : true,
19531             /**
19532              * @event show
19533              * Fires when this field hide.
19534              * @param {Roo.bootstrap.DateField} this
19535              * @param {Mixed} date The date value
19536              */
19537             hide : true,
19538             /**
19539              * @event select
19540              * Fires when select a date.
19541              * @param {Roo.bootstrap.DateField} this
19542              * @param {Mixed} date The date value
19543              */
19544             select : true
19545         });
19546 };
19547
19548 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19549     
19550     /**
19551      * @cfg {String} format
19552      * The default time format string which can be overriden for localization support.  The format must be
19553      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19554      */
19555     format : "H:i",
19556        
19557     onRender: function(ct, position)
19558     {
19559         
19560         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19561                 
19562         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19563         
19564         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19565         
19566         this.pop = this.picker().select('>.datepicker-time',true).first();
19567         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19568         
19569         this.picker().on('mousedown', this.onMousedown, this);
19570         this.picker().on('click', this.onClick, this);
19571         
19572         this.picker().addClass('datepicker-dropdown');
19573     
19574         this.fillTime();
19575         this.update();
19576             
19577         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19578         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19579         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19580         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19581         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19582         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19583
19584     },
19585     
19586     fireKey: function(e){
19587         if (!this.picker().isVisible()){
19588             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19589                 this.show();
19590             }
19591             return;
19592         }
19593
19594         e.preventDefault();
19595         
19596         switch(e.keyCode){
19597             case 27: // escape
19598                 this.hide();
19599                 break;
19600             case 37: // left
19601             case 39: // right
19602                 this.onTogglePeriod();
19603                 break;
19604             case 38: // up
19605                 this.onIncrementMinutes();
19606                 break;
19607             case 40: // down
19608                 this.onDecrementMinutes();
19609                 break;
19610             case 13: // enter
19611             case 9: // tab
19612                 this.setTime();
19613                 break;
19614         }
19615     },
19616     
19617     onClick: function(e) {
19618         e.stopPropagation();
19619         e.preventDefault();
19620     },
19621     
19622     picker : function()
19623     {
19624         return this.el.select('.datepicker', true).first();
19625     },
19626     
19627     fillTime: function()
19628     {    
19629         var time = this.pop.select('tbody', true).first();
19630         
19631         time.dom.innerHTML = '';
19632         
19633         time.createChild({
19634             tag: 'tr',
19635             cn: [
19636                 {
19637                     tag: 'td',
19638                     cn: [
19639                         {
19640                             tag: 'a',
19641                             href: '#',
19642                             cls: 'btn',
19643                             cn: [
19644                                 {
19645                                     tag: 'span',
19646                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19647                                 }
19648                             ]
19649                         } 
19650                     ]
19651                 },
19652                 {
19653                     tag: 'td',
19654                     cls: 'separator'
19655                 },
19656                 {
19657                     tag: 'td',
19658                     cn: [
19659                         {
19660                             tag: 'a',
19661                             href: '#',
19662                             cls: 'btn',
19663                             cn: [
19664                                 {
19665                                     tag: 'span',
19666                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19667                                 }
19668                             ]
19669                         }
19670                     ]
19671                 },
19672                 {
19673                     tag: 'td',
19674                     cls: 'separator'
19675                 }
19676             ]
19677         });
19678         
19679         time.createChild({
19680             tag: 'tr',
19681             cn: [
19682                 {
19683                     tag: 'td',
19684                     cn: [
19685                         {
19686                             tag: 'span',
19687                             cls: 'timepicker-hour',
19688                             html: '00'
19689                         }  
19690                     ]
19691                 },
19692                 {
19693                     tag: 'td',
19694                     cls: 'separator',
19695                     html: ':'
19696                 },
19697                 {
19698                     tag: 'td',
19699                     cn: [
19700                         {
19701                             tag: 'span',
19702                             cls: 'timepicker-minute',
19703                             html: '00'
19704                         }  
19705                     ]
19706                 },
19707                 {
19708                     tag: 'td',
19709                     cls: 'separator'
19710                 },
19711                 {
19712                     tag: 'td',
19713                     cn: [
19714                         {
19715                             tag: 'button',
19716                             type: 'button',
19717                             cls: 'btn btn-primary period',
19718                             html: 'AM'
19719                             
19720                         }
19721                     ]
19722                 }
19723             ]
19724         });
19725         
19726         time.createChild({
19727             tag: 'tr',
19728             cn: [
19729                 {
19730                     tag: 'td',
19731                     cn: [
19732                         {
19733                             tag: 'a',
19734                             href: '#',
19735                             cls: 'btn',
19736                             cn: [
19737                                 {
19738                                     tag: 'span',
19739                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19740                                 }
19741                             ]
19742                         }
19743                     ]
19744                 },
19745                 {
19746                     tag: 'td',
19747                     cls: 'separator'
19748                 },
19749                 {
19750                     tag: 'td',
19751                     cn: [
19752                         {
19753                             tag: 'a',
19754                             href: '#',
19755                             cls: 'btn',
19756                             cn: [
19757                                 {
19758                                     tag: 'span',
19759                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19760                                 }
19761                             ]
19762                         }
19763                     ]
19764                 },
19765                 {
19766                     tag: 'td',
19767                     cls: 'separator'
19768                 }
19769             ]
19770         });
19771         
19772     },
19773     
19774     update: function()
19775     {
19776         
19777         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19778         
19779         this.fill();
19780     },
19781     
19782     fill: function() 
19783     {
19784         var hours = this.time.getHours();
19785         var minutes = this.time.getMinutes();
19786         var period = 'AM';
19787         
19788         if(hours > 11){
19789             period = 'PM';
19790         }
19791         
19792         if(hours == 0){
19793             hours = 12;
19794         }
19795         
19796         
19797         if(hours > 12){
19798             hours = hours - 12;
19799         }
19800         
19801         if(hours < 10){
19802             hours = '0' + hours;
19803         }
19804         
19805         if(minutes < 10){
19806             minutes = '0' + minutes;
19807         }
19808         
19809         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19810         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19811         this.pop.select('button', true).first().dom.innerHTML = period;
19812         
19813     },
19814     
19815     place: function()
19816     {   
19817         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19818         
19819         var cls = ['bottom'];
19820         
19821         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19822             cls.pop();
19823             cls.push('top');
19824         }
19825         
19826         cls.push('right');
19827         
19828         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19829             cls.pop();
19830             cls.push('left');
19831         }
19832         
19833         this.picker().addClass(cls.join('-'));
19834         
19835         var _this = this;
19836         
19837         Roo.each(cls, function(c){
19838             if(c == 'bottom'){
19839                 _this.picker().setTop(_this.inputEl().getHeight());
19840                 return;
19841             }
19842             if(c == 'top'){
19843                 _this.picker().setTop(0 - _this.picker().getHeight());
19844                 return;
19845             }
19846             
19847             if(c == 'left'){
19848                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19849                 return;
19850             }
19851             if(c == 'right'){
19852                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19853                 return;
19854             }
19855         });
19856         
19857     },
19858   
19859     onFocus : function()
19860     {
19861         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19862         this.show();
19863     },
19864     
19865     onBlur : function()
19866     {
19867         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19868         this.hide();
19869     },
19870     
19871     show : function()
19872     {
19873         this.picker().show();
19874         this.pop.show();
19875         this.update();
19876         this.place();
19877         
19878         this.fireEvent('show', this, this.date);
19879     },
19880     
19881     hide : function()
19882     {
19883         this.picker().hide();
19884         this.pop.hide();
19885         
19886         this.fireEvent('hide', this, this.date);
19887     },
19888     
19889     setTime : function()
19890     {
19891         this.hide();
19892         this.setValue(this.time.format(this.format));
19893         
19894         this.fireEvent('select', this, this.date);
19895         
19896         
19897     },
19898     
19899     onMousedown: function(e){
19900         e.stopPropagation();
19901         e.preventDefault();
19902     },
19903     
19904     onIncrementHours: function()
19905     {
19906         Roo.log('onIncrementHours');
19907         this.time = this.time.add(Date.HOUR, 1);
19908         this.update();
19909         
19910     },
19911     
19912     onDecrementHours: function()
19913     {
19914         Roo.log('onDecrementHours');
19915         this.time = this.time.add(Date.HOUR, -1);
19916         this.update();
19917     },
19918     
19919     onIncrementMinutes: function()
19920     {
19921         Roo.log('onIncrementMinutes');
19922         this.time = this.time.add(Date.MINUTE, 1);
19923         this.update();
19924     },
19925     
19926     onDecrementMinutes: function()
19927     {
19928         Roo.log('onDecrementMinutes');
19929         this.time = this.time.add(Date.MINUTE, -1);
19930         this.update();
19931     },
19932     
19933     onTogglePeriod: function()
19934     {
19935         Roo.log('onTogglePeriod');
19936         this.time = this.time.add(Date.HOUR, 12);
19937         this.update();
19938     }
19939     
19940    
19941 });
19942
19943 Roo.apply(Roo.bootstrap.TimeField,  {
19944     
19945     content : {
19946         tag: 'tbody',
19947         cn: [
19948             {
19949                 tag: 'tr',
19950                 cn: [
19951                 {
19952                     tag: 'td',
19953                     colspan: '7'
19954                 }
19955                 ]
19956             }
19957         ]
19958     },
19959     
19960     footer : {
19961         tag: 'tfoot',
19962         cn: [
19963             {
19964                 tag: 'tr',
19965                 cn: [
19966                 {
19967                     tag: 'th',
19968                     colspan: '7',
19969                     cls: '',
19970                     cn: [
19971                         {
19972                             tag: 'button',
19973                             cls: 'btn btn-info ok',
19974                             html: 'OK'
19975                         }
19976                     ]
19977                 }
19978
19979                 ]
19980             }
19981         ]
19982     }
19983 });
19984
19985 Roo.apply(Roo.bootstrap.TimeField,  {
19986   
19987     template : {
19988         tag: 'div',
19989         cls: 'datepicker dropdown-menu',
19990         cn: [
19991             {
19992                 tag: 'div',
19993                 cls: 'datepicker-time',
19994                 cn: [
19995                 {
19996                     tag: 'table',
19997                     cls: 'table-condensed',
19998                     cn:[
19999                     Roo.bootstrap.TimeField.content,
20000                     Roo.bootstrap.TimeField.footer
20001                     ]
20002                 }
20003                 ]
20004             }
20005         ]
20006     }
20007 });
20008
20009  
20010
20011  /*
20012  * - LGPL
20013  *
20014  * MonthField
20015  * 
20016  */
20017
20018 /**
20019  * @class Roo.bootstrap.MonthField
20020  * @extends Roo.bootstrap.Input
20021  * Bootstrap MonthField class
20022  * 
20023  * @cfg {String} language default en
20024  * 
20025  * @constructor
20026  * Create a new MonthField
20027  * @param {Object} config The config object
20028  */
20029
20030 Roo.bootstrap.MonthField = function(config){
20031     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20032     
20033     this.addEvents({
20034         /**
20035          * @event show
20036          * Fires when this field show.
20037          * @param {Roo.bootstrap.MonthField} this
20038          * @param {Mixed} date The date value
20039          */
20040         show : true,
20041         /**
20042          * @event show
20043          * Fires when this field hide.
20044          * @param {Roo.bootstrap.MonthField} this
20045          * @param {Mixed} date The date value
20046          */
20047         hide : true,
20048         /**
20049          * @event select
20050          * Fires when select a date.
20051          * @param {Roo.bootstrap.MonthField} this
20052          * @param {String} oldvalue The old value
20053          * @param {String} newvalue The new value
20054          */
20055         select : true
20056     });
20057 };
20058
20059 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20060     
20061     onRender: function(ct, position)
20062     {
20063         
20064         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20065         
20066         this.language = this.language || 'en';
20067         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20068         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20069         
20070         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20071         this.isInline = false;
20072         this.isInput = true;
20073         this.component = this.el.select('.add-on', true).first() || false;
20074         this.component = (this.component && this.component.length === 0) ? false : this.component;
20075         this.hasInput = this.component && this.inputEL().length;
20076         
20077         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20078         
20079         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20080         
20081         this.picker().on('mousedown', this.onMousedown, this);
20082         this.picker().on('click', this.onClick, this);
20083         
20084         this.picker().addClass('datepicker-dropdown');
20085         
20086         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20087             v.setStyle('width', '189px');
20088         });
20089         
20090         this.fillMonths();
20091         
20092         this.update();
20093         
20094         if(this.isInline) {
20095             this.show();
20096         }
20097         
20098     },
20099     
20100     setValue: function(v, suppressEvent)
20101     {   
20102         var o = this.getValue();
20103         
20104         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20105         
20106         this.update();
20107
20108         if(suppressEvent !== true){
20109             this.fireEvent('select', this, o, v);
20110         }
20111         
20112     },
20113     
20114     getValue: function()
20115     {
20116         return this.value;
20117     },
20118     
20119     onClick: function(e) 
20120     {
20121         e.stopPropagation();
20122         e.preventDefault();
20123         
20124         var target = e.getTarget();
20125         
20126         if(target.nodeName.toLowerCase() === 'i'){
20127             target = Roo.get(target).dom.parentNode;
20128         }
20129         
20130         var nodeName = target.nodeName;
20131         var className = target.className;
20132         var html = target.innerHTML;
20133         
20134         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20135             return;
20136         }
20137         
20138         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20139         
20140         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20141         
20142         this.hide();
20143                         
20144     },
20145     
20146     picker : function()
20147     {
20148         return this.pickerEl;
20149     },
20150     
20151     fillMonths: function()
20152     {    
20153         var i = 0;
20154         var months = this.picker().select('>.datepicker-months td', true).first();
20155         
20156         months.dom.innerHTML = '';
20157         
20158         while (i < 12) {
20159             var month = {
20160                 tag: 'span',
20161                 cls: 'month',
20162                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20163             };
20164             
20165             months.createChild(month);
20166         }
20167         
20168     },
20169     
20170     update: function()
20171     {
20172         var _this = this;
20173         
20174         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20175             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20176         }
20177         
20178         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20179             e.removeClass('active');
20180             
20181             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20182                 e.addClass('active');
20183             }
20184         })
20185     },
20186     
20187     place: function()
20188     {
20189         if(this.isInline) {
20190             return;
20191         }
20192         
20193         this.picker().removeClass(['bottom', 'top']);
20194         
20195         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20196             /*
20197              * place to the top of element!
20198              *
20199              */
20200             
20201             this.picker().addClass('top');
20202             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20203             
20204             return;
20205         }
20206         
20207         this.picker().addClass('bottom');
20208         
20209         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20210     },
20211     
20212     onFocus : function()
20213     {
20214         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20215         this.show();
20216     },
20217     
20218     onBlur : function()
20219     {
20220         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20221         
20222         var d = this.inputEl().getValue();
20223         
20224         this.setValue(d);
20225                 
20226         this.hide();
20227     },
20228     
20229     show : function()
20230     {
20231         this.picker().show();
20232         this.picker().select('>.datepicker-months', true).first().show();
20233         this.update();
20234         this.place();
20235         
20236         this.fireEvent('show', this, this.date);
20237     },
20238     
20239     hide : function()
20240     {
20241         if(this.isInline) {
20242             return;
20243         }
20244         this.picker().hide();
20245         this.fireEvent('hide', this, this.date);
20246         
20247     },
20248     
20249     onMousedown: function(e)
20250     {
20251         e.stopPropagation();
20252         e.preventDefault();
20253     },
20254     
20255     keyup: function(e)
20256     {
20257         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20258         this.update();
20259     },
20260
20261     fireKey: function(e)
20262     {
20263         if (!this.picker().isVisible()){
20264             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20265                 this.show();
20266             }
20267             return;
20268         }
20269         
20270         var dir;
20271         
20272         switch(e.keyCode){
20273             case 27: // escape
20274                 this.hide();
20275                 e.preventDefault();
20276                 break;
20277             case 37: // left
20278             case 39: // right
20279                 dir = e.keyCode == 37 ? -1 : 1;
20280                 
20281                 this.vIndex = this.vIndex + dir;
20282                 
20283                 if(this.vIndex < 0){
20284                     this.vIndex = 0;
20285                 }
20286                 
20287                 if(this.vIndex > 11){
20288                     this.vIndex = 11;
20289                 }
20290                 
20291                 if(isNaN(this.vIndex)){
20292                     this.vIndex = 0;
20293                 }
20294                 
20295                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20296                 
20297                 break;
20298             case 38: // up
20299             case 40: // down
20300                 
20301                 dir = e.keyCode == 38 ? -1 : 1;
20302                 
20303                 this.vIndex = this.vIndex + dir * 4;
20304                 
20305                 if(this.vIndex < 0){
20306                     this.vIndex = 0;
20307                 }
20308                 
20309                 if(this.vIndex > 11){
20310                     this.vIndex = 11;
20311                 }
20312                 
20313                 if(isNaN(this.vIndex)){
20314                     this.vIndex = 0;
20315                 }
20316                 
20317                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20318                 break;
20319                 
20320             case 13: // enter
20321                 
20322                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20323                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20324                 }
20325                 
20326                 this.hide();
20327                 e.preventDefault();
20328                 break;
20329             case 9: // tab
20330                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20331                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20332                 }
20333                 this.hide();
20334                 break;
20335             case 16: // shift
20336             case 17: // ctrl
20337             case 18: // alt
20338                 break;
20339             default :
20340                 this.hide();
20341                 
20342         }
20343     },
20344     
20345     remove: function() 
20346     {
20347         this.picker().remove();
20348     }
20349    
20350 });
20351
20352 Roo.apply(Roo.bootstrap.MonthField,  {
20353     
20354     content : {
20355         tag: 'tbody',
20356         cn: [
20357         {
20358             tag: 'tr',
20359             cn: [
20360             {
20361                 tag: 'td',
20362                 colspan: '7'
20363             }
20364             ]
20365         }
20366         ]
20367     },
20368     
20369     dates:{
20370         en: {
20371             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20372             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20373         }
20374     }
20375 });
20376
20377 Roo.apply(Roo.bootstrap.MonthField,  {
20378   
20379     template : {
20380         tag: 'div',
20381         cls: 'datepicker dropdown-menu roo-dynamic',
20382         cn: [
20383             {
20384                 tag: 'div',
20385                 cls: 'datepicker-months',
20386                 cn: [
20387                 {
20388                     tag: 'table',
20389                     cls: 'table-condensed',
20390                     cn:[
20391                         Roo.bootstrap.DateField.content
20392                     ]
20393                 }
20394                 ]
20395             }
20396         ]
20397     }
20398 });
20399
20400  
20401
20402  
20403  /*
20404  * - LGPL
20405  *
20406  * CheckBox
20407  * 
20408  */
20409
20410 /**
20411  * @class Roo.bootstrap.CheckBox
20412  * @extends Roo.bootstrap.Input
20413  * Bootstrap CheckBox class
20414  * 
20415  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20416  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20417  * @cfg {String} boxLabel The text that appears beside the checkbox
20418  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20419  * @cfg {Boolean} checked initnal the element
20420  * @cfg {Boolean} inline inline the element (default false)
20421  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20422  * @cfg {String} tooltip label tooltip
20423  * 
20424  * @constructor
20425  * Create a new CheckBox
20426  * @param {Object} config The config object
20427  */
20428
20429 Roo.bootstrap.CheckBox = function(config){
20430     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20431    
20432     this.addEvents({
20433         /**
20434         * @event check
20435         * Fires when the element is checked or unchecked.
20436         * @param {Roo.bootstrap.CheckBox} this This input
20437         * @param {Boolean} checked The new checked value
20438         */
20439        check : true,
20440        /**
20441         * @event click
20442         * Fires when the element is click.
20443         * @param {Roo.bootstrap.CheckBox} this This input
20444         */
20445        click : true
20446     });
20447     
20448 };
20449
20450 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20451   
20452     inputType: 'checkbox',
20453     inputValue: 1,
20454     valueOff: 0,
20455     boxLabel: false,
20456     checked: false,
20457     weight : false,
20458     inline: false,
20459     tooltip : '',
20460     
20461     getAutoCreate : function()
20462     {
20463         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20464         
20465         var id = Roo.id();
20466         
20467         var cfg = {};
20468         
20469         cfg.cls = 'form-group ' + this.inputType; //input-group
20470         
20471         if(this.inline){
20472             cfg.cls += ' ' + this.inputType + '-inline';
20473         }
20474         
20475         var input =  {
20476             tag: 'input',
20477             id : id,
20478             type : this.inputType,
20479             value : this.inputValue,
20480             cls : 'roo-' + this.inputType, //'form-box',
20481             placeholder : this.placeholder || ''
20482             
20483         };
20484         
20485         if(this.inputType != 'radio'){
20486             var hidden =  {
20487                 tag: 'input',
20488                 type : 'hidden',
20489                 cls : 'roo-hidden-value',
20490                 value : this.checked ? this.inputValue : this.valueOff
20491             };
20492         }
20493         
20494             
20495         if (this.weight) { // Validity check?
20496             cfg.cls += " " + this.inputType + "-" + this.weight;
20497         }
20498         
20499         if (this.disabled) {
20500             input.disabled=true;
20501         }
20502         
20503         if(this.checked){
20504             input.checked = this.checked;
20505         }
20506         
20507         if (this.name) {
20508             
20509             input.name = this.name;
20510             
20511             if(this.inputType != 'radio'){
20512                 hidden.name = this.name;
20513                 input.name = '_hidden_' + this.name;
20514             }
20515         }
20516         
20517         if (this.size) {
20518             input.cls += ' input-' + this.size;
20519         }
20520         
20521         var settings=this;
20522         
20523         ['xs','sm','md','lg'].map(function(size){
20524             if (settings[size]) {
20525                 cfg.cls += ' col-' + size + '-' + settings[size];
20526             }
20527         });
20528         
20529         var inputblock = input;
20530          
20531         if (this.before || this.after) {
20532             
20533             inputblock = {
20534                 cls : 'input-group',
20535                 cn :  [] 
20536             };
20537             
20538             if (this.before) {
20539                 inputblock.cn.push({
20540                     tag :'span',
20541                     cls : 'input-group-addon',
20542                     html : this.before
20543                 });
20544             }
20545             
20546             inputblock.cn.push(input);
20547             
20548             if(this.inputType != 'radio'){
20549                 inputblock.cn.push(hidden);
20550             }
20551             
20552             if (this.after) {
20553                 inputblock.cn.push({
20554                     tag :'span',
20555                     cls : 'input-group-addon',
20556                     html : this.after
20557                 });
20558             }
20559             
20560         }
20561         
20562         if (align ==='left' && this.fieldLabel.length) {
20563 //                Roo.log("left and has label");
20564             cfg.cn = [
20565                 {
20566                     tag: 'label',
20567                     'for' :  id,
20568                     cls : 'control-label',
20569                     html : this.fieldLabel
20570                 },
20571                 {
20572                     cls : "", 
20573                     cn: [
20574                         inputblock
20575                     ]
20576                 }
20577             ];
20578             
20579             if(this.labelWidth > 12){
20580                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20581             }
20582             
20583             if(this.labelWidth < 13 && this.labelmd == 0){
20584                 this.labelmd = this.labelWidth;
20585             }
20586             
20587             if(this.labellg > 0){
20588                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20589                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20590             }
20591             
20592             if(this.labelmd > 0){
20593                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20594                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20595             }
20596             
20597             if(this.labelsm > 0){
20598                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20599                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20600             }
20601             
20602             if(this.labelxs > 0){
20603                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20604                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20605             }
20606             
20607         } else if ( this.fieldLabel.length) {
20608 //                Roo.log(" label");
20609                 cfg.cn = [
20610                    
20611                     {
20612                         tag: this.boxLabel ? 'span' : 'label',
20613                         'for': id,
20614                         cls: 'control-label box-input-label',
20615                         //cls : 'input-group-addon',
20616                         html : this.fieldLabel
20617                     },
20618                     
20619                     inputblock
20620                     
20621                 ];
20622
20623         } else {
20624             
20625 //                Roo.log(" no label && no align");
20626                 cfg.cn = [  inputblock ] ;
20627                 
20628                 
20629         }
20630         
20631         if(this.boxLabel){
20632              var boxLabelCfg = {
20633                 tag: 'label',
20634                 //'for': id, // box label is handled by onclick - so no for...
20635                 cls: 'box-label',
20636                 html: this.boxLabel
20637             };
20638             
20639             if(this.tooltip){
20640                 boxLabelCfg.tooltip = this.tooltip;
20641             }
20642              
20643             cfg.cn.push(boxLabelCfg);
20644         }
20645         
20646         if(this.inputType != 'radio'){
20647             cfg.cn.push(hidden);
20648         }
20649         
20650         return cfg;
20651         
20652     },
20653     
20654     /**
20655      * return the real input element.
20656      */
20657     inputEl: function ()
20658     {
20659         return this.el.select('input.roo-' + this.inputType,true).first();
20660     },
20661     hiddenEl: function ()
20662     {
20663         return this.el.select('input.roo-hidden-value',true).first();
20664     },
20665     
20666     labelEl: function()
20667     {
20668         return this.el.select('label.control-label',true).first();
20669     },
20670     /* depricated... */
20671     
20672     label: function()
20673     {
20674         return this.labelEl();
20675     },
20676     
20677     boxLabelEl: function()
20678     {
20679         return this.el.select('label.box-label',true).first();
20680     },
20681     
20682     initEvents : function()
20683     {
20684 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20685         
20686         this.inputEl().on('click', this.onClick,  this);
20687         
20688         if (this.boxLabel) { 
20689             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20690         }
20691         
20692         this.startValue = this.getValue();
20693         
20694         if(this.groupId){
20695             Roo.bootstrap.CheckBox.register(this);
20696         }
20697     },
20698     
20699     onClick : function(e)
20700     {   
20701         if(this.fireEvent('click', this, e) !== false){
20702             this.setChecked(!this.checked);
20703         }
20704         
20705     },
20706     
20707     setChecked : function(state,suppressEvent)
20708     {
20709         this.startValue = this.getValue();
20710
20711         if(this.inputType == 'radio'){
20712             
20713             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20714                 e.dom.checked = false;
20715             });
20716             
20717             this.inputEl().dom.checked = true;
20718             
20719             this.inputEl().dom.value = this.inputValue;
20720             
20721             if(suppressEvent !== true){
20722                 this.fireEvent('check', this, true);
20723             }
20724             
20725             this.validate();
20726             
20727             return;
20728         }
20729         
20730         this.checked = state;
20731         
20732         this.inputEl().dom.checked = state;
20733         
20734         
20735         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20736         
20737         if(suppressEvent !== true){
20738             this.fireEvent('check', this, state);
20739         }
20740         
20741         this.validate();
20742     },
20743     
20744     getValue : function()
20745     {
20746         if(this.inputType == 'radio'){
20747             return this.getGroupValue();
20748         }
20749         
20750         return this.hiddenEl().dom.value;
20751         
20752     },
20753     
20754     getGroupValue : function()
20755     {
20756         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20757             return '';
20758         }
20759         
20760         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20761     },
20762     
20763     setValue : function(v,suppressEvent)
20764     {
20765         if(this.inputType == 'radio'){
20766             this.setGroupValue(v, suppressEvent);
20767             return;
20768         }
20769         
20770         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20771         
20772         this.validate();
20773     },
20774     
20775     setGroupValue : function(v, suppressEvent)
20776     {
20777         this.startValue = this.getValue();
20778         
20779         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20780             e.dom.checked = false;
20781             
20782             if(e.dom.value == v){
20783                 e.dom.checked = true;
20784             }
20785         });
20786         
20787         if(suppressEvent !== true){
20788             this.fireEvent('check', this, true);
20789         }
20790
20791         this.validate();
20792         
20793         return;
20794     },
20795     
20796     validate : function()
20797     {
20798         if(this.getVisibilityEl().hasClass('hidden')){
20799             return true;
20800         }
20801         
20802         if(
20803                 this.disabled || 
20804                 (this.inputType == 'radio' && this.validateRadio()) ||
20805                 (this.inputType == 'checkbox' && this.validateCheckbox())
20806         ){
20807             this.markValid();
20808             return true;
20809         }
20810         
20811         this.markInvalid();
20812         return false;
20813     },
20814     
20815     validateRadio : function()
20816     {
20817         if(this.getVisibilityEl().hasClass('hidden')){
20818             return true;
20819         }
20820         
20821         if(this.allowBlank){
20822             return true;
20823         }
20824         
20825         var valid = false;
20826         
20827         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20828             if(!e.dom.checked){
20829                 return;
20830             }
20831             
20832             valid = true;
20833             
20834             return false;
20835         });
20836         
20837         return valid;
20838     },
20839     
20840     validateCheckbox : function()
20841     {
20842         if(!this.groupId){
20843             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20844             //return (this.getValue() == this.inputValue) ? true : false;
20845         }
20846         
20847         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20848         
20849         if(!group){
20850             return false;
20851         }
20852         
20853         var r = false;
20854         
20855         for(var i in group){
20856             if(group[i].el.isVisible(true)){
20857                 r = false;
20858                 break;
20859             }
20860             
20861             r = true;
20862         }
20863         
20864         for(var i in group){
20865             if(r){
20866                 break;
20867             }
20868             
20869             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20870         }
20871         
20872         return r;
20873     },
20874     
20875     /**
20876      * Mark this field as valid
20877      */
20878     markValid : function()
20879     {
20880         var _this = this;
20881         
20882         this.fireEvent('valid', this);
20883         
20884         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20885         
20886         if(this.groupId){
20887             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20888         }
20889         
20890         if(label){
20891             label.markValid();
20892         }
20893
20894         if(this.inputType == 'radio'){
20895             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20896                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20897                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20898             });
20899             
20900             return;
20901         }
20902
20903         if(!this.groupId){
20904             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20905             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20906             return;
20907         }
20908         
20909         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20910         
20911         if(!group){
20912             return;
20913         }
20914         
20915         for(var i in group){
20916             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20917             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20918         }
20919     },
20920     
20921      /**
20922      * Mark this field as invalid
20923      * @param {String} msg The validation message
20924      */
20925     markInvalid : function(msg)
20926     {
20927         if(this.allowBlank){
20928             return;
20929         }
20930         
20931         var _this = this;
20932         
20933         this.fireEvent('invalid', this, msg);
20934         
20935         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20936         
20937         if(this.groupId){
20938             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20939         }
20940         
20941         if(label){
20942             label.markInvalid();
20943         }
20944             
20945         if(this.inputType == 'radio'){
20946             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20947                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20948                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20949             });
20950             
20951             return;
20952         }
20953         
20954         if(!this.groupId){
20955             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20956             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20957             return;
20958         }
20959         
20960         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20961         
20962         if(!group){
20963             return;
20964         }
20965         
20966         for(var i in group){
20967             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20968             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20969         }
20970         
20971     },
20972     
20973     clearInvalid : function()
20974     {
20975         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20976         
20977         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20978         
20979         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20980         
20981         if (label && label.iconEl) {
20982             label.iconEl.removeClass(label.validClass);
20983             label.iconEl.removeClass(label.invalidClass);
20984         }
20985     },
20986     
20987     disable : function()
20988     {
20989         if(this.inputType != 'radio'){
20990             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20991             return;
20992         }
20993         
20994         var _this = this;
20995         
20996         if(this.rendered){
20997             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20998                 _this.getActionEl().addClass(this.disabledClass);
20999                 e.dom.disabled = true;
21000             });
21001         }
21002         
21003         this.disabled = true;
21004         this.fireEvent("disable", this);
21005         return this;
21006     },
21007
21008     enable : function()
21009     {
21010         if(this.inputType != 'radio'){
21011             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21012             return;
21013         }
21014         
21015         var _this = this;
21016         
21017         if(this.rendered){
21018             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21019                 _this.getActionEl().removeClass(this.disabledClass);
21020                 e.dom.disabled = false;
21021             });
21022         }
21023         
21024         this.disabled = false;
21025         this.fireEvent("enable", this);
21026         return this;
21027     },
21028     
21029     setBoxLabel : function(v)
21030     {
21031         this.boxLabel = v;
21032         
21033         if(this.rendered){
21034             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21035         }
21036     }
21037
21038 });
21039
21040 Roo.apply(Roo.bootstrap.CheckBox, {
21041     
21042     groups: {},
21043     
21044      /**
21045     * register a CheckBox Group
21046     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21047     */
21048     register : function(checkbox)
21049     {
21050         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21051             this.groups[checkbox.groupId] = {};
21052         }
21053         
21054         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21055             return;
21056         }
21057         
21058         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21059         
21060     },
21061     /**
21062     * fetch a CheckBox Group based on the group ID
21063     * @param {string} the group ID
21064     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21065     */
21066     get: function(groupId) {
21067         if (typeof(this.groups[groupId]) == 'undefined') {
21068             return false;
21069         }
21070         
21071         return this.groups[groupId] ;
21072     }
21073     
21074     
21075 });
21076 /*
21077  * - LGPL
21078  *
21079  * RadioItem
21080  * 
21081  */
21082
21083 /**
21084  * @class Roo.bootstrap.Radio
21085  * @extends Roo.bootstrap.Component
21086  * Bootstrap Radio class
21087  * @cfg {String} boxLabel - the label associated
21088  * @cfg {String} value - the value of radio
21089  * 
21090  * @constructor
21091  * Create a new Radio
21092  * @param {Object} config The config object
21093  */
21094 Roo.bootstrap.Radio = function(config){
21095     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21096     
21097 };
21098
21099 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21100     
21101     boxLabel : '',
21102     
21103     value : '',
21104     
21105     getAutoCreate : function()
21106     {
21107         var cfg = {
21108             tag : 'div',
21109             cls : 'form-group radio',
21110             cn : [
21111                 {
21112                     tag : 'label',
21113                     cls : 'box-label',
21114                     html : this.boxLabel
21115                 }
21116             ]
21117         };
21118         
21119         return cfg;
21120     },
21121     
21122     initEvents : function() 
21123     {
21124         this.parent().register(this);
21125         
21126         this.el.on('click', this.onClick, this);
21127         
21128     },
21129     
21130     onClick : function(e)
21131     {
21132         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21133             this.setChecked(true);
21134         }
21135     },
21136     
21137     setChecked : function(state, suppressEvent)
21138     {
21139         this.parent().setValue(this.value, suppressEvent);
21140         
21141     },
21142     
21143     setBoxLabel : function(v)
21144     {
21145         this.boxLabel = v;
21146         
21147         if(this.rendered){
21148             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21149         }
21150     }
21151     
21152 });
21153  
21154
21155  /*
21156  * - LGPL
21157  *
21158  * Input
21159  * 
21160  */
21161
21162 /**
21163  * @class Roo.bootstrap.SecurePass
21164  * @extends Roo.bootstrap.Input
21165  * Bootstrap SecurePass class
21166  *
21167  * 
21168  * @constructor
21169  * Create a new SecurePass
21170  * @param {Object} config The config object
21171  */
21172  
21173 Roo.bootstrap.SecurePass = function (config) {
21174     // these go here, so the translation tool can replace them..
21175     this.errors = {
21176         PwdEmpty: "Please type a password, and then retype it to confirm.",
21177         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21178         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21179         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21180         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21181         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21182         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21183         TooWeak: "Your password is Too Weak."
21184     },
21185     this.meterLabel = "Password strength:";
21186     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21187     this.meterClass = [
21188         "roo-password-meter-tooweak", 
21189         "roo-password-meter-weak", 
21190         "roo-password-meter-medium", 
21191         "roo-password-meter-strong", 
21192         "roo-password-meter-grey"
21193     ];
21194     
21195     this.errors = {};
21196     
21197     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21198 }
21199
21200 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21201     /**
21202      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21203      * {
21204      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21205      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21206      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21207      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21208      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21209      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21210      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21211      * })
21212      */
21213     // private
21214     
21215     meterWidth: 300,
21216     errorMsg :'',    
21217     errors: false,
21218     imageRoot: '/',
21219     /**
21220      * @cfg {String/Object} Label for the strength meter (defaults to
21221      * 'Password strength:')
21222      */
21223     // private
21224     meterLabel: '',
21225     /**
21226      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21227      * ['Weak', 'Medium', 'Strong'])
21228      */
21229     // private    
21230     pwdStrengths: false,    
21231     // private
21232     strength: 0,
21233     // private
21234     _lastPwd: null,
21235     // private
21236     kCapitalLetter: 0,
21237     kSmallLetter: 1,
21238     kDigit: 2,
21239     kPunctuation: 3,
21240     
21241     insecure: false,
21242     // private
21243     initEvents: function ()
21244     {
21245         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21246
21247         if (this.el.is('input[type=password]') && Roo.isSafari) {
21248             this.el.on('keydown', this.SafariOnKeyDown, this);
21249         }
21250
21251         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21252     },
21253     // private
21254     onRender: function (ct, position)
21255     {
21256         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21257         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21258         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21259
21260         this.trigger.createChild({
21261                    cn: [
21262                     {
21263                     //id: 'PwdMeter',
21264                     tag: 'div',
21265                     cls: 'roo-password-meter-grey col-xs-12',
21266                     style: {
21267                         //width: 0,
21268                         //width: this.meterWidth + 'px'                                                
21269                         }
21270                     },
21271                     {                            
21272                          cls: 'roo-password-meter-text'                          
21273                     }
21274                 ]            
21275         });
21276
21277          
21278         if (this.hideTrigger) {
21279             this.trigger.setDisplayed(false);
21280         }
21281         this.setSize(this.width || '', this.height || '');
21282     },
21283     // private
21284     onDestroy: function ()
21285     {
21286         if (this.trigger) {
21287             this.trigger.removeAllListeners();
21288             this.trigger.remove();
21289         }
21290         if (this.wrap) {
21291             this.wrap.remove();
21292         }
21293         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21294     },
21295     // private
21296     checkStrength: function ()
21297     {
21298         var pwd = this.inputEl().getValue();
21299         if (pwd == this._lastPwd) {
21300             return;
21301         }
21302
21303         var strength;
21304         if (this.ClientSideStrongPassword(pwd)) {
21305             strength = 3;
21306         } else if (this.ClientSideMediumPassword(pwd)) {
21307             strength = 2;
21308         } else if (this.ClientSideWeakPassword(pwd)) {
21309             strength = 1;
21310         } else {
21311             strength = 0;
21312         }
21313         
21314         Roo.log('strength1: ' + strength);
21315         
21316         //var pm = this.trigger.child('div/div/div').dom;
21317         var pm = this.trigger.child('div/div');
21318         pm.removeClass(this.meterClass);
21319         pm.addClass(this.meterClass[strength]);
21320                 
21321         
21322         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21323                 
21324         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21325         
21326         this._lastPwd = pwd;
21327     },
21328     reset: function ()
21329     {
21330         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21331         
21332         this._lastPwd = '';
21333         
21334         var pm = this.trigger.child('div/div');
21335         pm.removeClass(this.meterClass);
21336         pm.addClass('roo-password-meter-grey');        
21337         
21338         
21339         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21340         
21341         pt.innerHTML = '';
21342         this.inputEl().dom.type='password';
21343     },
21344     // private
21345     validateValue: function (value)
21346     {
21347         
21348         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21349             return false;
21350         }
21351         if (value.length == 0) {
21352             if (this.allowBlank) {
21353                 this.clearInvalid();
21354                 return true;
21355             }
21356
21357             this.markInvalid(this.errors.PwdEmpty);
21358             this.errorMsg = this.errors.PwdEmpty;
21359             return false;
21360         }
21361         
21362         if(this.insecure){
21363             return true;
21364         }
21365         
21366         if ('[\x21-\x7e]*'.match(value)) {
21367             this.markInvalid(this.errors.PwdBadChar);
21368             this.errorMsg = this.errors.PwdBadChar;
21369             return false;
21370         }
21371         if (value.length < 6) {
21372             this.markInvalid(this.errors.PwdShort);
21373             this.errorMsg = this.errors.PwdShort;
21374             return false;
21375         }
21376         if (value.length > 16) {
21377             this.markInvalid(this.errors.PwdLong);
21378             this.errorMsg = this.errors.PwdLong;
21379             return false;
21380         }
21381         var strength;
21382         if (this.ClientSideStrongPassword(value)) {
21383             strength = 3;
21384         } else if (this.ClientSideMediumPassword(value)) {
21385             strength = 2;
21386         } else if (this.ClientSideWeakPassword(value)) {
21387             strength = 1;
21388         } else {
21389             strength = 0;
21390         }
21391
21392         
21393         if (strength < 2) {
21394             //this.markInvalid(this.errors.TooWeak);
21395             this.errorMsg = this.errors.TooWeak;
21396             //return false;
21397         }
21398         
21399         
21400         console.log('strength2: ' + strength);
21401         
21402         //var pm = this.trigger.child('div/div/div').dom;
21403         
21404         var pm = this.trigger.child('div/div');
21405         pm.removeClass(this.meterClass);
21406         pm.addClass(this.meterClass[strength]);
21407                 
21408         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21409                 
21410         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21411         
21412         this.errorMsg = ''; 
21413         return true;
21414     },
21415     // private
21416     CharacterSetChecks: function (type)
21417     {
21418         this.type = type;
21419         this.fResult = false;
21420     },
21421     // private
21422     isctype: function (character, type)
21423     {
21424         switch (type) {  
21425             case this.kCapitalLetter:
21426                 if (character >= 'A' && character <= 'Z') {
21427                     return true;
21428                 }
21429                 break;
21430             
21431             case this.kSmallLetter:
21432                 if (character >= 'a' && character <= 'z') {
21433                     return true;
21434                 }
21435                 break;
21436             
21437             case this.kDigit:
21438                 if (character >= '0' && character <= '9') {
21439                     return true;
21440                 }
21441                 break;
21442             
21443             case this.kPunctuation:
21444                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21445                     return true;
21446                 }
21447                 break;
21448             
21449             default:
21450                 return false;
21451         }
21452
21453     },
21454     // private
21455     IsLongEnough: function (pwd, size)
21456     {
21457         return !(pwd == null || isNaN(size) || pwd.length < size);
21458     },
21459     // private
21460     SpansEnoughCharacterSets: function (word, nb)
21461     {
21462         if (!this.IsLongEnough(word, nb))
21463         {
21464             return false;
21465         }
21466
21467         var characterSetChecks = new Array(
21468             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21469             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21470         );
21471         
21472         for (var index = 0; index < word.length; ++index) {
21473             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21474                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21475                     characterSetChecks[nCharSet].fResult = true;
21476                     break;
21477                 }
21478             }
21479         }
21480
21481         var nCharSets = 0;
21482         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21483             if (characterSetChecks[nCharSet].fResult) {
21484                 ++nCharSets;
21485             }
21486         }
21487
21488         if (nCharSets < nb) {
21489             return false;
21490         }
21491         return true;
21492     },
21493     // private
21494     ClientSideStrongPassword: function (pwd)
21495     {
21496         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21497     },
21498     // private
21499     ClientSideMediumPassword: function (pwd)
21500     {
21501         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21502     },
21503     // private
21504     ClientSideWeakPassword: function (pwd)
21505     {
21506         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21507     }
21508           
21509 })//<script type="text/javascript">
21510
21511 /*
21512  * Based  Ext JS Library 1.1.1
21513  * Copyright(c) 2006-2007, Ext JS, LLC.
21514  * LGPL
21515  *
21516  */
21517  
21518 /**
21519  * @class Roo.HtmlEditorCore
21520  * @extends Roo.Component
21521  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21522  *
21523  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21524  */
21525
21526 Roo.HtmlEditorCore = function(config){
21527     
21528     
21529     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21530     
21531     
21532     this.addEvents({
21533         /**
21534          * @event initialize
21535          * Fires when the editor is fully initialized (including the iframe)
21536          * @param {Roo.HtmlEditorCore} this
21537          */
21538         initialize: true,
21539         /**
21540          * @event activate
21541          * Fires when the editor is first receives the focus. Any insertion must wait
21542          * until after this event.
21543          * @param {Roo.HtmlEditorCore} this
21544          */
21545         activate: true,
21546          /**
21547          * @event beforesync
21548          * Fires before the textarea is updated with content from the editor iframe. Return false
21549          * to cancel the sync.
21550          * @param {Roo.HtmlEditorCore} this
21551          * @param {String} html
21552          */
21553         beforesync: true,
21554          /**
21555          * @event beforepush
21556          * Fires before the iframe editor is updated with content from the textarea. Return false
21557          * to cancel the push.
21558          * @param {Roo.HtmlEditorCore} this
21559          * @param {String} html
21560          */
21561         beforepush: true,
21562          /**
21563          * @event sync
21564          * Fires when the textarea is updated with content from the editor iframe.
21565          * @param {Roo.HtmlEditorCore} this
21566          * @param {String} html
21567          */
21568         sync: true,
21569          /**
21570          * @event push
21571          * Fires when the iframe editor is updated with content from the textarea.
21572          * @param {Roo.HtmlEditorCore} this
21573          * @param {String} html
21574          */
21575         push: true,
21576         
21577         /**
21578          * @event editorevent
21579          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21580          * @param {Roo.HtmlEditorCore} this
21581          */
21582         editorevent: true
21583         
21584     });
21585     
21586     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21587     
21588     // defaults : white / black...
21589     this.applyBlacklists();
21590     
21591     
21592     
21593 };
21594
21595
21596 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21597
21598
21599      /**
21600      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21601      */
21602     
21603     owner : false,
21604     
21605      /**
21606      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21607      *                        Roo.resizable.
21608      */
21609     resizable : false,
21610      /**
21611      * @cfg {Number} height (in pixels)
21612      */   
21613     height: 300,
21614    /**
21615      * @cfg {Number} width (in pixels)
21616      */   
21617     width: 500,
21618     
21619     /**
21620      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21621      * 
21622      */
21623     stylesheets: false,
21624     
21625     // id of frame..
21626     frameId: false,
21627     
21628     // private properties
21629     validationEvent : false,
21630     deferHeight: true,
21631     initialized : false,
21632     activated : false,
21633     sourceEditMode : false,
21634     onFocus : Roo.emptyFn,
21635     iframePad:3,
21636     hideMode:'offsets',
21637     
21638     clearUp: true,
21639     
21640     // blacklist + whitelisted elements..
21641     black: false,
21642     white: false,
21643      
21644     bodyCls : '',
21645
21646     /**
21647      * Protected method that will not generally be called directly. It
21648      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21649      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21650      */
21651     getDocMarkup : function(){
21652         // body styles..
21653         var st = '';
21654         
21655         // inherit styels from page...?? 
21656         if (this.stylesheets === false) {
21657             
21658             Roo.get(document.head).select('style').each(function(node) {
21659                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21660             });
21661             
21662             Roo.get(document.head).select('link').each(function(node) { 
21663                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21664             });
21665             
21666         } else if (!this.stylesheets.length) {
21667                 // simple..
21668                 st = '<style type="text/css">' +
21669                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21670                    '</style>';
21671         } else { 
21672             st = '<style type="text/css">' +
21673                     this.stylesheets +
21674                 '</style>';
21675         }
21676         
21677         st +=  '<style type="text/css">' +
21678             'IMG { cursor: pointer } ' +
21679         '</style>';
21680
21681         var cls = 'roo-htmleditor-body';
21682         
21683         if(this.bodyCls.length){
21684             cls += ' ' + this.bodyCls;
21685         }
21686         
21687         return '<html><head>' + st  +
21688             //<style type="text/css">' +
21689             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21690             //'</style>' +
21691             ' </head><body class="' +  cls + '"></body></html>';
21692     },
21693
21694     // private
21695     onRender : function(ct, position)
21696     {
21697         var _t = this;
21698         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21699         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21700         
21701         
21702         this.el.dom.style.border = '0 none';
21703         this.el.dom.setAttribute('tabIndex', -1);
21704         this.el.addClass('x-hidden hide');
21705         
21706         
21707         
21708         if(Roo.isIE){ // fix IE 1px bogus margin
21709             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21710         }
21711        
21712         
21713         this.frameId = Roo.id();
21714         
21715          
21716         
21717         var iframe = this.owner.wrap.createChild({
21718             tag: 'iframe',
21719             cls: 'form-control', // bootstrap..
21720             id: this.frameId,
21721             name: this.frameId,
21722             frameBorder : 'no',
21723             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21724         }, this.el
21725         );
21726         
21727         
21728         this.iframe = iframe.dom;
21729
21730          this.assignDocWin();
21731         
21732         this.doc.designMode = 'on';
21733        
21734         this.doc.open();
21735         this.doc.write(this.getDocMarkup());
21736         this.doc.close();
21737
21738         
21739         var task = { // must defer to wait for browser to be ready
21740             run : function(){
21741                 //console.log("run task?" + this.doc.readyState);
21742                 this.assignDocWin();
21743                 if(this.doc.body || this.doc.readyState == 'complete'){
21744                     try {
21745                         this.doc.designMode="on";
21746                     } catch (e) {
21747                         return;
21748                     }
21749                     Roo.TaskMgr.stop(task);
21750                     this.initEditor.defer(10, this);
21751                 }
21752             },
21753             interval : 10,
21754             duration: 10000,
21755             scope: this
21756         };
21757         Roo.TaskMgr.start(task);
21758
21759     },
21760
21761     // private
21762     onResize : function(w, h)
21763     {
21764          Roo.log('resize: ' +w + ',' + h );
21765         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21766         if(!this.iframe){
21767             return;
21768         }
21769         if(typeof w == 'number'){
21770             
21771             this.iframe.style.width = w + 'px';
21772         }
21773         if(typeof h == 'number'){
21774             
21775             this.iframe.style.height = h + 'px';
21776             if(this.doc){
21777                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21778             }
21779         }
21780         
21781     },
21782
21783     /**
21784      * Toggles the editor between standard and source edit mode.
21785      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21786      */
21787     toggleSourceEdit : function(sourceEditMode){
21788         
21789         this.sourceEditMode = sourceEditMode === true;
21790         
21791         if(this.sourceEditMode){
21792  
21793             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21794             
21795         }else{
21796             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21797             //this.iframe.className = '';
21798             this.deferFocus();
21799         }
21800         //this.setSize(this.owner.wrap.getSize());
21801         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21802     },
21803
21804     
21805   
21806
21807     /**
21808      * Protected method that will not generally be called directly. If you need/want
21809      * custom HTML cleanup, this is the method you should override.
21810      * @param {String} html The HTML to be cleaned
21811      * return {String} The cleaned HTML
21812      */
21813     cleanHtml : function(html){
21814         html = String(html);
21815         if(html.length > 5){
21816             if(Roo.isSafari){ // strip safari nonsense
21817                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21818             }
21819         }
21820         if(html == '&nbsp;'){
21821             html = '';
21822         }
21823         return html;
21824     },
21825
21826     /**
21827      * HTML Editor -> Textarea
21828      * Protected method that will not generally be called directly. Syncs the contents
21829      * of the editor iframe with the textarea.
21830      */
21831     syncValue : function(){
21832         if(this.initialized){
21833             var bd = (this.doc.body || this.doc.documentElement);
21834             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21835             var html = bd.innerHTML;
21836             if(Roo.isSafari){
21837                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21838                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21839                 if(m && m[1]){
21840                     html = '<div style="'+m[0]+'">' + html + '</div>';
21841                 }
21842             }
21843             html = this.cleanHtml(html);
21844             // fix up the special chars.. normaly like back quotes in word...
21845             // however we do not want to do this with chinese..
21846             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21847                 var cc = b.charCodeAt();
21848                 if (
21849                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21850                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21851                     (cc >= 0xf900 && cc < 0xfb00 )
21852                 ) {
21853                         return b;
21854                 }
21855                 return "&#"+cc+";" 
21856             });
21857             if(this.owner.fireEvent('beforesync', this, html) !== false){
21858                 this.el.dom.value = html;
21859                 this.owner.fireEvent('sync', this, html);
21860             }
21861         }
21862     },
21863
21864     /**
21865      * Protected method that will not generally be called directly. Pushes the value of the textarea
21866      * into the iframe editor.
21867      */
21868     pushValue : function(){
21869         if(this.initialized){
21870             var v = this.el.dom.value.trim();
21871             
21872 //            if(v.length < 1){
21873 //                v = '&#160;';
21874 //            }
21875             
21876             if(this.owner.fireEvent('beforepush', this, v) !== false){
21877                 var d = (this.doc.body || this.doc.documentElement);
21878                 d.innerHTML = v;
21879                 this.cleanUpPaste();
21880                 this.el.dom.value = d.innerHTML;
21881                 this.owner.fireEvent('push', this, v);
21882             }
21883         }
21884     },
21885
21886     // private
21887     deferFocus : function(){
21888         this.focus.defer(10, this);
21889     },
21890
21891     // doc'ed in Field
21892     focus : function(){
21893         if(this.win && !this.sourceEditMode){
21894             this.win.focus();
21895         }else{
21896             this.el.focus();
21897         }
21898     },
21899     
21900     assignDocWin: function()
21901     {
21902         var iframe = this.iframe;
21903         
21904          if(Roo.isIE){
21905             this.doc = iframe.contentWindow.document;
21906             this.win = iframe.contentWindow;
21907         } else {
21908 //            if (!Roo.get(this.frameId)) {
21909 //                return;
21910 //            }
21911 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21912 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21913             
21914             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21915                 return;
21916             }
21917             
21918             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21919             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21920         }
21921     },
21922     
21923     // private
21924     initEditor : function(){
21925         //console.log("INIT EDITOR");
21926         this.assignDocWin();
21927         
21928         
21929         
21930         this.doc.designMode="on";
21931         this.doc.open();
21932         this.doc.write(this.getDocMarkup());
21933         this.doc.close();
21934         
21935         var dbody = (this.doc.body || this.doc.documentElement);
21936         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21937         // this copies styles from the containing element into thsi one..
21938         // not sure why we need all of this..
21939         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21940         
21941         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21942         //ss['background-attachment'] = 'fixed'; // w3c
21943         dbody.bgProperties = 'fixed'; // ie
21944         //Roo.DomHelper.applyStyles(dbody, ss);
21945         Roo.EventManager.on(this.doc, {
21946             //'mousedown': this.onEditorEvent,
21947             'mouseup': this.onEditorEvent,
21948             'dblclick': this.onEditorEvent,
21949             'click': this.onEditorEvent,
21950             'keyup': this.onEditorEvent,
21951             buffer:100,
21952             scope: this
21953         });
21954         if(Roo.isGecko){
21955             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21956         }
21957         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21958             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21959         }
21960         this.initialized = true;
21961
21962         this.owner.fireEvent('initialize', this);
21963         this.pushValue();
21964     },
21965
21966     // private
21967     onDestroy : function(){
21968         
21969         
21970         
21971         if(this.rendered){
21972             
21973             //for (var i =0; i < this.toolbars.length;i++) {
21974             //    // fixme - ask toolbars for heights?
21975             //    this.toolbars[i].onDestroy();
21976            // }
21977             
21978             //this.wrap.dom.innerHTML = '';
21979             //this.wrap.remove();
21980         }
21981     },
21982
21983     // private
21984     onFirstFocus : function(){
21985         
21986         this.assignDocWin();
21987         
21988         
21989         this.activated = true;
21990          
21991     
21992         if(Roo.isGecko){ // prevent silly gecko errors
21993             this.win.focus();
21994             var s = this.win.getSelection();
21995             if(!s.focusNode || s.focusNode.nodeType != 3){
21996                 var r = s.getRangeAt(0);
21997                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21998                 r.collapse(true);
21999                 this.deferFocus();
22000             }
22001             try{
22002                 this.execCmd('useCSS', true);
22003                 this.execCmd('styleWithCSS', false);
22004             }catch(e){}
22005         }
22006         this.owner.fireEvent('activate', this);
22007     },
22008
22009     // private
22010     adjustFont: function(btn){
22011         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22012         //if(Roo.isSafari){ // safari
22013         //    adjust *= 2;
22014        // }
22015         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22016         if(Roo.isSafari){ // safari
22017             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22018             v =  (v < 10) ? 10 : v;
22019             v =  (v > 48) ? 48 : v;
22020             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22021             
22022         }
22023         
22024         
22025         v = Math.max(1, v+adjust);
22026         
22027         this.execCmd('FontSize', v  );
22028     },
22029
22030     onEditorEvent : function(e)
22031     {
22032         this.owner.fireEvent('editorevent', this, e);
22033       //  this.updateToolbar();
22034         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22035     },
22036
22037     insertTag : function(tg)
22038     {
22039         // could be a bit smarter... -> wrap the current selected tRoo..
22040         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22041             
22042             range = this.createRange(this.getSelection());
22043             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22044             wrappingNode.appendChild(range.extractContents());
22045             range.insertNode(wrappingNode);
22046
22047             return;
22048             
22049             
22050             
22051         }
22052         this.execCmd("formatblock",   tg);
22053         
22054     },
22055     
22056     insertText : function(txt)
22057     {
22058         
22059         
22060         var range = this.createRange();
22061         range.deleteContents();
22062                //alert(Sender.getAttribute('label'));
22063                
22064         range.insertNode(this.doc.createTextNode(txt));
22065     } ,
22066     
22067      
22068
22069     /**
22070      * Executes a Midas editor command on the editor document and performs necessary focus and
22071      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22072      * @param {String} cmd The Midas command
22073      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22074      */
22075     relayCmd : function(cmd, value){
22076         this.win.focus();
22077         this.execCmd(cmd, value);
22078         this.owner.fireEvent('editorevent', this);
22079         //this.updateToolbar();
22080         this.owner.deferFocus();
22081     },
22082
22083     /**
22084      * Executes a Midas editor command directly on the editor document.
22085      * For visual commands, you should use {@link #relayCmd} instead.
22086      * <b>This should only be called after the editor is initialized.</b>
22087      * @param {String} cmd The Midas command
22088      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22089      */
22090     execCmd : function(cmd, value){
22091         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22092         this.syncValue();
22093     },
22094  
22095  
22096    
22097     /**
22098      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22099      * to insert tRoo.
22100      * @param {String} text | dom node.. 
22101      */
22102     insertAtCursor : function(text)
22103     {
22104         
22105         if(!this.activated){
22106             return;
22107         }
22108         /*
22109         if(Roo.isIE){
22110             this.win.focus();
22111             var r = this.doc.selection.createRange();
22112             if(r){
22113                 r.collapse(true);
22114                 r.pasteHTML(text);
22115                 this.syncValue();
22116                 this.deferFocus();
22117             
22118             }
22119             return;
22120         }
22121         */
22122         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22123             this.win.focus();
22124             
22125             
22126             // from jquery ui (MIT licenced)
22127             var range, node;
22128             var win = this.win;
22129             
22130             if (win.getSelection && win.getSelection().getRangeAt) {
22131                 range = win.getSelection().getRangeAt(0);
22132                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22133                 range.insertNode(node);
22134             } else if (win.document.selection && win.document.selection.createRange) {
22135                 // no firefox support
22136                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22137                 win.document.selection.createRange().pasteHTML(txt);
22138             } else {
22139                 // no firefox support
22140                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22141                 this.execCmd('InsertHTML', txt);
22142             } 
22143             
22144             this.syncValue();
22145             
22146             this.deferFocus();
22147         }
22148     },
22149  // private
22150     mozKeyPress : function(e){
22151         if(e.ctrlKey){
22152             var c = e.getCharCode(), cmd;
22153           
22154             if(c > 0){
22155                 c = String.fromCharCode(c).toLowerCase();
22156                 switch(c){
22157                     case 'b':
22158                         cmd = 'bold';
22159                         break;
22160                     case 'i':
22161                         cmd = 'italic';
22162                         break;
22163                     
22164                     case 'u':
22165                         cmd = 'underline';
22166                         break;
22167                     
22168                     case 'v':
22169                         this.cleanUpPaste.defer(100, this);
22170                         return;
22171                         
22172                 }
22173                 if(cmd){
22174                     this.win.focus();
22175                     this.execCmd(cmd);
22176                     this.deferFocus();
22177                     e.preventDefault();
22178                 }
22179                 
22180             }
22181         }
22182     },
22183
22184     // private
22185     fixKeys : function(){ // load time branching for fastest keydown performance
22186         if(Roo.isIE){
22187             return function(e){
22188                 var k = e.getKey(), r;
22189                 if(k == e.TAB){
22190                     e.stopEvent();
22191                     r = this.doc.selection.createRange();
22192                     if(r){
22193                         r.collapse(true);
22194                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22195                         this.deferFocus();
22196                     }
22197                     return;
22198                 }
22199                 
22200                 if(k == e.ENTER){
22201                     r = this.doc.selection.createRange();
22202                     if(r){
22203                         var target = r.parentElement();
22204                         if(!target || target.tagName.toLowerCase() != 'li'){
22205                             e.stopEvent();
22206                             r.pasteHTML('<br />');
22207                             r.collapse(false);
22208                             r.select();
22209                         }
22210                     }
22211                 }
22212                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22213                     this.cleanUpPaste.defer(100, this);
22214                     return;
22215                 }
22216                 
22217                 
22218             };
22219         }else if(Roo.isOpera){
22220             return function(e){
22221                 var k = e.getKey();
22222                 if(k == e.TAB){
22223                     e.stopEvent();
22224                     this.win.focus();
22225                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22226                     this.deferFocus();
22227                 }
22228                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22229                     this.cleanUpPaste.defer(100, this);
22230                     return;
22231                 }
22232                 
22233             };
22234         }else if(Roo.isSafari){
22235             return function(e){
22236                 var k = e.getKey();
22237                 
22238                 if(k == e.TAB){
22239                     e.stopEvent();
22240                     this.execCmd('InsertText','\t');
22241                     this.deferFocus();
22242                     return;
22243                 }
22244                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22245                     this.cleanUpPaste.defer(100, this);
22246                     return;
22247                 }
22248                 
22249              };
22250         }
22251     }(),
22252     
22253     getAllAncestors: function()
22254     {
22255         var p = this.getSelectedNode();
22256         var a = [];
22257         if (!p) {
22258             a.push(p); // push blank onto stack..
22259             p = this.getParentElement();
22260         }
22261         
22262         
22263         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22264             a.push(p);
22265             p = p.parentNode;
22266         }
22267         a.push(this.doc.body);
22268         return a;
22269     },
22270     lastSel : false,
22271     lastSelNode : false,
22272     
22273     
22274     getSelection : function() 
22275     {
22276         this.assignDocWin();
22277         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22278     },
22279     
22280     getSelectedNode: function() 
22281     {
22282         // this may only work on Gecko!!!
22283         
22284         // should we cache this!!!!
22285         
22286         
22287         
22288          
22289         var range = this.createRange(this.getSelection()).cloneRange();
22290         
22291         if (Roo.isIE) {
22292             var parent = range.parentElement();
22293             while (true) {
22294                 var testRange = range.duplicate();
22295                 testRange.moveToElementText(parent);
22296                 if (testRange.inRange(range)) {
22297                     break;
22298                 }
22299                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22300                     break;
22301                 }
22302                 parent = parent.parentElement;
22303             }
22304             return parent;
22305         }
22306         
22307         // is ancestor a text element.
22308         var ac =  range.commonAncestorContainer;
22309         if (ac.nodeType == 3) {
22310             ac = ac.parentNode;
22311         }
22312         
22313         var ar = ac.childNodes;
22314          
22315         var nodes = [];
22316         var other_nodes = [];
22317         var has_other_nodes = false;
22318         for (var i=0;i<ar.length;i++) {
22319             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22320                 continue;
22321             }
22322             // fullly contained node.
22323             
22324             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22325                 nodes.push(ar[i]);
22326                 continue;
22327             }
22328             
22329             // probably selected..
22330             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22331                 other_nodes.push(ar[i]);
22332                 continue;
22333             }
22334             // outer..
22335             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22336                 continue;
22337             }
22338             
22339             
22340             has_other_nodes = true;
22341         }
22342         if (!nodes.length && other_nodes.length) {
22343             nodes= other_nodes;
22344         }
22345         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22346             return false;
22347         }
22348         
22349         return nodes[0];
22350     },
22351     createRange: function(sel)
22352     {
22353         // this has strange effects when using with 
22354         // top toolbar - not sure if it's a great idea.
22355         //this.editor.contentWindow.focus();
22356         if (typeof sel != "undefined") {
22357             try {
22358                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22359             } catch(e) {
22360                 return this.doc.createRange();
22361             }
22362         } else {
22363             return this.doc.createRange();
22364         }
22365     },
22366     getParentElement: function()
22367     {
22368         
22369         this.assignDocWin();
22370         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22371         
22372         var range = this.createRange(sel);
22373          
22374         try {
22375             var p = range.commonAncestorContainer;
22376             while (p.nodeType == 3) { // text node
22377                 p = p.parentNode;
22378             }
22379             return p;
22380         } catch (e) {
22381             return null;
22382         }
22383     
22384     },
22385     /***
22386      *
22387      * Range intersection.. the hard stuff...
22388      *  '-1' = before
22389      *  '0' = hits..
22390      *  '1' = after.
22391      *         [ -- selected range --- ]
22392      *   [fail]                        [fail]
22393      *
22394      *    basically..
22395      *      if end is before start or  hits it. fail.
22396      *      if start is after end or hits it fail.
22397      *
22398      *   if either hits (but other is outside. - then it's not 
22399      *   
22400      *    
22401      **/
22402     
22403     
22404     // @see http://www.thismuchiknow.co.uk/?p=64.
22405     rangeIntersectsNode : function(range, node)
22406     {
22407         var nodeRange = node.ownerDocument.createRange();
22408         try {
22409             nodeRange.selectNode(node);
22410         } catch (e) {
22411             nodeRange.selectNodeContents(node);
22412         }
22413     
22414         var rangeStartRange = range.cloneRange();
22415         rangeStartRange.collapse(true);
22416     
22417         var rangeEndRange = range.cloneRange();
22418         rangeEndRange.collapse(false);
22419     
22420         var nodeStartRange = nodeRange.cloneRange();
22421         nodeStartRange.collapse(true);
22422     
22423         var nodeEndRange = nodeRange.cloneRange();
22424         nodeEndRange.collapse(false);
22425     
22426         return rangeStartRange.compareBoundaryPoints(
22427                  Range.START_TO_START, nodeEndRange) == -1 &&
22428                rangeEndRange.compareBoundaryPoints(
22429                  Range.START_TO_START, nodeStartRange) == 1;
22430         
22431          
22432     },
22433     rangeCompareNode : function(range, node)
22434     {
22435         var nodeRange = node.ownerDocument.createRange();
22436         try {
22437             nodeRange.selectNode(node);
22438         } catch (e) {
22439             nodeRange.selectNodeContents(node);
22440         }
22441         
22442         
22443         range.collapse(true);
22444     
22445         nodeRange.collapse(true);
22446      
22447         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22448         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22449          
22450         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22451         
22452         var nodeIsBefore   =  ss == 1;
22453         var nodeIsAfter    = ee == -1;
22454         
22455         if (nodeIsBefore && nodeIsAfter) {
22456             return 0; // outer
22457         }
22458         if (!nodeIsBefore && nodeIsAfter) {
22459             return 1; //right trailed.
22460         }
22461         
22462         if (nodeIsBefore && !nodeIsAfter) {
22463             return 2;  // left trailed.
22464         }
22465         // fully contined.
22466         return 3;
22467     },
22468
22469     // private? - in a new class?
22470     cleanUpPaste :  function()
22471     {
22472         // cleans up the whole document..
22473         Roo.log('cleanuppaste');
22474         
22475         this.cleanUpChildren(this.doc.body);
22476         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22477         if (clean != this.doc.body.innerHTML) {
22478             this.doc.body.innerHTML = clean;
22479         }
22480         
22481     },
22482     
22483     cleanWordChars : function(input) {// change the chars to hex code
22484         var he = Roo.HtmlEditorCore;
22485         
22486         var output = input;
22487         Roo.each(he.swapCodes, function(sw) { 
22488             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22489             
22490             output = output.replace(swapper, sw[1]);
22491         });
22492         
22493         return output;
22494     },
22495     
22496     
22497     cleanUpChildren : function (n)
22498     {
22499         if (!n.childNodes.length) {
22500             return;
22501         }
22502         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22503            this.cleanUpChild(n.childNodes[i]);
22504         }
22505     },
22506     
22507     
22508         
22509     
22510     cleanUpChild : function (node)
22511     {
22512         var ed = this;
22513         //console.log(node);
22514         if (node.nodeName == "#text") {
22515             // clean up silly Windows -- stuff?
22516             return; 
22517         }
22518         if (node.nodeName == "#comment") {
22519             node.parentNode.removeChild(node);
22520             // clean up silly Windows -- stuff?
22521             return; 
22522         }
22523         var lcname = node.tagName.toLowerCase();
22524         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22525         // whitelist of tags..
22526         
22527         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22528             // remove node.
22529             node.parentNode.removeChild(node);
22530             return;
22531             
22532         }
22533         
22534         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22535         
22536         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22537         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22538         
22539         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22540         //    remove_keep_children = true;
22541         //}
22542         
22543         if (remove_keep_children) {
22544             this.cleanUpChildren(node);
22545             // inserts everything just before this node...
22546             while (node.childNodes.length) {
22547                 var cn = node.childNodes[0];
22548                 node.removeChild(cn);
22549                 node.parentNode.insertBefore(cn, node);
22550             }
22551             node.parentNode.removeChild(node);
22552             return;
22553         }
22554         
22555         if (!node.attributes || !node.attributes.length) {
22556             this.cleanUpChildren(node);
22557             return;
22558         }
22559         
22560         function cleanAttr(n,v)
22561         {
22562             
22563             if (v.match(/^\./) || v.match(/^\//)) {
22564                 return;
22565             }
22566             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22567                 return;
22568             }
22569             if (v.match(/^#/)) {
22570                 return;
22571             }
22572 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22573             node.removeAttribute(n);
22574             
22575         }
22576         
22577         var cwhite = this.cwhite;
22578         var cblack = this.cblack;
22579             
22580         function cleanStyle(n,v)
22581         {
22582             if (v.match(/expression/)) { //XSS?? should we even bother..
22583                 node.removeAttribute(n);
22584                 return;
22585             }
22586             
22587             var parts = v.split(/;/);
22588             var clean = [];
22589             
22590             Roo.each(parts, function(p) {
22591                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22592                 if (!p.length) {
22593                     return true;
22594                 }
22595                 var l = p.split(':').shift().replace(/\s+/g,'');
22596                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22597                 
22598                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22599 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22600                     //node.removeAttribute(n);
22601                     return true;
22602                 }
22603                 //Roo.log()
22604                 // only allow 'c whitelisted system attributes'
22605                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22606 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22607                     //node.removeAttribute(n);
22608                     return true;
22609                 }
22610                 
22611                 
22612                  
22613                 
22614                 clean.push(p);
22615                 return true;
22616             });
22617             if (clean.length) { 
22618                 node.setAttribute(n, clean.join(';'));
22619             } else {
22620                 node.removeAttribute(n);
22621             }
22622             
22623         }
22624         
22625         
22626         for (var i = node.attributes.length-1; i > -1 ; i--) {
22627             var a = node.attributes[i];
22628             //console.log(a);
22629             
22630             if (a.name.toLowerCase().substr(0,2)=='on')  {
22631                 node.removeAttribute(a.name);
22632                 continue;
22633             }
22634             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22635                 node.removeAttribute(a.name);
22636                 continue;
22637             }
22638             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22639                 cleanAttr(a.name,a.value); // fixme..
22640                 continue;
22641             }
22642             if (a.name == 'style') {
22643                 cleanStyle(a.name,a.value);
22644                 continue;
22645             }
22646             /// clean up MS crap..
22647             // tecnically this should be a list of valid class'es..
22648             
22649             
22650             if (a.name == 'class') {
22651                 if (a.value.match(/^Mso/)) {
22652                     node.className = '';
22653                 }
22654                 
22655                 if (a.value.match(/^body$/)) {
22656                     node.className = '';
22657                 }
22658                 continue;
22659             }
22660             
22661             // style cleanup!?
22662             // class cleanup?
22663             
22664         }
22665         
22666         
22667         this.cleanUpChildren(node);
22668         
22669         
22670     },
22671     
22672     /**
22673      * Clean up MS wordisms...
22674      */
22675     cleanWord : function(node)
22676     {
22677         
22678         
22679         if (!node) {
22680             this.cleanWord(this.doc.body);
22681             return;
22682         }
22683         if (node.nodeName == "#text") {
22684             // clean up silly Windows -- stuff?
22685             return; 
22686         }
22687         if (node.nodeName == "#comment") {
22688             node.parentNode.removeChild(node);
22689             // clean up silly Windows -- stuff?
22690             return; 
22691         }
22692         
22693         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22694             node.parentNode.removeChild(node);
22695             return;
22696         }
22697         
22698         // remove - but keep children..
22699         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22700             while (node.childNodes.length) {
22701                 var cn = node.childNodes[0];
22702                 node.removeChild(cn);
22703                 node.parentNode.insertBefore(cn, node);
22704             }
22705             node.parentNode.removeChild(node);
22706             this.iterateChildren(node, this.cleanWord);
22707             return;
22708         }
22709         // clean styles
22710         if (node.className.length) {
22711             
22712             var cn = node.className.split(/\W+/);
22713             var cna = [];
22714             Roo.each(cn, function(cls) {
22715                 if (cls.match(/Mso[a-zA-Z]+/)) {
22716                     return;
22717                 }
22718                 cna.push(cls);
22719             });
22720             node.className = cna.length ? cna.join(' ') : '';
22721             if (!cna.length) {
22722                 node.removeAttribute("class");
22723             }
22724         }
22725         
22726         if (node.hasAttribute("lang")) {
22727             node.removeAttribute("lang");
22728         }
22729         
22730         if (node.hasAttribute("style")) {
22731             
22732             var styles = node.getAttribute("style").split(";");
22733             var nstyle = [];
22734             Roo.each(styles, function(s) {
22735                 if (!s.match(/:/)) {
22736                     return;
22737                 }
22738                 var kv = s.split(":");
22739                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22740                     return;
22741                 }
22742                 // what ever is left... we allow.
22743                 nstyle.push(s);
22744             });
22745             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22746             if (!nstyle.length) {
22747                 node.removeAttribute('style');
22748             }
22749         }
22750         this.iterateChildren(node, this.cleanWord);
22751         
22752         
22753         
22754     },
22755     /**
22756      * iterateChildren of a Node, calling fn each time, using this as the scole..
22757      * @param {DomNode} node node to iterate children of.
22758      * @param {Function} fn method of this class to call on each item.
22759      */
22760     iterateChildren : function(node, fn)
22761     {
22762         if (!node.childNodes.length) {
22763                 return;
22764         }
22765         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22766            fn.call(this, node.childNodes[i])
22767         }
22768     },
22769     
22770     
22771     /**
22772      * cleanTableWidths.
22773      *
22774      * Quite often pasting from word etc.. results in tables with column and widths.
22775      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22776      *
22777      */
22778     cleanTableWidths : function(node)
22779     {
22780          
22781          
22782         if (!node) {
22783             this.cleanTableWidths(this.doc.body);
22784             return;
22785         }
22786         
22787         // ignore list...
22788         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22789             return; 
22790         }
22791         Roo.log(node.tagName);
22792         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22793             this.iterateChildren(node, this.cleanTableWidths);
22794             return;
22795         }
22796         if (node.hasAttribute('width')) {
22797             node.removeAttribute('width');
22798         }
22799         
22800          
22801         if (node.hasAttribute("style")) {
22802             // pretty basic...
22803             
22804             var styles = node.getAttribute("style").split(";");
22805             var nstyle = [];
22806             Roo.each(styles, function(s) {
22807                 if (!s.match(/:/)) {
22808                     return;
22809                 }
22810                 var kv = s.split(":");
22811                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22812                     return;
22813                 }
22814                 // what ever is left... we allow.
22815                 nstyle.push(s);
22816             });
22817             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22818             if (!nstyle.length) {
22819                 node.removeAttribute('style');
22820             }
22821         }
22822         
22823         this.iterateChildren(node, this.cleanTableWidths);
22824         
22825         
22826     },
22827     
22828     
22829     
22830     
22831     domToHTML : function(currentElement, depth, nopadtext) {
22832         
22833         depth = depth || 0;
22834         nopadtext = nopadtext || false;
22835     
22836         if (!currentElement) {
22837             return this.domToHTML(this.doc.body);
22838         }
22839         
22840         //Roo.log(currentElement);
22841         var j;
22842         var allText = false;
22843         var nodeName = currentElement.nodeName;
22844         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22845         
22846         if  (nodeName == '#text') {
22847             
22848             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22849         }
22850         
22851         
22852         var ret = '';
22853         if (nodeName != 'BODY') {
22854              
22855             var i = 0;
22856             // Prints the node tagName, such as <A>, <IMG>, etc
22857             if (tagName) {
22858                 var attr = [];
22859                 for(i = 0; i < currentElement.attributes.length;i++) {
22860                     // quoting?
22861                     var aname = currentElement.attributes.item(i).name;
22862                     if (!currentElement.attributes.item(i).value.length) {
22863                         continue;
22864                     }
22865                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22866                 }
22867                 
22868                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22869             } 
22870             else {
22871                 
22872                 // eack
22873             }
22874         } else {
22875             tagName = false;
22876         }
22877         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22878             return ret;
22879         }
22880         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22881             nopadtext = true;
22882         }
22883         
22884         
22885         // Traverse the tree
22886         i = 0;
22887         var currentElementChild = currentElement.childNodes.item(i);
22888         var allText = true;
22889         var innerHTML  = '';
22890         lastnode = '';
22891         while (currentElementChild) {
22892             // Formatting code (indent the tree so it looks nice on the screen)
22893             var nopad = nopadtext;
22894             if (lastnode == 'SPAN') {
22895                 nopad  = true;
22896             }
22897             // text
22898             if  (currentElementChild.nodeName == '#text') {
22899                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22900                 toadd = nopadtext ? toadd : toadd.trim();
22901                 if (!nopad && toadd.length > 80) {
22902                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22903                 }
22904                 innerHTML  += toadd;
22905                 
22906                 i++;
22907                 currentElementChild = currentElement.childNodes.item(i);
22908                 lastNode = '';
22909                 continue;
22910             }
22911             allText = false;
22912             
22913             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22914                 
22915             // Recursively traverse the tree structure of the child node
22916             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22917             lastnode = currentElementChild.nodeName;
22918             i++;
22919             currentElementChild=currentElement.childNodes.item(i);
22920         }
22921         
22922         ret += innerHTML;
22923         
22924         if (!allText) {
22925                 // The remaining code is mostly for formatting the tree
22926             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22927         }
22928         
22929         
22930         if (tagName) {
22931             ret+= "</"+tagName+">";
22932         }
22933         return ret;
22934         
22935     },
22936         
22937     applyBlacklists : function()
22938     {
22939         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22940         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22941         
22942         this.white = [];
22943         this.black = [];
22944         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22945             if (b.indexOf(tag) > -1) {
22946                 return;
22947             }
22948             this.white.push(tag);
22949             
22950         }, this);
22951         
22952         Roo.each(w, function(tag) {
22953             if (b.indexOf(tag) > -1) {
22954                 return;
22955             }
22956             if (this.white.indexOf(tag) > -1) {
22957                 return;
22958             }
22959             this.white.push(tag);
22960             
22961         }, this);
22962         
22963         
22964         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22965             if (w.indexOf(tag) > -1) {
22966                 return;
22967             }
22968             this.black.push(tag);
22969             
22970         }, this);
22971         
22972         Roo.each(b, function(tag) {
22973             if (w.indexOf(tag) > -1) {
22974                 return;
22975             }
22976             if (this.black.indexOf(tag) > -1) {
22977                 return;
22978             }
22979             this.black.push(tag);
22980             
22981         }, this);
22982         
22983         
22984         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22985         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22986         
22987         this.cwhite = [];
22988         this.cblack = [];
22989         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22990             if (b.indexOf(tag) > -1) {
22991                 return;
22992             }
22993             this.cwhite.push(tag);
22994             
22995         }, this);
22996         
22997         Roo.each(w, function(tag) {
22998             if (b.indexOf(tag) > -1) {
22999                 return;
23000             }
23001             if (this.cwhite.indexOf(tag) > -1) {
23002                 return;
23003             }
23004             this.cwhite.push(tag);
23005             
23006         }, this);
23007         
23008         
23009         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23010             if (w.indexOf(tag) > -1) {
23011                 return;
23012             }
23013             this.cblack.push(tag);
23014             
23015         }, this);
23016         
23017         Roo.each(b, function(tag) {
23018             if (w.indexOf(tag) > -1) {
23019                 return;
23020             }
23021             if (this.cblack.indexOf(tag) > -1) {
23022                 return;
23023             }
23024             this.cblack.push(tag);
23025             
23026         }, this);
23027     },
23028     
23029     setStylesheets : function(stylesheets)
23030     {
23031         if(typeof(stylesheets) == 'string'){
23032             Roo.get(this.iframe.contentDocument.head).createChild({
23033                 tag : 'link',
23034                 rel : 'stylesheet',
23035                 type : 'text/css',
23036                 href : stylesheets
23037             });
23038             
23039             return;
23040         }
23041         var _this = this;
23042      
23043         Roo.each(stylesheets, function(s) {
23044             if(!s.length){
23045                 return;
23046             }
23047             
23048             Roo.get(_this.iframe.contentDocument.head).createChild({
23049                 tag : 'link',
23050                 rel : 'stylesheet',
23051                 type : 'text/css',
23052                 href : s
23053             });
23054         });
23055
23056         
23057     },
23058     
23059     removeStylesheets : function()
23060     {
23061         var _this = this;
23062         
23063         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23064             s.remove();
23065         });
23066     },
23067     
23068     setStyle : function(style)
23069     {
23070         Roo.get(this.iframe.contentDocument.head).createChild({
23071             tag : 'style',
23072             type : 'text/css',
23073             html : style
23074         });
23075
23076         return;
23077     }
23078     
23079     // hide stuff that is not compatible
23080     /**
23081      * @event blur
23082      * @hide
23083      */
23084     /**
23085      * @event change
23086      * @hide
23087      */
23088     /**
23089      * @event focus
23090      * @hide
23091      */
23092     /**
23093      * @event specialkey
23094      * @hide
23095      */
23096     /**
23097      * @cfg {String} fieldClass @hide
23098      */
23099     /**
23100      * @cfg {String} focusClass @hide
23101      */
23102     /**
23103      * @cfg {String} autoCreate @hide
23104      */
23105     /**
23106      * @cfg {String} inputType @hide
23107      */
23108     /**
23109      * @cfg {String} invalidClass @hide
23110      */
23111     /**
23112      * @cfg {String} invalidText @hide
23113      */
23114     /**
23115      * @cfg {String} msgFx @hide
23116      */
23117     /**
23118      * @cfg {String} validateOnBlur @hide
23119      */
23120 });
23121
23122 Roo.HtmlEditorCore.white = [
23123         'area', 'br', 'img', 'input', 'hr', 'wbr',
23124         
23125        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23126        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23127        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23128        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23129        'table',   'ul',         'xmp', 
23130        
23131        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23132       'thead',   'tr', 
23133      
23134       'dir', 'menu', 'ol', 'ul', 'dl',
23135        
23136       'embed',  'object'
23137 ];
23138
23139
23140 Roo.HtmlEditorCore.black = [
23141     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23142         'applet', // 
23143         'base',   'basefont', 'bgsound', 'blink',  'body', 
23144         'frame',  'frameset', 'head',    'html',   'ilayer', 
23145         'iframe', 'layer',  'link',     'meta',    'object',   
23146         'script', 'style' ,'title',  'xml' // clean later..
23147 ];
23148 Roo.HtmlEditorCore.clean = [
23149     'script', 'style', 'title', 'xml'
23150 ];
23151 Roo.HtmlEditorCore.remove = [
23152     'font'
23153 ];
23154 // attributes..
23155
23156 Roo.HtmlEditorCore.ablack = [
23157     'on'
23158 ];
23159     
23160 Roo.HtmlEditorCore.aclean = [ 
23161     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23162 ];
23163
23164 // protocols..
23165 Roo.HtmlEditorCore.pwhite= [
23166         'http',  'https',  'mailto'
23167 ];
23168
23169 // white listed style attributes.
23170 Roo.HtmlEditorCore.cwhite= [
23171       //  'text-align', /// default is to allow most things..
23172       
23173          
23174 //        'font-size'//??
23175 ];
23176
23177 // black listed style attributes.
23178 Roo.HtmlEditorCore.cblack= [
23179       //  'font-size' -- this can be set by the project 
23180 ];
23181
23182
23183 Roo.HtmlEditorCore.swapCodes   =[ 
23184     [    8211, "--" ], 
23185     [    8212, "--" ], 
23186     [    8216,  "'" ],  
23187     [    8217, "'" ],  
23188     [    8220, '"' ],  
23189     [    8221, '"' ],  
23190     [    8226, "*" ],  
23191     [    8230, "..." ]
23192 ]; 
23193
23194     /*
23195  * - LGPL
23196  *
23197  * HtmlEditor
23198  * 
23199  */
23200
23201 /**
23202  * @class Roo.bootstrap.HtmlEditor
23203  * @extends Roo.bootstrap.TextArea
23204  * Bootstrap HtmlEditor class
23205
23206  * @constructor
23207  * Create a new HtmlEditor
23208  * @param {Object} config The config object
23209  */
23210
23211 Roo.bootstrap.HtmlEditor = function(config){
23212     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23213     if (!this.toolbars) {
23214         this.toolbars = [];
23215     }
23216     
23217     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23218     this.addEvents({
23219             /**
23220              * @event initialize
23221              * Fires when the editor is fully initialized (including the iframe)
23222              * @param {HtmlEditor} this
23223              */
23224             initialize: true,
23225             /**
23226              * @event activate
23227              * Fires when the editor is first receives the focus. Any insertion must wait
23228              * until after this event.
23229              * @param {HtmlEditor} this
23230              */
23231             activate: true,
23232              /**
23233              * @event beforesync
23234              * Fires before the textarea is updated with content from the editor iframe. Return false
23235              * to cancel the sync.
23236              * @param {HtmlEditor} this
23237              * @param {String} html
23238              */
23239             beforesync: true,
23240              /**
23241              * @event beforepush
23242              * Fires before the iframe editor is updated with content from the textarea. Return false
23243              * to cancel the push.
23244              * @param {HtmlEditor} this
23245              * @param {String} html
23246              */
23247             beforepush: true,
23248              /**
23249              * @event sync
23250              * Fires when the textarea is updated with content from the editor iframe.
23251              * @param {HtmlEditor} this
23252              * @param {String} html
23253              */
23254             sync: true,
23255              /**
23256              * @event push
23257              * Fires when the iframe editor is updated with content from the textarea.
23258              * @param {HtmlEditor} this
23259              * @param {String} html
23260              */
23261             push: true,
23262              /**
23263              * @event editmodechange
23264              * Fires when the editor switches edit modes
23265              * @param {HtmlEditor} this
23266              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23267              */
23268             editmodechange: true,
23269             /**
23270              * @event editorevent
23271              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23272              * @param {HtmlEditor} this
23273              */
23274             editorevent: true,
23275             /**
23276              * @event firstfocus
23277              * Fires when on first focus - needed by toolbars..
23278              * @param {HtmlEditor} this
23279              */
23280             firstfocus: true,
23281             /**
23282              * @event autosave
23283              * Auto save the htmlEditor value as a file into Events
23284              * @param {HtmlEditor} this
23285              */
23286             autosave: true,
23287             /**
23288              * @event savedpreview
23289              * preview the saved version of htmlEditor
23290              * @param {HtmlEditor} this
23291              */
23292             savedpreview: true
23293         });
23294 };
23295
23296
23297 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23298     
23299     
23300       /**
23301      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23302      */
23303     toolbars : false,
23304     
23305      /**
23306     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23307     */
23308     btns : [],
23309    
23310      /**
23311      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23312      *                        Roo.resizable.
23313      */
23314     resizable : false,
23315      /**
23316      * @cfg {Number} height (in pixels)
23317      */   
23318     height: 300,
23319    /**
23320      * @cfg {Number} width (in pixels)
23321      */   
23322     width: false,
23323     
23324     /**
23325      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23326      * 
23327      */
23328     stylesheets: false,
23329     
23330     // id of frame..
23331     frameId: false,
23332     
23333     // private properties
23334     validationEvent : false,
23335     deferHeight: true,
23336     initialized : false,
23337     activated : false,
23338     
23339     onFocus : Roo.emptyFn,
23340     iframePad:3,
23341     hideMode:'offsets',
23342     
23343     tbContainer : false,
23344     
23345     bodyCls : '',
23346     
23347     toolbarContainer :function() {
23348         return this.wrap.select('.x-html-editor-tb',true).first();
23349     },
23350
23351     /**
23352      * Protected method that will not generally be called directly. It
23353      * is called when the editor creates its toolbar. Override this method if you need to
23354      * add custom toolbar buttons.
23355      * @param {HtmlEditor} editor
23356      */
23357     createToolbar : function(){
23358         Roo.log('renewing');
23359         Roo.log("create toolbars");
23360         
23361         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23362         this.toolbars[0].render(this.toolbarContainer());
23363         
23364         return;
23365         
23366 //        if (!editor.toolbars || !editor.toolbars.length) {
23367 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23368 //        }
23369 //        
23370 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23371 //            editor.toolbars[i] = Roo.factory(
23372 //                    typeof(editor.toolbars[i]) == 'string' ?
23373 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23374 //                Roo.bootstrap.HtmlEditor);
23375 //            editor.toolbars[i].init(editor);
23376 //        }
23377     },
23378
23379      
23380     // private
23381     onRender : function(ct, position)
23382     {
23383        // Roo.log("Call onRender: " + this.xtype);
23384         var _t = this;
23385         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23386       
23387         this.wrap = this.inputEl().wrap({
23388             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23389         });
23390         
23391         this.editorcore.onRender(ct, position);
23392          
23393         if (this.resizable) {
23394             this.resizeEl = new Roo.Resizable(this.wrap, {
23395                 pinned : true,
23396                 wrap: true,
23397                 dynamic : true,
23398                 minHeight : this.height,
23399                 height: this.height,
23400                 handles : this.resizable,
23401                 width: this.width,
23402                 listeners : {
23403                     resize : function(r, w, h) {
23404                         _t.onResize(w,h); // -something
23405                     }
23406                 }
23407             });
23408             
23409         }
23410         this.createToolbar(this);
23411        
23412         
23413         if(!this.width && this.resizable){
23414             this.setSize(this.wrap.getSize());
23415         }
23416         if (this.resizeEl) {
23417             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23418             // should trigger onReize..
23419         }
23420         
23421     },
23422
23423     // private
23424     onResize : function(w, h)
23425     {
23426         Roo.log('resize: ' +w + ',' + h );
23427         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23428         var ew = false;
23429         var eh = false;
23430         
23431         if(this.inputEl() ){
23432             if(typeof w == 'number'){
23433                 var aw = w - this.wrap.getFrameWidth('lr');
23434                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23435                 ew = aw;
23436             }
23437             if(typeof h == 'number'){
23438                  var tbh = -11;  // fixme it needs to tool bar size!
23439                 for (var i =0; i < this.toolbars.length;i++) {
23440                     // fixme - ask toolbars for heights?
23441                     tbh += this.toolbars[i].el.getHeight();
23442                     //if (this.toolbars[i].footer) {
23443                     //    tbh += this.toolbars[i].footer.el.getHeight();
23444                     //}
23445                 }
23446               
23447                 
23448                 
23449                 
23450                 
23451                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23452                 ah -= 5; // knock a few pixes off for look..
23453                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23454                 var eh = ah;
23455             }
23456         }
23457         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23458         this.editorcore.onResize(ew,eh);
23459         
23460     },
23461
23462     /**
23463      * Toggles the editor between standard and source edit mode.
23464      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23465      */
23466     toggleSourceEdit : function(sourceEditMode)
23467     {
23468         this.editorcore.toggleSourceEdit(sourceEditMode);
23469         
23470         if(this.editorcore.sourceEditMode){
23471             Roo.log('editor - showing textarea');
23472             
23473 //            Roo.log('in');
23474 //            Roo.log(this.syncValue());
23475             this.syncValue();
23476             this.inputEl().removeClass(['hide', 'x-hidden']);
23477             this.inputEl().dom.removeAttribute('tabIndex');
23478             this.inputEl().focus();
23479         }else{
23480             Roo.log('editor - hiding textarea');
23481 //            Roo.log('out')
23482 //            Roo.log(this.pushValue()); 
23483             this.pushValue();
23484             
23485             this.inputEl().addClass(['hide', 'x-hidden']);
23486             this.inputEl().dom.setAttribute('tabIndex', -1);
23487             //this.deferFocus();
23488         }
23489          
23490         if(this.resizable){
23491             this.setSize(this.wrap.getSize());
23492         }
23493         
23494         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23495     },
23496  
23497     // private (for BoxComponent)
23498     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23499
23500     // private (for BoxComponent)
23501     getResizeEl : function(){
23502         return this.wrap;
23503     },
23504
23505     // private (for BoxComponent)
23506     getPositionEl : function(){
23507         return this.wrap;
23508     },
23509
23510     // private
23511     initEvents : function(){
23512         this.originalValue = this.getValue();
23513     },
23514
23515 //    /**
23516 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23517 //     * @method
23518 //     */
23519 //    markInvalid : Roo.emptyFn,
23520 //    /**
23521 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23522 //     * @method
23523 //     */
23524 //    clearInvalid : Roo.emptyFn,
23525
23526     setValue : function(v){
23527         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23528         this.editorcore.pushValue();
23529     },
23530
23531      
23532     // private
23533     deferFocus : function(){
23534         this.focus.defer(10, this);
23535     },
23536
23537     // doc'ed in Field
23538     focus : function(){
23539         this.editorcore.focus();
23540         
23541     },
23542       
23543
23544     // private
23545     onDestroy : function(){
23546         
23547         
23548         
23549         if(this.rendered){
23550             
23551             for (var i =0; i < this.toolbars.length;i++) {
23552                 // fixme - ask toolbars for heights?
23553                 this.toolbars[i].onDestroy();
23554             }
23555             
23556             this.wrap.dom.innerHTML = '';
23557             this.wrap.remove();
23558         }
23559     },
23560
23561     // private
23562     onFirstFocus : function(){
23563         //Roo.log("onFirstFocus");
23564         this.editorcore.onFirstFocus();
23565          for (var i =0; i < this.toolbars.length;i++) {
23566             this.toolbars[i].onFirstFocus();
23567         }
23568         
23569     },
23570     
23571     // private
23572     syncValue : function()
23573     {   
23574         this.editorcore.syncValue();
23575     },
23576     
23577     pushValue : function()
23578     {   
23579         this.editorcore.pushValue();
23580     }
23581      
23582     
23583     // hide stuff that is not compatible
23584     /**
23585      * @event blur
23586      * @hide
23587      */
23588     /**
23589      * @event change
23590      * @hide
23591      */
23592     /**
23593      * @event focus
23594      * @hide
23595      */
23596     /**
23597      * @event specialkey
23598      * @hide
23599      */
23600     /**
23601      * @cfg {String} fieldClass @hide
23602      */
23603     /**
23604      * @cfg {String} focusClass @hide
23605      */
23606     /**
23607      * @cfg {String} autoCreate @hide
23608      */
23609     /**
23610      * @cfg {String} inputType @hide
23611      */
23612     /**
23613      * @cfg {String} invalidClass @hide
23614      */
23615     /**
23616      * @cfg {String} invalidText @hide
23617      */
23618     /**
23619      * @cfg {String} msgFx @hide
23620      */
23621     /**
23622      * @cfg {String} validateOnBlur @hide
23623      */
23624 });
23625  
23626     
23627    
23628    
23629    
23630       
23631 Roo.namespace('Roo.bootstrap.htmleditor');
23632 /**
23633  * @class Roo.bootstrap.HtmlEditorToolbar1
23634  * Basic Toolbar
23635  * 
23636  * Usage:
23637  *
23638  new Roo.bootstrap.HtmlEditor({
23639     ....
23640     toolbars : [
23641         new Roo.bootstrap.HtmlEditorToolbar1({
23642             disable : { fonts: 1 , format: 1, ..., ... , ...],
23643             btns : [ .... ]
23644         })
23645     }
23646      
23647  * 
23648  * @cfg {Object} disable List of elements to disable..
23649  * @cfg {Array} btns List of additional buttons.
23650  * 
23651  * 
23652  * NEEDS Extra CSS? 
23653  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23654  */
23655  
23656 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23657 {
23658     
23659     Roo.apply(this, config);
23660     
23661     // default disabled, based on 'good practice'..
23662     this.disable = this.disable || {};
23663     Roo.applyIf(this.disable, {
23664         fontSize : true,
23665         colors : true,
23666         specialElements : true
23667     });
23668     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23669     
23670     this.editor = config.editor;
23671     this.editorcore = config.editor.editorcore;
23672     
23673     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23674     
23675     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23676     // dont call parent... till later.
23677 }
23678 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23679      
23680     bar : true,
23681     
23682     editor : false,
23683     editorcore : false,
23684     
23685     
23686     formats : [
23687         "p" ,  
23688         "h1","h2","h3","h4","h5","h6", 
23689         "pre", "code", 
23690         "abbr", "acronym", "address", "cite", "samp", "var",
23691         'div','span'
23692     ],
23693     
23694     onRender : function(ct, position)
23695     {
23696        // Roo.log("Call onRender: " + this.xtype);
23697         
23698        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23699        Roo.log(this.el);
23700        this.el.dom.style.marginBottom = '0';
23701        var _this = this;
23702        var editorcore = this.editorcore;
23703        var editor= this.editor;
23704        
23705        var children = [];
23706        var btn = function(id,cmd , toggle, handler, html){
23707        
23708             var  event = toggle ? 'toggle' : 'click';
23709        
23710             var a = {
23711                 size : 'sm',
23712                 xtype: 'Button',
23713                 xns: Roo.bootstrap,
23714                 glyphicon : id,
23715                 cmd : id || cmd,
23716                 enableToggle:toggle !== false,
23717                 html : html || '',
23718                 pressed : toggle ? false : null,
23719                 listeners : {}
23720             };
23721             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23722                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23723             };
23724             children.push(a);
23725             return a;
23726        }
23727        
23728     //    var cb_box = function...
23729         
23730         var style = {
23731                 xtype: 'Button',
23732                 size : 'sm',
23733                 xns: Roo.bootstrap,
23734                 glyphicon : 'font',
23735                 //html : 'submit'
23736                 menu : {
23737                     xtype: 'Menu',
23738                     xns: Roo.bootstrap,
23739                     items:  []
23740                 }
23741         };
23742         Roo.each(this.formats, function(f) {
23743             style.menu.items.push({
23744                 xtype :'MenuItem',
23745                 xns: Roo.bootstrap,
23746                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23747                 tagname : f,
23748                 listeners : {
23749                     click : function()
23750                     {
23751                         editorcore.insertTag(this.tagname);
23752                         editor.focus();
23753                     }
23754                 }
23755                 
23756             });
23757         });
23758         children.push(style);   
23759         
23760         btn('bold',false,true);
23761         btn('italic',false,true);
23762         btn('align-left', 'justifyleft',true);
23763         btn('align-center', 'justifycenter',true);
23764         btn('align-right' , 'justifyright',true);
23765         btn('link', false, false, function(btn) {
23766             //Roo.log("create link?");
23767             var url = prompt(this.createLinkText, this.defaultLinkValue);
23768             if(url && url != 'http:/'+'/'){
23769                 this.editorcore.relayCmd('createlink', url);
23770             }
23771         }),
23772         btn('list','insertunorderedlist',true);
23773         btn('pencil', false,true, function(btn){
23774                 Roo.log(this);
23775                 this.toggleSourceEdit(btn.pressed);
23776         });
23777         
23778         if (this.editor.btns.length > 0) {
23779             for (var i = 0; i<this.editor.btns.length; i++) {
23780                 children.push(this.editor.btns[i]);
23781             }
23782         }
23783         
23784         /*
23785         var cog = {
23786                 xtype: 'Button',
23787                 size : 'sm',
23788                 xns: Roo.bootstrap,
23789                 glyphicon : 'cog',
23790                 //html : 'submit'
23791                 menu : {
23792                     xtype: 'Menu',
23793                     xns: Roo.bootstrap,
23794                     items:  []
23795                 }
23796         };
23797         
23798         cog.menu.items.push({
23799             xtype :'MenuItem',
23800             xns: Roo.bootstrap,
23801             html : Clean styles,
23802             tagname : f,
23803             listeners : {
23804                 click : function()
23805                 {
23806                     editorcore.insertTag(this.tagname);
23807                     editor.focus();
23808                 }
23809             }
23810             
23811         });
23812        */
23813         
23814          
23815        this.xtype = 'NavSimplebar';
23816         
23817         for(var i=0;i< children.length;i++) {
23818             
23819             this.buttons.add(this.addxtypeChild(children[i]));
23820             
23821         }
23822         
23823         editor.on('editorevent', this.updateToolbar, this);
23824     },
23825     onBtnClick : function(id)
23826     {
23827        this.editorcore.relayCmd(id);
23828        this.editorcore.focus();
23829     },
23830     
23831     /**
23832      * Protected method that will not generally be called directly. It triggers
23833      * a toolbar update by reading the markup state of the current selection in the editor.
23834      */
23835     updateToolbar: function(){
23836
23837         if(!this.editorcore.activated){
23838             this.editor.onFirstFocus(); // is this neeed?
23839             return;
23840         }
23841
23842         var btns = this.buttons; 
23843         var doc = this.editorcore.doc;
23844         btns.get('bold').setActive(doc.queryCommandState('bold'));
23845         btns.get('italic').setActive(doc.queryCommandState('italic'));
23846         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23847         
23848         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23849         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23850         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23851         
23852         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23853         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23854          /*
23855         
23856         var ans = this.editorcore.getAllAncestors();
23857         if (this.formatCombo) {
23858             
23859             
23860             var store = this.formatCombo.store;
23861             this.formatCombo.setValue("");
23862             for (var i =0; i < ans.length;i++) {
23863                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23864                     // select it..
23865                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23866                     break;
23867                 }
23868             }
23869         }
23870         
23871         
23872         
23873         // hides menus... - so this cant be on a menu...
23874         Roo.bootstrap.MenuMgr.hideAll();
23875         */
23876         Roo.bootstrap.MenuMgr.hideAll();
23877         //this.editorsyncValue();
23878     },
23879     onFirstFocus: function() {
23880         this.buttons.each(function(item){
23881            item.enable();
23882         });
23883     },
23884     toggleSourceEdit : function(sourceEditMode){
23885         
23886           
23887         if(sourceEditMode){
23888             Roo.log("disabling buttons");
23889            this.buttons.each( function(item){
23890                 if(item.cmd != 'pencil'){
23891                     item.disable();
23892                 }
23893             });
23894           
23895         }else{
23896             Roo.log("enabling buttons");
23897             if(this.editorcore.initialized){
23898                 this.buttons.each( function(item){
23899                     item.enable();
23900                 });
23901             }
23902             
23903         }
23904         Roo.log("calling toggole on editor");
23905         // tell the editor that it's been pressed..
23906         this.editor.toggleSourceEdit(sourceEditMode);
23907        
23908     }
23909 });
23910
23911
23912
23913
23914
23915 /**
23916  * @class Roo.bootstrap.Table.AbstractSelectionModel
23917  * @extends Roo.util.Observable
23918  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23919  * implemented by descendant classes.  This class should not be directly instantiated.
23920  * @constructor
23921  */
23922 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23923     this.locked = false;
23924     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23925 };
23926
23927
23928 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23929     /** @ignore Called by the grid automatically. Do not call directly. */
23930     init : function(grid){
23931         this.grid = grid;
23932         this.initEvents();
23933     },
23934
23935     /**
23936      * Locks the selections.
23937      */
23938     lock : function(){
23939         this.locked = true;
23940     },
23941
23942     /**
23943      * Unlocks the selections.
23944      */
23945     unlock : function(){
23946         this.locked = false;
23947     },
23948
23949     /**
23950      * Returns true if the selections are locked.
23951      * @return {Boolean}
23952      */
23953     isLocked : function(){
23954         return this.locked;
23955     }
23956 });
23957 /**
23958  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23959  * @class Roo.bootstrap.Table.RowSelectionModel
23960  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23961  * It supports multiple selections and keyboard selection/navigation. 
23962  * @constructor
23963  * @param {Object} config
23964  */
23965
23966 Roo.bootstrap.Table.RowSelectionModel = function(config){
23967     Roo.apply(this, config);
23968     this.selections = new Roo.util.MixedCollection(false, function(o){
23969         return o.id;
23970     });
23971
23972     this.last = false;
23973     this.lastActive = false;
23974
23975     this.addEvents({
23976         /**
23977              * @event selectionchange
23978              * Fires when the selection changes
23979              * @param {SelectionModel} this
23980              */
23981             "selectionchange" : true,
23982         /**
23983              * @event afterselectionchange
23984              * Fires after the selection changes (eg. by key press or clicking)
23985              * @param {SelectionModel} this
23986              */
23987             "afterselectionchange" : true,
23988         /**
23989              * @event beforerowselect
23990              * Fires when a row is selected being selected, return false to cancel.
23991              * @param {SelectionModel} this
23992              * @param {Number} rowIndex The selected index
23993              * @param {Boolean} keepExisting False if other selections will be cleared
23994              */
23995             "beforerowselect" : true,
23996         /**
23997              * @event rowselect
23998              * Fires when a row is selected.
23999              * @param {SelectionModel} this
24000              * @param {Number} rowIndex The selected index
24001              * @param {Roo.data.Record} r The record
24002              */
24003             "rowselect" : true,
24004         /**
24005              * @event rowdeselect
24006              * Fires when a row is deselected.
24007              * @param {SelectionModel} this
24008              * @param {Number} rowIndex The selected index
24009              */
24010         "rowdeselect" : true
24011     });
24012     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24013     this.locked = false;
24014  };
24015
24016 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24017     /**
24018      * @cfg {Boolean} singleSelect
24019      * True to allow selection of only one row at a time (defaults to false)
24020      */
24021     singleSelect : false,
24022
24023     // private
24024     initEvents : function()
24025     {
24026
24027         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24028         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24029         //}else{ // allow click to work like normal
24030          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24031         //}
24032         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24033         this.grid.on("rowclick", this.handleMouseDown, this);
24034         
24035         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24036             "up" : function(e){
24037                 if(!e.shiftKey){
24038                     this.selectPrevious(e.shiftKey);
24039                 }else if(this.last !== false && this.lastActive !== false){
24040                     var last = this.last;
24041                     this.selectRange(this.last,  this.lastActive-1);
24042                     this.grid.getView().focusRow(this.lastActive);
24043                     if(last !== false){
24044                         this.last = last;
24045                     }
24046                 }else{
24047                     this.selectFirstRow();
24048                 }
24049                 this.fireEvent("afterselectionchange", this);
24050             },
24051             "down" : function(e){
24052                 if(!e.shiftKey){
24053                     this.selectNext(e.shiftKey);
24054                 }else if(this.last !== false && this.lastActive !== false){
24055                     var last = this.last;
24056                     this.selectRange(this.last,  this.lastActive+1);
24057                     this.grid.getView().focusRow(this.lastActive);
24058                     if(last !== false){
24059                         this.last = last;
24060                     }
24061                 }else{
24062                     this.selectFirstRow();
24063                 }
24064                 this.fireEvent("afterselectionchange", this);
24065             },
24066             scope: this
24067         });
24068         this.grid.store.on('load', function(){
24069             this.selections.clear();
24070         },this);
24071         /*
24072         var view = this.grid.view;
24073         view.on("refresh", this.onRefresh, this);
24074         view.on("rowupdated", this.onRowUpdated, this);
24075         view.on("rowremoved", this.onRemove, this);
24076         */
24077     },
24078
24079     // private
24080     onRefresh : function()
24081     {
24082         var ds = this.grid.store, i, v = this.grid.view;
24083         var s = this.selections;
24084         s.each(function(r){
24085             if((i = ds.indexOfId(r.id)) != -1){
24086                 v.onRowSelect(i);
24087             }else{
24088                 s.remove(r);
24089             }
24090         });
24091     },
24092
24093     // private
24094     onRemove : function(v, index, r){
24095         this.selections.remove(r);
24096     },
24097
24098     // private
24099     onRowUpdated : function(v, index, r){
24100         if(this.isSelected(r)){
24101             v.onRowSelect(index);
24102         }
24103     },
24104
24105     /**
24106      * Select records.
24107      * @param {Array} records The records to select
24108      * @param {Boolean} keepExisting (optional) True to keep existing selections
24109      */
24110     selectRecords : function(records, keepExisting)
24111     {
24112         if(!keepExisting){
24113             this.clearSelections();
24114         }
24115             var ds = this.grid.store;
24116         for(var i = 0, len = records.length; i < len; i++){
24117             this.selectRow(ds.indexOf(records[i]), true);
24118         }
24119     },
24120
24121     /**
24122      * Gets the number of selected rows.
24123      * @return {Number}
24124      */
24125     getCount : function(){
24126         return this.selections.length;
24127     },
24128
24129     /**
24130      * Selects the first row in the grid.
24131      */
24132     selectFirstRow : function(){
24133         this.selectRow(0);
24134     },
24135
24136     /**
24137      * Select the last row.
24138      * @param {Boolean} keepExisting (optional) True to keep existing selections
24139      */
24140     selectLastRow : function(keepExisting){
24141         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24142         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24143     },
24144
24145     /**
24146      * Selects the row immediately following the last selected row.
24147      * @param {Boolean} keepExisting (optional) True to keep existing selections
24148      */
24149     selectNext : function(keepExisting)
24150     {
24151             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24152             this.selectRow(this.last+1, keepExisting);
24153             this.grid.getView().focusRow(this.last);
24154         }
24155     },
24156
24157     /**
24158      * Selects the row that precedes the last selected row.
24159      * @param {Boolean} keepExisting (optional) True to keep existing selections
24160      */
24161     selectPrevious : function(keepExisting){
24162         if(this.last){
24163             this.selectRow(this.last-1, keepExisting);
24164             this.grid.getView().focusRow(this.last);
24165         }
24166     },
24167
24168     /**
24169      * Returns the selected records
24170      * @return {Array} Array of selected records
24171      */
24172     getSelections : function(){
24173         return [].concat(this.selections.items);
24174     },
24175
24176     /**
24177      * Returns the first selected record.
24178      * @return {Record}
24179      */
24180     getSelected : function(){
24181         return this.selections.itemAt(0);
24182     },
24183
24184
24185     /**
24186      * Clears all selections.
24187      */
24188     clearSelections : function(fast)
24189     {
24190         if(this.locked) {
24191             return;
24192         }
24193         if(fast !== true){
24194                 var ds = this.grid.store;
24195             var s = this.selections;
24196             s.each(function(r){
24197                 this.deselectRow(ds.indexOfId(r.id));
24198             }, this);
24199             s.clear();
24200         }else{
24201             this.selections.clear();
24202         }
24203         this.last = false;
24204     },
24205
24206
24207     /**
24208      * Selects all rows.
24209      */
24210     selectAll : function(){
24211         if(this.locked) {
24212             return;
24213         }
24214         this.selections.clear();
24215         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24216             this.selectRow(i, true);
24217         }
24218     },
24219
24220     /**
24221      * Returns True if there is a selection.
24222      * @return {Boolean}
24223      */
24224     hasSelection : function(){
24225         return this.selections.length > 0;
24226     },
24227
24228     /**
24229      * Returns True if the specified row is selected.
24230      * @param {Number/Record} record The record or index of the record to check
24231      * @return {Boolean}
24232      */
24233     isSelected : function(index){
24234             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24235         return (r && this.selections.key(r.id) ? true : false);
24236     },
24237
24238     /**
24239      * Returns True if the specified record id is selected.
24240      * @param {String} id The id of record to check
24241      * @return {Boolean}
24242      */
24243     isIdSelected : function(id){
24244         return (this.selections.key(id) ? true : false);
24245     },
24246
24247
24248     // private
24249     handleMouseDBClick : function(e, t){
24250         
24251     },
24252     // private
24253     handleMouseDown : function(e, t)
24254     {
24255             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24256         if(this.isLocked() || rowIndex < 0 ){
24257             return;
24258         };
24259         if(e.shiftKey && this.last !== false){
24260             var last = this.last;
24261             this.selectRange(last, rowIndex, e.ctrlKey);
24262             this.last = last; // reset the last
24263             t.focus();
24264     
24265         }else{
24266             var isSelected = this.isSelected(rowIndex);
24267             //Roo.log("select row:" + rowIndex);
24268             if(isSelected){
24269                 this.deselectRow(rowIndex);
24270             } else {
24271                         this.selectRow(rowIndex, true);
24272             }
24273     
24274             /*
24275                 if(e.button !== 0 && isSelected){
24276                 alert('rowIndex 2: ' + rowIndex);
24277                     view.focusRow(rowIndex);
24278                 }else if(e.ctrlKey && isSelected){
24279                     this.deselectRow(rowIndex);
24280                 }else if(!isSelected){
24281                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24282                     view.focusRow(rowIndex);
24283                 }
24284             */
24285         }
24286         this.fireEvent("afterselectionchange", this);
24287     },
24288     // private
24289     handleDragableRowClick :  function(grid, rowIndex, e) 
24290     {
24291         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24292             this.selectRow(rowIndex, false);
24293             grid.view.focusRow(rowIndex);
24294              this.fireEvent("afterselectionchange", this);
24295         }
24296     },
24297     
24298     /**
24299      * Selects multiple rows.
24300      * @param {Array} rows Array of the indexes of the row to select
24301      * @param {Boolean} keepExisting (optional) True to keep existing selections
24302      */
24303     selectRows : function(rows, keepExisting){
24304         if(!keepExisting){
24305             this.clearSelections();
24306         }
24307         for(var i = 0, len = rows.length; i < len; i++){
24308             this.selectRow(rows[i], true);
24309         }
24310     },
24311
24312     /**
24313      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24314      * @param {Number} startRow The index of the first row in the range
24315      * @param {Number} endRow The index of the last row in the range
24316      * @param {Boolean} keepExisting (optional) True to retain existing selections
24317      */
24318     selectRange : function(startRow, endRow, keepExisting){
24319         if(this.locked) {
24320             return;
24321         }
24322         if(!keepExisting){
24323             this.clearSelections();
24324         }
24325         if(startRow <= endRow){
24326             for(var i = startRow; i <= endRow; i++){
24327                 this.selectRow(i, true);
24328             }
24329         }else{
24330             for(var i = startRow; i >= endRow; i--){
24331                 this.selectRow(i, true);
24332             }
24333         }
24334     },
24335
24336     /**
24337      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24338      * @param {Number} startRow The index of the first row in the range
24339      * @param {Number} endRow The index of the last row in the range
24340      */
24341     deselectRange : function(startRow, endRow, preventViewNotify){
24342         if(this.locked) {
24343             return;
24344         }
24345         for(var i = startRow; i <= endRow; i++){
24346             this.deselectRow(i, preventViewNotify);
24347         }
24348     },
24349
24350     /**
24351      * Selects a row.
24352      * @param {Number} row The index of the row to select
24353      * @param {Boolean} keepExisting (optional) True to keep existing selections
24354      */
24355     selectRow : function(index, keepExisting, preventViewNotify)
24356     {
24357             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24358             return;
24359         }
24360         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24361             if(!keepExisting || this.singleSelect){
24362                 this.clearSelections();
24363             }
24364             
24365             var r = this.grid.store.getAt(index);
24366             //console.log('selectRow - record id :' + r.id);
24367             
24368             this.selections.add(r);
24369             this.last = this.lastActive = index;
24370             if(!preventViewNotify){
24371                 var proxy = new Roo.Element(
24372                                 this.grid.getRowDom(index)
24373                 );
24374                 proxy.addClass('bg-info info');
24375             }
24376             this.fireEvent("rowselect", this, index, r);
24377             this.fireEvent("selectionchange", this);
24378         }
24379     },
24380
24381     /**
24382      * Deselects a row.
24383      * @param {Number} row The index of the row to deselect
24384      */
24385     deselectRow : function(index, preventViewNotify)
24386     {
24387         if(this.locked) {
24388             return;
24389         }
24390         if(this.last == index){
24391             this.last = false;
24392         }
24393         if(this.lastActive == index){
24394             this.lastActive = false;
24395         }
24396         
24397         var r = this.grid.store.getAt(index);
24398         if (!r) {
24399             return;
24400         }
24401         
24402         this.selections.remove(r);
24403         //.console.log('deselectRow - record id :' + r.id);
24404         if(!preventViewNotify){
24405         
24406             var proxy = new Roo.Element(
24407                 this.grid.getRowDom(index)
24408             );
24409             proxy.removeClass('bg-info info');
24410         }
24411         this.fireEvent("rowdeselect", this, index);
24412         this.fireEvent("selectionchange", this);
24413     },
24414
24415     // private
24416     restoreLast : function(){
24417         if(this._last){
24418             this.last = this._last;
24419         }
24420     },
24421
24422     // private
24423     acceptsNav : function(row, col, cm){
24424         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24425     },
24426
24427     // private
24428     onEditorKey : function(field, e){
24429         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24430         if(k == e.TAB){
24431             e.stopEvent();
24432             ed.completeEdit();
24433             if(e.shiftKey){
24434                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24435             }else{
24436                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24437             }
24438         }else if(k == e.ENTER && !e.ctrlKey){
24439             e.stopEvent();
24440             ed.completeEdit();
24441             if(e.shiftKey){
24442                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24443             }else{
24444                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24445             }
24446         }else if(k == e.ESC){
24447             ed.cancelEdit();
24448         }
24449         if(newCell){
24450             g.startEditing(newCell[0], newCell[1]);
24451         }
24452     }
24453 });
24454 /*
24455  * Based on:
24456  * Ext JS Library 1.1.1
24457  * Copyright(c) 2006-2007, Ext JS, LLC.
24458  *
24459  * Originally Released Under LGPL - original licence link has changed is not relivant.
24460  *
24461  * Fork - LGPL
24462  * <script type="text/javascript">
24463  */
24464  
24465 /**
24466  * @class Roo.bootstrap.PagingToolbar
24467  * @extends Roo.bootstrap.NavSimplebar
24468  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24469  * @constructor
24470  * Create a new PagingToolbar
24471  * @param {Object} config The config object
24472  * @param {Roo.data.Store} store
24473  */
24474 Roo.bootstrap.PagingToolbar = function(config)
24475 {
24476     // old args format still supported... - xtype is prefered..
24477         // created from xtype...
24478     
24479     this.ds = config.dataSource;
24480     
24481     if (config.store && !this.ds) {
24482         this.store= Roo.factory(config.store, Roo.data);
24483         this.ds = this.store;
24484         this.ds.xmodule = this.xmodule || false;
24485     }
24486     
24487     this.toolbarItems = [];
24488     if (config.items) {
24489         this.toolbarItems = config.items;
24490     }
24491     
24492     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24493     
24494     this.cursor = 0;
24495     
24496     if (this.ds) { 
24497         this.bind(this.ds);
24498     }
24499     
24500     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24501     
24502 };
24503
24504 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24505     /**
24506      * @cfg {Roo.data.Store} dataSource
24507      * The underlying data store providing the paged data
24508      */
24509     /**
24510      * @cfg {String/HTMLElement/Element} container
24511      * container The id or element that will contain the toolbar
24512      */
24513     /**
24514      * @cfg {Boolean} displayInfo
24515      * True to display the displayMsg (defaults to false)
24516      */
24517     /**
24518      * @cfg {Number} pageSize
24519      * The number of records to display per page (defaults to 20)
24520      */
24521     pageSize: 20,
24522     /**
24523      * @cfg {String} displayMsg
24524      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24525      */
24526     displayMsg : 'Displaying {0} - {1} of {2}',
24527     /**
24528      * @cfg {String} emptyMsg
24529      * The message to display when no records are found (defaults to "No data to display")
24530      */
24531     emptyMsg : 'No data to display',
24532     /**
24533      * Customizable piece of the default paging text (defaults to "Page")
24534      * @type String
24535      */
24536     beforePageText : "Page",
24537     /**
24538      * Customizable piece of the default paging text (defaults to "of %0")
24539      * @type String
24540      */
24541     afterPageText : "of {0}",
24542     /**
24543      * Customizable piece of the default paging text (defaults to "First Page")
24544      * @type String
24545      */
24546     firstText : "First Page",
24547     /**
24548      * Customizable piece of the default paging text (defaults to "Previous Page")
24549      * @type String
24550      */
24551     prevText : "Previous Page",
24552     /**
24553      * Customizable piece of the default paging text (defaults to "Next Page")
24554      * @type String
24555      */
24556     nextText : "Next Page",
24557     /**
24558      * Customizable piece of the default paging text (defaults to "Last Page")
24559      * @type String
24560      */
24561     lastText : "Last Page",
24562     /**
24563      * Customizable piece of the default paging text (defaults to "Refresh")
24564      * @type String
24565      */
24566     refreshText : "Refresh",
24567
24568     buttons : false,
24569     // private
24570     onRender : function(ct, position) 
24571     {
24572         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24573         this.navgroup.parentId = this.id;
24574         this.navgroup.onRender(this.el, null);
24575         // add the buttons to the navgroup
24576         
24577         if(this.displayInfo){
24578             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24579             this.displayEl = this.el.select('.x-paging-info', true).first();
24580 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24581 //            this.displayEl = navel.el.select('span',true).first();
24582         }
24583         
24584         var _this = this;
24585         
24586         if(this.buttons){
24587             Roo.each(_this.buttons, function(e){ // this might need to use render????
24588                Roo.factory(e).render(_this.el);
24589             });
24590         }
24591             
24592         Roo.each(_this.toolbarItems, function(e) {
24593             _this.navgroup.addItem(e);
24594         });
24595         
24596         
24597         this.first = this.navgroup.addItem({
24598             tooltip: this.firstText,
24599             cls: "prev",
24600             icon : 'fa fa-backward',
24601             disabled: true,
24602             preventDefault: true,
24603             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24604         });
24605         
24606         this.prev =  this.navgroup.addItem({
24607             tooltip: this.prevText,
24608             cls: "prev",
24609             icon : 'fa fa-step-backward',
24610             disabled: true,
24611             preventDefault: true,
24612             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24613         });
24614     //this.addSeparator();
24615         
24616         
24617         var field = this.navgroup.addItem( {
24618             tagtype : 'span',
24619             cls : 'x-paging-position',
24620             
24621             html : this.beforePageText  +
24622                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24623                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24624          } ); //?? escaped?
24625         
24626         this.field = field.el.select('input', true).first();
24627         this.field.on("keydown", this.onPagingKeydown, this);
24628         this.field.on("focus", function(){this.dom.select();});
24629     
24630     
24631         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24632         //this.field.setHeight(18);
24633         //this.addSeparator();
24634         this.next = this.navgroup.addItem({
24635             tooltip: this.nextText,
24636             cls: "next",
24637             html : ' <i class="fa fa-step-forward">',
24638             disabled: true,
24639             preventDefault: true,
24640             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24641         });
24642         this.last = this.navgroup.addItem({
24643             tooltip: this.lastText,
24644             icon : 'fa fa-forward',
24645             cls: "next",
24646             disabled: true,
24647             preventDefault: true,
24648             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24649         });
24650     //this.addSeparator();
24651         this.loading = this.navgroup.addItem({
24652             tooltip: this.refreshText,
24653             icon: 'fa fa-refresh',
24654             preventDefault: true,
24655             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24656         });
24657         
24658     },
24659
24660     // private
24661     updateInfo : function(){
24662         if(this.displayEl){
24663             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24664             var msg = count == 0 ?
24665                 this.emptyMsg :
24666                 String.format(
24667                     this.displayMsg,
24668                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24669                 );
24670             this.displayEl.update(msg);
24671         }
24672     },
24673
24674     // private
24675     onLoad : function(ds, r, o)
24676     {
24677         this.cursor = o.params.start ? o.params.start : 0;
24678         
24679         var d = this.getPageData(),
24680             ap = d.activePage,
24681             ps = d.pages;
24682         
24683         
24684         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24685         this.field.dom.value = ap;
24686         this.first.setDisabled(ap == 1);
24687         this.prev.setDisabled(ap == 1);
24688         this.next.setDisabled(ap == ps);
24689         this.last.setDisabled(ap == ps);
24690         this.loading.enable();
24691         this.updateInfo();
24692     },
24693
24694     // private
24695     getPageData : function(){
24696         var total = this.ds.getTotalCount();
24697         return {
24698             total : total,
24699             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24700             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24701         };
24702     },
24703
24704     // private
24705     onLoadError : function(){
24706         this.loading.enable();
24707     },
24708
24709     // private
24710     onPagingKeydown : function(e){
24711         var k = e.getKey();
24712         var d = this.getPageData();
24713         if(k == e.RETURN){
24714             var v = this.field.dom.value, pageNum;
24715             if(!v || isNaN(pageNum = parseInt(v, 10))){
24716                 this.field.dom.value = d.activePage;
24717                 return;
24718             }
24719             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24720             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24721             e.stopEvent();
24722         }
24723         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))
24724         {
24725           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24726           this.field.dom.value = pageNum;
24727           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24728           e.stopEvent();
24729         }
24730         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24731         {
24732           var v = this.field.dom.value, pageNum; 
24733           var increment = (e.shiftKey) ? 10 : 1;
24734           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24735                 increment *= -1;
24736           }
24737           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24738             this.field.dom.value = d.activePage;
24739             return;
24740           }
24741           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24742           {
24743             this.field.dom.value = parseInt(v, 10) + increment;
24744             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24745             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24746           }
24747           e.stopEvent();
24748         }
24749     },
24750
24751     // private
24752     beforeLoad : function(){
24753         if(this.loading){
24754             this.loading.disable();
24755         }
24756     },
24757
24758     // private
24759     onClick : function(which){
24760         
24761         var ds = this.ds;
24762         if (!ds) {
24763             return;
24764         }
24765         
24766         switch(which){
24767             case "first":
24768                 ds.load({params:{start: 0, limit: this.pageSize}});
24769             break;
24770             case "prev":
24771                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24772             break;
24773             case "next":
24774                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24775             break;
24776             case "last":
24777                 var total = ds.getTotalCount();
24778                 var extra = total % this.pageSize;
24779                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24780                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24781             break;
24782             case "refresh":
24783                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24784             break;
24785         }
24786     },
24787
24788     /**
24789      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24790      * @param {Roo.data.Store} store The data store to unbind
24791      */
24792     unbind : function(ds){
24793         ds.un("beforeload", this.beforeLoad, this);
24794         ds.un("load", this.onLoad, this);
24795         ds.un("loadexception", this.onLoadError, this);
24796         ds.un("remove", this.updateInfo, this);
24797         ds.un("add", this.updateInfo, this);
24798         this.ds = undefined;
24799     },
24800
24801     /**
24802      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24803      * @param {Roo.data.Store} store The data store to bind
24804      */
24805     bind : function(ds){
24806         ds.on("beforeload", this.beforeLoad, this);
24807         ds.on("load", this.onLoad, this);
24808         ds.on("loadexception", this.onLoadError, this);
24809         ds.on("remove", this.updateInfo, this);
24810         ds.on("add", this.updateInfo, this);
24811         this.ds = ds;
24812     }
24813 });/*
24814  * - LGPL
24815  *
24816  * element
24817  * 
24818  */
24819
24820 /**
24821  * @class Roo.bootstrap.MessageBar
24822  * @extends Roo.bootstrap.Component
24823  * Bootstrap MessageBar class
24824  * @cfg {String} html contents of the MessageBar
24825  * @cfg {String} weight (info | success | warning | danger) default info
24826  * @cfg {String} beforeClass insert the bar before the given class
24827  * @cfg {Boolean} closable (true | false) default false
24828  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24829  * 
24830  * @constructor
24831  * Create a new Element
24832  * @param {Object} config The config object
24833  */
24834
24835 Roo.bootstrap.MessageBar = function(config){
24836     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24837 };
24838
24839 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24840     
24841     html: '',
24842     weight: 'info',
24843     closable: false,
24844     fixed: false,
24845     beforeClass: 'bootstrap-sticky-wrap',
24846     
24847     getAutoCreate : function(){
24848         
24849         var cfg = {
24850             tag: 'div',
24851             cls: 'alert alert-dismissable alert-' + this.weight,
24852             cn: [
24853                 {
24854                     tag: 'span',
24855                     cls: 'message',
24856                     html: this.html || ''
24857                 }
24858             ]
24859         };
24860         
24861         if(this.fixed){
24862             cfg.cls += ' alert-messages-fixed';
24863         }
24864         
24865         if(this.closable){
24866             cfg.cn.push({
24867                 tag: 'button',
24868                 cls: 'close',
24869                 html: 'x'
24870             });
24871         }
24872         
24873         return cfg;
24874     },
24875     
24876     onRender : function(ct, position)
24877     {
24878         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24879         
24880         if(!this.el){
24881             var cfg = Roo.apply({},  this.getAutoCreate());
24882             cfg.id = Roo.id();
24883             
24884             if (this.cls) {
24885                 cfg.cls += ' ' + this.cls;
24886             }
24887             if (this.style) {
24888                 cfg.style = this.style;
24889             }
24890             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24891             
24892             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24893         }
24894         
24895         this.el.select('>button.close').on('click', this.hide, this);
24896         
24897     },
24898     
24899     show : function()
24900     {
24901         if (!this.rendered) {
24902             this.render();
24903         }
24904         
24905         this.el.show();
24906         
24907         this.fireEvent('show', this);
24908         
24909     },
24910     
24911     hide : function()
24912     {
24913         if (!this.rendered) {
24914             this.render();
24915         }
24916         
24917         this.el.hide();
24918         
24919         this.fireEvent('hide', this);
24920     },
24921     
24922     update : function()
24923     {
24924 //        var e = this.el.dom.firstChild;
24925 //        
24926 //        if(this.closable){
24927 //            e = e.nextSibling;
24928 //        }
24929 //        
24930 //        e.data = this.html || '';
24931
24932         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24933     }
24934    
24935 });
24936
24937  
24938
24939      /*
24940  * - LGPL
24941  *
24942  * Graph
24943  * 
24944  */
24945
24946
24947 /**
24948  * @class Roo.bootstrap.Graph
24949  * @extends Roo.bootstrap.Component
24950  * Bootstrap Graph class
24951 > Prameters
24952  -sm {number} sm 4
24953  -md {number} md 5
24954  @cfg {String} graphtype  bar | vbar | pie
24955  @cfg {number} g_x coodinator | centre x (pie)
24956  @cfg {number} g_y coodinator | centre y (pie)
24957  @cfg {number} g_r radius (pie)
24958  @cfg {number} g_height height of the chart (respected by all elements in the set)
24959  @cfg {number} g_width width of the chart (respected by all elements in the set)
24960  @cfg {Object} title The title of the chart
24961     
24962  -{Array}  values
24963  -opts (object) options for the chart 
24964      o {
24965      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24966      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24967      o vgutter (number)
24968      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.
24969      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24970      o to
24971      o stretch (boolean)
24972      o }
24973  -opts (object) options for the pie
24974      o{
24975      o cut
24976      o startAngle (number)
24977      o endAngle (number)
24978      } 
24979  *
24980  * @constructor
24981  * Create a new Input
24982  * @param {Object} config The config object
24983  */
24984
24985 Roo.bootstrap.Graph = function(config){
24986     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24987     
24988     this.addEvents({
24989         // img events
24990         /**
24991          * @event click
24992          * The img click event for the img.
24993          * @param {Roo.EventObject} e
24994          */
24995         "click" : true
24996     });
24997 };
24998
24999 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25000     
25001     sm: 4,
25002     md: 5,
25003     graphtype: 'bar',
25004     g_height: 250,
25005     g_width: 400,
25006     g_x: 50,
25007     g_y: 50,
25008     g_r: 30,
25009     opts:{
25010         //g_colors: this.colors,
25011         g_type: 'soft',
25012         g_gutter: '20%'
25013
25014     },
25015     title : false,
25016
25017     getAutoCreate : function(){
25018         
25019         var cfg = {
25020             tag: 'div',
25021             html : null
25022         };
25023         
25024         
25025         return  cfg;
25026     },
25027
25028     onRender : function(ct,position){
25029         
25030         
25031         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25032         
25033         if (typeof(Raphael) == 'undefined') {
25034             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25035             return;
25036         }
25037         
25038         this.raphael = Raphael(this.el.dom);
25039         
25040                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25041                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25042                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25043                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25044                 /*
25045                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25046                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25047                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25048                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25049                 
25050                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25051                 r.barchart(330, 10, 300, 220, data1);
25052                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25053                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25054                 */
25055                 
25056                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25057                 // r.barchart(30, 30, 560, 250,  xdata, {
25058                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25059                 //     axis : "0 0 1 1",
25060                 //     axisxlabels :  xdata
25061                 //     //yvalues : cols,
25062                    
25063                 // });
25064 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25065 //        
25066 //        this.load(null,xdata,{
25067 //                axis : "0 0 1 1",
25068 //                axisxlabels :  xdata
25069 //                });
25070
25071     },
25072
25073     load : function(graphtype,xdata,opts)
25074     {
25075         this.raphael.clear();
25076         if(!graphtype) {
25077             graphtype = this.graphtype;
25078         }
25079         if(!opts){
25080             opts = this.opts;
25081         }
25082         var r = this.raphael,
25083             fin = function () {
25084                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25085             },
25086             fout = function () {
25087                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25088             },
25089             pfin = function() {
25090                 this.sector.stop();
25091                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25092
25093                 if (this.label) {
25094                     this.label[0].stop();
25095                     this.label[0].attr({ r: 7.5 });
25096                     this.label[1].attr({ "font-weight": 800 });
25097                 }
25098             },
25099             pfout = function() {
25100                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25101
25102                 if (this.label) {
25103                     this.label[0].animate({ r: 5 }, 500, "bounce");
25104                     this.label[1].attr({ "font-weight": 400 });
25105                 }
25106             };
25107
25108         switch(graphtype){
25109             case 'bar':
25110                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25111                 break;
25112             case 'hbar':
25113                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25114                 break;
25115             case 'pie':
25116 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25117 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25118 //            
25119                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25120                 
25121                 break;
25122
25123         }
25124         
25125         if(this.title){
25126             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25127         }
25128         
25129     },
25130     
25131     setTitle: function(o)
25132     {
25133         this.title = o;
25134     },
25135     
25136     initEvents: function() {
25137         
25138         if(!this.href){
25139             this.el.on('click', this.onClick, this);
25140         }
25141     },
25142     
25143     onClick : function(e)
25144     {
25145         Roo.log('img onclick');
25146         this.fireEvent('click', this, e);
25147     }
25148    
25149 });
25150
25151  
25152 /*
25153  * - LGPL
25154  *
25155  * numberBox
25156  * 
25157  */
25158 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25159
25160 /**
25161  * @class Roo.bootstrap.dash.NumberBox
25162  * @extends Roo.bootstrap.Component
25163  * Bootstrap NumberBox class
25164  * @cfg {String} headline Box headline
25165  * @cfg {String} content Box content
25166  * @cfg {String} icon Box icon
25167  * @cfg {String} footer Footer text
25168  * @cfg {String} fhref Footer href
25169  * 
25170  * @constructor
25171  * Create a new NumberBox
25172  * @param {Object} config The config object
25173  */
25174
25175
25176 Roo.bootstrap.dash.NumberBox = function(config){
25177     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25178     
25179 };
25180
25181 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25182     
25183     headline : '',
25184     content : '',
25185     icon : '',
25186     footer : '',
25187     fhref : '',
25188     ficon : '',
25189     
25190     getAutoCreate : function(){
25191         
25192         var cfg = {
25193             tag : 'div',
25194             cls : 'small-box ',
25195             cn : [
25196                 {
25197                     tag : 'div',
25198                     cls : 'inner',
25199                     cn :[
25200                         {
25201                             tag : 'h3',
25202                             cls : 'roo-headline',
25203                             html : this.headline
25204                         },
25205                         {
25206                             tag : 'p',
25207                             cls : 'roo-content',
25208                             html : this.content
25209                         }
25210                     ]
25211                 }
25212             ]
25213         };
25214         
25215         if(this.icon){
25216             cfg.cn.push({
25217                 tag : 'div',
25218                 cls : 'icon',
25219                 cn :[
25220                     {
25221                         tag : 'i',
25222                         cls : 'ion ' + this.icon
25223                     }
25224                 ]
25225             });
25226         }
25227         
25228         if(this.footer){
25229             var footer = {
25230                 tag : 'a',
25231                 cls : 'small-box-footer',
25232                 href : this.fhref || '#',
25233                 html : this.footer
25234             };
25235             
25236             cfg.cn.push(footer);
25237             
25238         }
25239         
25240         return  cfg;
25241     },
25242
25243     onRender : function(ct,position){
25244         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25245
25246
25247        
25248                 
25249     },
25250
25251     setHeadline: function (value)
25252     {
25253         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25254     },
25255     
25256     setFooter: function (value, href)
25257     {
25258         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25259         
25260         if(href){
25261             this.el.select('a.small-box-footer',true).first().attr('href', href);
25262         }
25263         
25264     },
25265
25266     setContent: function (value)
25267     {
25268         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25269     },
25270
25271     initEvents: function() 
25272     {   
25273         
25274     }
25275     
25276 });
25277
25278  
25279 /*
25280  * - LGPL
25281  *
25282  * TabBox
25283  * 
25284  */
25285 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25286
25287 /**
25288  * @class Roo.bootstrap.dash.TabBox
25289  * @extends Roo.bootstrap.Component
25290  * Bootstrap TabBox class
25291  * @cfg {String} title Title of the TabBox
25292  * @cfg {String} icon Icon of the TabBox
25293  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25294  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25295  * 
25296  * @constructor
25297  * Create a new TabBox
25298  * @param {Object} config The config object
25299  */
25300
25301
25302 Roo.bootstrap.dash.TabBox = function(config){
25303     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25304     this.addEvents({
25305         // raw events
25306         /**
25307          * @event addpane
25308          * When a pane is added
25309          * @param {Roo.bootstrap.dash.TabPane} pane
25310          */
25311         "addpane" : true,
25312         /**
25313          * @event activatepane
25314          * When a pane is activated
25315          * @param {Roo.bootstrap.dash.TabPane} pane
25316          */
25317         "activatepane" : true
25318         
25319          
25320     });
25321     
25322     this.panes = [];
25323 };
25324
25325 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25326
25327     title : '',
25328     icon : false,
25329     showtabs : true,
25330     tabScrollable : false,
25331     
25332     getChildContainer : function()
25333     {
25334         return this.el.select('.tab-content', true).first();
25335     },
25336     
25337     getAutoCreate : function(){
25338         
25339         var header = {
25340             tag: 'li',
25341             cls: 'pull-left header',
25342             html: this.title,
25343             cn : []
25344         };
25345         
25346         if(this.icon){
25347             header.cn.push({
25348                 tag: 'i',
25349                 cls: 'fa ' + this.icon
25350             });
25351         }
25352         
25353         var h = {
25354             tag: 'ul',
25355             cls: 'nav nav-tabs pull-right',
25356             cn: [
25357                 header
25358             ]
25359         };
25360         
25361         if(this.tabScrollable){
25362             h = {
25363                 tag: 'div',
25364                 cls: 'tab-header',
25365                 cn: [
25366                     {
25367                         tag: 'ul',
25368                         cls: 'nav nav-tabs pull-right',
25369                         cn: [
25370                             header
25371                         ]
25372                     }
25373                 ]
25374             };
25375         }
25376         
25377         var cfg = {
25378             tag: 'div',
25379             cls: 'nav-tabs-custom',
25380             cn: [
25381                 h,
25382                 {
25383                     tag: 'div',
25384                     cls: 'tab-content no-padding',
25385                     cn: []
25386                 }
25387             ]
25388         };
25389
25390         return  cfg;
25391     },
25392     initEvents : function()
25393     {
25394         //Roo.log('add add pane handler');
25395         this.on('addpane', this.onAddPane, this);
25396     },
25397      /**
25398      * Updates the box title
25399      * @param {String} html to set the title to.
25400      */
25401     setTitle : function(value)
25402     {
25403         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25404     },
25405     onAddPane : function(pane)
25406     {
25407         this.panes.push(pane);
25408         //Roo.log('addpane');
25409         //Roo.log(pane);
25410         // tabs are rendere left to right..
25411         if(!this.showtabs){
25412             return;
25413         }
25414         
25415         var ctr = this.el.select('.nav-tabs', true).first();
25416          
25417          
25418         var existing = ctr.select('.nav-tab',true);
25419         var qty = existing.getCount();;
25420         
25421         
25422         var tab = ctr.createChild({
25423             tag : 'li',
25424             cls : 'nav-tab' + (qty ? '' : ' active'),
25425             cn : [
25426                 {
25427                     tag : 'a',
25428                     href:'#',
25429                     html : pane.title
25430                 }
25431             ]
25432         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25433         pane.tab = tab;
25434         
25435         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25436         if (!qty) {
25437             pane.el.addClass('active');
25438         }
25439         
25440                 
25441     },
25442     onTabClick : function(ev,un,ob,pane)
25443     {
25444         //Roo.log('tab - prev default');
25445         ev.preventDefault();
25446         
25447         
25448         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25449         pane.tab.addClass('active');
25450         //Roo.log(pane.title);
25451         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25452         // technically we should have a deactivate event.. but maybe add later.
25453         // and it should not de-activate the selected tab...
25454         this.fireEvent('activatepane', pane);
25455         pane.el.addClass('active');
25456         pane.fireEvent('activate');
25457         
25458         
25459     },
25460     
25461     getActivePane : function()
25462     {
25463         var r = false;
25464         Roo.each(this.panes, function(p) {
25465             if(p.el.hasClass('active')){
25466                 r = p;
25467                 return false;
25468             }
25469             
25470             return;
25471         });
25472         
25473         return r;
25474     }
25475     
25476     
25477 });
25478
25479  
25480 /*
25481  * - LGPL
25482  *
25483  * Tab pane
25484  * 
25485  */
25486 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25487 /**
25488  * @class Roo.bootstrap.TabPane
25489  * @extends Roo.bootstrap.Component
25490  * Bootstrap TabPane class
25491  * @cfg {Boolean} active (false | true) Default false
25492  * @cfg {String} title title of panel
25493
25494  * 
25495  * @constructor
25496  * Create a new TabPane
25497  * @param {Object} config The config object
25498  */
25499
25500 Roo.bootstrap.dash.TabPane = function(config){
25501     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25502     
25503     this.addEvents({
25504         // raw events
25505         /**
25506          * @event activate
25507          * When a pane is activated
25508          * @param {Roo.bootstrap.dash.TabPane} pane
25509          */
25510         "activate" : true
25511          
25512     });
25513 };
25514
25515 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25516     
25517     active : false,
25518     title : '',
25519     
25520     // the tabBox that this is attached to.
25521     tab : false,
25522      
25523     getAutoCreate : function() 
25524     {
25525         var cfg = {
25526             tag: 'div',
25527             cls: 'tab-pane'
25528         };
25529         
25530         if(this.active){
25531             cfg.cls += ' active';
25532         }
25533         
25534         return cfg;
25535     },
25536     initEvents  : function()
25537     {
25538         //Roo.log('trigger add pane handler');
25539         this.parent().fireEvent('addpane', this)
25540     },
25541     
25542      /**
25543      * Updates the tab title 
25544      * @param {String} html to set the title to.
25545      */
25546     setTitle: function(str)
25547     {
25548         if (!this.tab) {
25549             return;
25550         }
25551         this.title = str;
25552         this.tab.select('a', true).first().dom.innerHTML = str;
25553         
25554     }
25555     
25556     
25557     
25558 });
25559
25560  
25561
25562
25563  /*
25564  * - LGPL
25565  *
25566  * menu
25567  * 
25568  */
25569 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25570
25571 /**
25572  * @class Roo.bootstrap.menu.Menu
25573  * @extends Roo.bootstrap.Component
25574  * Bootstrap Menu class - container for Menu
25575  * @cfg {String} html Text of the menu
25576  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25577  * @cfg {String} icon Font awesome icon
25578  * @cfg {String} pos Menu align to (top | bottom) default bottom
25579  * 
25580  * 
25581  * @constructor
25582  * Create a new Menu
25583  * @param {Object} config The config object
25584  */
25585
25586
25587 Roo.bootstrap.menu.Menu = function(config){
25588     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25589     
25590     this.addEvents({
25591         /**
25592          * @event beforeshow
25593          * Fires before this menu is displayed
25594          * @param {Roo.bootstrap.menu.Menu} this
25595          */
25596         beforeshow : true,
25597         /**
25598          * @event beforehide
25599          * Fires before this menu is hidden
25600          * @param {Roo.bootstrap.menu.Menu} this
25601          */
25602         beforehide : true,
25603         /**
25604          * @event show
25605          * Fires after this menu is displayed
25606          * @param {Roo.bootstrap.menu.Menu} this
25607          */
25608         show : true,
25609         /**
25610          * @event hide
25611          * Fires after this menu is hidden
25612          * @param {Roo.bootstrap.menu.Menu} this
25613          */
25614         hide : true,
25615         /**
25616          * @event click
25617          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25618          * @param {Roo.bootstrap.menu.Menu} this
25619          * @param {Roo.EventObject} e
25620          */
25621         click : true
25622     });
25623     
25624 };
25625
25626 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25627     
25628     submenu : false,
25629     html : '',
25630     weight : 'default',
25631     icon : false,
25632     pos : 'bottom',
25633     
25634     
25635     getChildContainer : function() {
25636         if(this.isSubMenu){
25637             return this.el;
25638         }
25639         
25640         return this.el.select('ul.dropdown-menu', true).first();  
25641     },
25642     
25643     getAutoCreate : function()
25644     {
25645         var text = [
25646             {
25647                 tag : 'span',
25648                 cls : 'roo-menu-text',
25649                 html : this.html
25650             }
25651         ];
25652         
25653         if(this.icon){
25654             text.unshift({
25655                 tag : 'i',
25656                 cls : 'fa ' + this.icon
25657             })
25658         }
25659         
25660         
25661         var cfg = {
25662             tag : 'div',
25663             cls : 'btn-group',
25664             cn : [
25665                 {
25666                     tag : 'button',
25667                     cls : 'dropdown-button btn btn-' + this.weight,
25668                     cn : text
25669                 },
25670                 {
25671                     tag : 'button',
25672                     cls : 'dropdown-toggle btn btn-' + this.weight,
25673                     cn : [
25674                         {
25675                             tag : 'span',
25676                             cls : 'caret'
25677                         }
25678                     ]
25679                 },
25680                 {
25681                     tag : 'ul',
25682                     cls : 'dropdown-menu'
25683                 }
25684             ]
25685             
25686         };
25687         
25688         if(this.pos == 'top'){
25689             cfg.cls += ' dropup';
25690         }
25691         
25692         if(this.isSubMenu){
25693             cfg = {
25694                 tag : 'ul',
25695                 cls : 'dropdown-menu'
25696             }
25697         }
25698         
25699         return cfg;
25700     },
25701     
25702     onRender : function(ct, position)
25703     {
25704         this.isSubMenu = ct.hasClass('dropdown-submenu');
25705         
25706         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25707     },
25708     
25709     initEvents : function() 
25710     {
25711         if(this.isSubMenu){
25712             return;
25713         }
25714         
25715         this.hidden = true;
25716         
25717         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25718         this.triggerEl.on('click', this.onTriggerPress, this);
25719         
25720         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25721         this.buttonEl.on('click', this.onClick, this);
25722         
25723     },
25724     
25725     list : function()
25726     {
25727         if(this.isSubMenu){
25728             return this.el;
25729         }
25730         
25731         return this.el.select('ul.dropdown-menu', true).first();
25732     },
25733     
25734     onClick : function(e)
25735     {
25736         this.fireEvent("click", this, e);
25737     },
25738     
25739     onTriggerPress  : function(e)
25740     {   
25741         if (this.isVisible()) {
25742             this.hide();
25743         } else {
25744             this.show();
25745         }
25746     },
25747     
25748     isVisible : function(){
25749         return !this.hidden;
25750     },
25751     
25752     show : function()
25753     {
25754         this.fireEvent("beforeshow", this);
25755         
25756         this.hidden = false;
25757         this.el.addClass('open');
25758         
25759         Roo.get(document).on("mouseup", this.onMouseUp, this);
25760         
25761         this.fireEvent("show", this);
25762         
25763         
25764     },
25765     
25766     hide : function()
25767     {
25768         this.fireEvent("beforehide", this);
25769         
25770         this.hidden = true;
25771         this.el.removeClass('open');
25772         
25773         Roo.get(document).un("mouseup", this.onMouseUp);
25774         
25775         this.fireEvent("hide", this);
25776     },
25777     
25778     onMouseUp : function()
25779     {
25780         this.hide();
25781     }
25782     
25783 });
25784
25785  
25786  /*
25787  * - LGPL
25788  *
25789  * menu item
25790  * 
25791  */
25792 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25793
25794 /**
25795  * @class Roo.bootstrap.menu.Item
25796  * @extends Roo.bootstrap.Component
25797  * Bootstrap MenuItem class
25798  * @cfg {Boolean} submenu (true | false) default false
25799  * @cfg {String} html text of the item
25800  * @cfg {String} href the link
25801  * @cfg {Boolean} disable (true | false) default false
25802  * @cfg {Boolean} preventDefault (true | false) default true
25803  * @cfg {String} icon Font awesome icon
25804  * @cfg {String} pos Submenu align to (left | right) default right 
25805  * 
25806  * 
25807  * @constructor
25808  * Create a new Item
25809  * @param {Object} config The config object
25810  */
25811
25812
25813 Roo.bootstrap.menu.Item = function(config){
25814     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25815     this.addEvents({
25816         /**
25817          * @event mouseover
25818          * Fires when the mouse is hovering over this menu
25819          * @param {Roo.bootstrap.menu.Item} this
25820          * @param {Roo.EventObject} e
25821          */
25822         mouseover : true,
25823         /**
25824          * @event mouseout
25825          * Fires when the mouse exits this menu
25826          * @param {Roo.bootstrap.menu.Item} this
25827          * @param {Roo.EventObject} e
25828          */
25829         mouseout : true,
25830         // raw events
25831         /**
25832          * @event click
25833          * The raw click event for the entire grid.
25834          * @param {Roo.EventObject} e
25835          */
25836         click : true
25837     });
25838 };
25839
25840 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25841     
25842     submenu : false,
25843     href : '',
25844     html : '',
25845     preventDefault: true,
25846     disable : false,
25847     icon : false,
25848     pos : 'right',
25849     
25850     getAutoCreate : function()
25851     {
25852         var text = [
25853             {
25854                 tag : 'span',
25855                 cls : 'roo-menu-item-text',
25856                 html : this.html
25857             }
25858         ];
25859         
25860         if(this.icon){
25861             text.unshift({
25862                 tag : 'i',
25863                 cls : 'fa ' + this.icon
25864             })
25865         }
25866         
25867         var cfg = {
25868             tag : 'li',
25869             cn : [
25870                 {
25871                     tag : 'a',
25872                     href : this.href || '#',
25873                     cn : text
25874                 }
25875             ]
25876         };
25877         
25878         if(this.disable){
25879             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25880         }
25881         
25882         if(this.submenu){
25883             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25884             
25885             if(this.pos == 'left'){
25886                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25887             }
25888         }
25889         
25890         return cfg;
25891     },
25892     
25893     initEvents : function() 
25894     {
25895         this.el.on('mouseover', this.onMouseOver, this);
25896         this.el.on('mouseout', this.onMouseOut, this);
25897         
25898         this.el.select('a', true).first().on('click', this.onClick, this);
25899         
25900     },
25901     
25902     onClick : function(e)
25903     {
25904         if(this.preventDefault){
25905             e.preventDefault();
25906         }
25907         
25908         this.fireEvent("click", this, e);
25909     },
25910     
25911     onMouseOver : function(e)
25912     {
25913         if(this.submenu && this.pos == 'left'){
25914             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25915         }
25916         
25917         this.fireEvent("mouseover", this, e);
25918     },
25919     
25920     onMouseOut : function(e)
25921     {
25922         this.fireEvent("mouseout", this, e);
25923     }
25924 });
25925
25926  
25927
25928  /*
25929  * - LGPL
25930  *
25931  * menu separator
25932  * 
25933  */
25934 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25935
25936 /**
25937  * @class Roo.bootstrap.menu.Separator
25938  * @extends Roo.bootstrap.Component
25939  * Bootstrap Separator class
25940  * 
25941  * @constructor
25942  * Create a new Separator
25943  * @param {Object} config The config object
25944  */
25945
25946
25947 Roo.bootstrap.menu.Separator = function(config){
25948     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25949 };
25950
25951 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25952     
25953     getAutoCreate : function(){
25954         var cfg = {
25955             tag : 'li',
25956             cls: 'divider'
25957         };
25958         
25959         return cfg;
25960     }
25961    
25962 });
25963
25964  
25965
25966  /*
25967  * - LGPL
25968  *
25969  * Tooltip
25970  * 
25971  */
25972
25973 /**
25974  * @class Roo.bootstrap.Tooltip
25975  * Bootstrap Tooltip class
25976  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25977  * to determine which dom element triggers the tooltip.
25978  * 
25979  * It needs to add support for additional attributes like tooltip-position
25980  * 
25981  * @constructor
25982  * Create a new Toolti
25983  * @param {Object} config The config object
25984  */
25985
25986 Roo.bootstrap.Tooltip = function(config){
25987     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25988     
25989     this.alignment = Roo.bootstrap.Tooltip.alignment;
25990     
25991     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25992         this.alignment = config.alignment;
25993     }
25994     
25995 };
25996
25997 Roo.apply(Roo.bootstrap.Tooltip, {
25998     /**
25999      * @function init initialize tooltip monitoring.
26000      * @static
26001      */
26002     currentEl : false,
26003     currentTip : false,
26004     currentRegion : false,
26005     
26006     //  init : delay?
26007     
26008     init : function()
26009     {
26010         Roo.get(document).on('mouseover', this.enter ,this);
26011         Roo.get(document).on('mouseout', this.leave, this);
26012          
26013         
26014         this.currentTip = new Roo.bootstrap.Tooltip();
26015     },
26016     
26017     enter : function(ev)
26018     {
26019         var dom = ev.getTarget();
26020         
26021         //Roo.log(['enter',dom]);
26022         var el = Roo.fly(dom);
26023         if (this.currentEl) {
26024             //Roo.log(dom);
26025             //Roo.log(this.currentEl);
26026             //Roo.log(this.currentEl.contains(dom));
26027             if (this.currentEl == el) {
26028                 return;
26029             }
26030             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26031                 return;
26032             }
26033
26034         }
26035         
26036         if (this.currentTip.el) {
26037             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26038         }    
26039         //Roo.log(ev);
26040         
26041         if(!el || el.dom == document){
26042             return;
26043         }
26044         
26045         var bindEl = el;
26046         
26047         // you can not look for children, as if el is the body.. then everythign is the child..
26048         if (!el.attr('tooltip')) { //
26049             if (!el.select("[tooltip]").elements.length) {
26050                 return;
26051             }
26052             // is the mouse over this child...?
26053             bindEl = el.select("[tooltip]").first();
26054             var xy = ev.getXY();
26055             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26056                 //Roo.log("not in region.");
26057                 return;
26058             }
26059             //Roo.log("child element over..");
26060             
26061         }
26062         this.currentEl = bindEl;
26063         this.currentTip.bind(bindEl);
26064         this.currentRegion = Roo.lib.Region.getRegion(dom);
26065         this.currentTip.enter();
26066         
26067     },
26068     leave : function(ev)
26069     {
26070         var dom = ev.getTarget();
26071         //Roo.log(['leave',dom]);
26072         if (!this.currentEl) {
26073             return;
26074         }
26075         
26076         
26077         if (dom != this.currentEl.dom) {
26078             return;
26079         }
26080         var xy = ev.getXY();
26081         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26082             return;
26083         }
26084         // only activate leave if mouse cursor is outside... bounding box..
26085         
26086         
26087         
26088         
26089         if (this.currentTip) {
26090             this.currentTip.leave();
26091         }
26092         //Roo.log('clear currentEl');
26093         this.currentEl = false;
26094         
26095         
26096     },
26097     alignment : {
26098         'left' : ['r-l', [-2,0], 'right'],
26099         'right' : ['l-r', [2,0], 'left'],
26100         'bottom' : ['t-b', [0,2], 'top'],
26101         'top' : [ 'b-t', [0,-2], 'bottom']
26102     }
26103     
26104 });
26105
26106
26107 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26108     
26109     
26110     bindEl : false,
26111     
26112     delay : null, // can be { show : 300 , hide: 500}
26113     
26114     timeout : null,
26115     
26116     hoverState : null, //???
26117     
26118     placement : 'bottom', 
26119     
26120     alignment : false,
26121     
26122     getAutoCreate : function(){
26123     
26124         var cfg = {
26125            cls : 'tooltip',
26126            role : 'tooltip',
26127            cn : [
26128                 {
26129                     cls : 'tooltip-arrow'
26130                 },
26131                 {
26132                     cls : 'tooltip-inner'
26133                 }
26134            ]
26135         };
26136         
26137         return cfg;
26138     },
26139     bind : function(el)
26140     {
26141         this.bindEl = el;
26142     },
26143       
26144     
26145     enter : function () {
26146        
26147         if (this.timeout != null) {
26148             clearTimeout(this.timeout);
26149         }
26150         
26151         this.hoverState = 'in';
26152          //Roo.log("enter - show");
26153         if (!this.delay || !this.delay.show) {
26154             this.show();
26155             return;
26156         }
26157         var _t = this;
26158         this.timeout = setTimeout(function () {
26159             if (_t.hoverState == 'in') {
26160                 _t.show();
26161             }
26162         }, this.delay.show);
26163     },
26164     leave : function()
26165     {
26166         clearTimeout(this.timeout);
26167     
26168         this.hoverState = 'out';
26169          if (!this.delay || !this.delay.hide) {
26170             this.hide();
26171             return;
26172         }
26173        
26174         var _t = this;
26175         this.timeout = setTimeout(function () {
26176             //Roo.log("leave - timeout");
26177             
26178             if (_t.hoverState == 'out') {
26179                 _t.hide();
26180                 Roo.bootstrap.Tooltip.currentEl = false;
26181             }
26182         }, delay);
26183     },
26184     
26185     show : function (msg)
26186     {
26187         if (!this.el) {
26188             this.render(document.body);
26189         }
26190         // set content.
26191         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26192         
26193         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26194         
26195         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26196         
26197         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26198         
26199         var placement = typeof this.placement == 'function' ?
26200             this.placement.call(this, this.el, on_el) :
26201             this.placement;
26202             
26203         var autoToken = /\s?auto?\s?/i;
26204         var autoPlace = autoToken.test(placement);
26205         if (autoPlace) {
26206             placement = placement.replace(autoToken, '') || 'top';
26207         }
26208         
26209         //this.el.detach()
26210         //this.el.setXY([0,0]);
26211         this.el.show();
26212         //this.el.dom.style.display='block';
26213         
26214         //this.el.appendTo(on_el);
26215         
26216         var p = this.getPosition();
26217         var box = this.el.getBox();
26218         
26219         if (autoPlace) {
26220             // fixme..
26221         }
26222         
26223         var align = this.alignment[placement];
26224         
26225         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26226         
26227         if(placement == 'top' || placement == 'bottom'){
26228             if(xy[0] < 0){
26229                 placement = 'right';
26230             }
26231             
26232             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26233                 placement = 'left';
26234             }
26235             
26236             var scroll = Roo.select('body', true).first().getScroll();
26237             
26238             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26239                 placement = 'top';
26240             }
26241             
26242             align = this.alignment[placement];
26243         }
26244         
26245         this.el.alignTo(this.bindEl, align[0],align[1]);
26246         //var arrow = this.el.select('.arrow',true).first();
26247         //arrow.set(align[2], 
26248         
26249         this.el.addClass(placement);
26250         
26251         this.el.addClass('in fade');
26252         
26253         this.hoverState = null;
26254         
26255         if (this.el.hasClass('fade')) {
26256             // fade it?
26257         }
26258         
26259     },
26260     hide : function()
26261     {
26262          
26263         if (!this.el) {
26264             return;
26265         }
26266         //this.el.setXY([0,0]);
26267         this.el.removeClass('in');
26268         //this.el.hide();
26269         
26270     }
26271     
26272 });
26273  
26274
26275  /*
26276  * - LGPL
26277  *
26278  * Location Picker
26279  * 
26280  */
26281
26282 /**
26283  * @class Roo.bootstrap.LocationPicker
26284  * @extends Roo.bootstrap.Component
26285  * Bootstrap LocationPicker class
26286  * @cfg {Number} latitude Position when init default 0
26287  * @cfg {Number} longitude Position when init default 0
26288  * @cfg {Number} zoom default 15
26289  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26290  * @cfg {Boolean} mapTypeControl default false
26291  * @cfg {Boolean} disableDoubleClickZoom default false
26292  * @cfg {Boolean} scrollwheel default true
26293  * @cfg {Boolean} streetViewControl default false
26294  * @cfg {Number} radius default 0
26295  * @cfg {String} locationName
26296  * @cfg {Boolean} draggable default true
26297  * @cfg {Boolean} enableAutocomplete default false
26298  * @cfg {Boolean} enableReverseGeocode default true
26299  * @cfg {String} markerTitle
26300  * 
26301  * @constructor
26302  * Create a new LocationPicker
26303  * @param {Object} config The config object
26304  */
26305
26306
26307 Roo.bootstrap.LocationPicker = function(config){
26308     
26309     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26310     
26311     this.addEvents({
26312         /**
26313          * @event initial
26314          * Fires when the picker initialized.
26315          * @param {Roo.bootstrap.LocationPicker} this
26316          * @param {Google Location} location
26317          */
26318         initial : true,
26319         /**
26320          * @event positionchanged
26321          * Fires when the picker position changed.
26322          * @param {Roo.bootstrap.LocationPicker} this
26323          * @param {Google Location} location
26324          */
26325         positionchanged : true,
26326         /**
26327          * @event resize
26328          * Fires when the map resize.
26329          * @param {Roo.bootstrap.LocationPicker} this
26330          */
26331         resize : true,
26332         /**
26333          * @event show
26334          * Fires when the map show.
26335          * @param {Roo.bootstrap.LocationPicker} this
26336          */
26337         show : true,
26338         /**
26339          * @event hide
26340          * Fires when the map hide.
26341          * @param {Roo.bootstrap.LocationPicker} this
26342          */
26343         hide : true,
26344         /**
26345          * @event mapClick
26346          * Fires when click the map.
26347          * @param {Roo.bootstrap.LocationPicker} this
26348          * @param {Map event} e
26349          */
26350         mapClick : true,
26351         /**
26352          * @event mapRightClick
26353          * Fires when right click the map.
26354          * @param {Roo.bootstrap.LocationPicker} this
26355          * @param {Map event} e
26356          */
26357         mapRightClick : true,
26358         /**
26359          * @event markerClick
26360          * Fires when click the marker.
26361          * @param {Roo.bootstrap.LocationPicker} this
26362          * @param {Map event} e
26363          */
26364         markerClick : true,
26365         /**
26366          * @event markerRightClick
26367          * Fires when right click the marker.
26368          * @param {Roo.bootstrap.LocationPicker} this
26369          * @param {Map event} e
26370          */
26371         markerRightClick : true,
26372         /**
26373          * @event OverlayViewDraw
26374          * Fires when OverlayView Draw
26375          * @param {Roo.bootstrap.LocationPicker} this
26376          */
26377         OverlayViewDraw : true,
26378         /**
26379          * @event OverlayViewOnAdd
26380          * Fires when OverlayView Draw
26381          * @param {Roo.bootstrap.LocationPicker} this
26382          */
26383         OverlayViewOnAdd : true,
26384         /**
26385          * @event OverlayViewOnRemove
26386          * Fires when OverlayView Draw
26387          * @param {Roo.bootstrap.LocationPicker} this
26388          */
26389         OverlayViewOnRemove : true,
26390         /**
26391          * @event OverlayViewShow
26392          * Fires when OverlayView Draw
26393          * @param {Roo.bootstrap.LocationPicker} this
26394          * @param {Pixel} cpx
26395          */
26396         OverlayViewShow : true,
26397         /**
26398          * @event OverlayViewHide
26399          * Fires when OverlayView Draw
26400          * @param {Roo.bootstrap.LocationPicker} this
26401          */
26402         OverlayViewHide : true,
26403         /**
26404          * @event loadexception
26405          * Fires when load google lib failed.
26406          * @param {Roo.bootstrap.LocationPicker} this
26407          */
26408         loadexception : true
26409     });
26410         
26411 };
26412
26413 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26414     
26415     gMapContext: false,
26416     
26417     latitude: 0,
26418     longitude: 0,
26419     zoom: 15,
26420     mapTypeId: false,
26421     mapTypeControl: false,
26422     disableDoubleClickZoom: false,
26423     scrollwheel: true,
26424     streetViewControl: false,
26425     radius: 0,
26426     locationName: '',
26427     draggable: true,
26428     enableAutocomplete: false,
26429     enableReverseGeocode: true,
26430     markerTitle: '',
26431     
26432     getAutoCreate: function()
26433     {
26434
26435         var cfg = {
26436             tag: 'div',
26437             cls: 'roo-location-picker'
26438         };
26439         
26440         return cfg
26441     },
26442     
26443     initEvents: function(ct, position)
26444     {       
26445         if(!this.el.getWidth() || this.isApplied()){
26446             return;
26447         }
26448         
26449         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26450         
26451         this.initial();
26452     },
26453     
26454     initial: function()
26455     {
26456         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26457             this.fireEvent('loadexception', this);
26458             return;
26459         }
26460         
26461         if(!this.mapTypeId){
26462             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26463         }
26464         
26465         this.gMapContext = this.GMapContext();
26466         
26467         this.initOverlayView();
26468         
26469         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26470         
26471         var _this = this;
26472                 
26473         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26474             _this.setPosition(_this.gMapContext.marker.position);
26475         });
26476         
26477         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26478             _this.fireEvent('mapClick', this, event);
26479             
26480         });
26481
26482         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26483             _this.fireEvent('mapRightClick', this, event);
26484             
26485         });
26486         
26487         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26488             _this.fireEvent('markerClick', this, event);
26489             
26490         });
26491
26492         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26493             _this.fireEvent('markerRightClick', this, event);
26494             
26495         });
26496         
26497         this.setPosition(this.gMapContext.location);
26498         
26499         this.fireEvent('initial', this, this.gMapContext.location);
26500     },
26501     
26502     initOverlayView: function()
26503     {
26504         var _this = this;
26505         
26506         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26507             
26508             draw: function()
26509             {
26510                 _this.fireEvent('OverlayViewDraw', _this);
26511             },
26512             
26513             onAdd: function()
26514             {
26515                 _this.fireEvent('OverlayViewOnAdd', _this);
26516             },
26517             
26518             onRemove: function()
26519             {
26520                 _this.fireEvent('OverlayViewOnRemove', _this);
26521             },
26522             
26523             show: function(cpx)
26524             {
26525                 _this.fireEvent('OverlayViewShow', _this, cpx);
26526             },
26527             
26528             hide: function()
26529             {
26530                 _this.fireEvent('OverlayViewHide', _this);
26531             }
26532             
26533         });
26534     },
26535     
26536     fromLatLngToContainerPixel: function(event)
26537     {
26538         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26539     },
26540     
26541     isApplied: function() 
26542     {
26543         return this.getGmapContext() == false ? false : true;
26544     },
26545     
26546     getGmapContext: function() 
26547     {
26548         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26549     },
26550     
26551     GMapContext: function() 
26552     {
26553         var position = new google.maps.LatLng(this.latitude, this.longitude);
26554         
26555         var _map = new google.maps.Map(this.el.dom, {
26556             center: position,
26557             zoom: this.zoom,
26558             mapTypeId: this.mapTypeId,
26559             mapTypeControl: this.mapTypeControl,
26560             disableDoubleClickZoom: this.disableDoubleClickZoom,
26561             scrollwheel: this.scrollwheel,
26562             streetViewControl: this.streetViewControl,
26563             locationName: this.locationName,
26564             draggable: this.draggable,
26565             enableAutocomplete: this.enableAutocomplete,
26566             enableReverseGeocode: this.enableReverseGeocode
26567         });
26568         
26569         var _marker = new google.maps.Marker({
26570             position: position,
26571             map: _map,
26572             title: this.markerTitle,
26573             draggable: this.draggable
26574         });
26575         
26576         return {
26577             map: _map,
26578             marker: _marker,
26579             circle: null,
26580             location: position,
26581             radius: this.radius,
26582             locationName: this.locationName,
26583             addressComponents: {
26584                 formatted_address: null,
26585                 addressLine1: null,
26586                 addressLine2: null,
26587                 streetName: null,
26588                 streetNumber: null,
26589                 city: null,
26590                 district: null,
26591                 state: null,
26592                 stateOrProvince: null
26593             },
26594             settings: this,
26595             domContainer: this.el.dom,
26596             geodecoder: new google.maps.Geocoder()
26597         };
26598     },
26599     
26600     drawCircle: function(center, radius, options) 
26601     {
26602         if (this.gMapContext.circle != null) {
26603             this.gMapContext.circle.setMap(null);
26604         }
26605         if (radius > 0) {
26606             radius *= 1;
26607             options = Roo.apply({}, options, {
26608                 strokeColor: "#0000FF",
26609                 strokeOpacity: .35,
26610                 strokeWeight: 2,
26611                 fillColor: "#0000FF",
26612                 fillOpacity: .2
26613             });
26614             
26615             options.map = this.gMapContext.map;
26616             options.radius = radius;
26617             options.center = center;
26618             this.gMapContext.circle = new google.maps.Circle(options);
26619             return this.gMapContext.circle;
26620         }
26621         
26622         return null;
26623     },
26624     
26625     setPosition: function(location) 
26626     {
26627         this.gMapContext.location = location;
26628         this.gMapContext.marker.setPosition(location);
26629         this.gMapContext.map.panTo(location);
26630         this.drawCircle(location, this.gMapContext.radius, {});
26631         
26632         var _this = this;
26633         
26634         if (this.gMapContext.settings.enableReverseGeocode) {
26635             this.gMapContext.geodecoder.geocode({
26636                 latLng: this.gMapContext.location
26637             }, function(results, status) {
26638                 
26639                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26640                     _this.gMapContext.locationName = results[0].formatted_address;
26641                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26642                     
26643                     _this.fireEvent('positionchanged', this, location);
26644                 }
26645             });
26646             
26647             return;
26648         }
26649         
26650         this.fireEvent('positionchanged', this, location);
26651     },
26652     
26653     resize: function()
26654     {
26655         google.maps.event.trigger(this.gMapContext.map, "resize");
26656         
26657         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26658         
26659         this.fireEvent('resize', this);
26660     },
26661     
26662     setPositionByLatLng: function(latitude, longitude)
26663     {
26664         this.setPosition(new google.maps.LatLng(latitude, longitude));
26665     },
26666     
26667     getCurrentPosition: function() 
26668     {
26669         return {
26670             latitude: this.gMapContext.location.lat(),
26671             longitude: this.gMapContext.location.lng()
26672         };
26673     },
26674     
26675     getAddressName: function() 
26676     {
26677         return this.gMapContext.locationName;
26678     },
26679     
26680     getAddressComponents: function() 
26681     {
26682         return this.gMapContext.addressComponents;
26683     },
26684     
26685     address_component_from_google_geocode: function(address_components) 
26686     {
26687         var result = {};
26688         
26689         for (var i = 0; i < address_components.length; i++) {
26690             var component = address_components[i];
26691             if (component.types.indexOf("postal_code") >= 0) {
26692                 result.postalCode = component.short_name;
26693             } else if (component.types.indexOf("street_number") >= 0) {
26694                 result.streetNumber = component.short_name;
26695             } else if (component.types.indexOf("route") >= 0) {
26696                 result.streetName = component.short_name;
26697             } else if (component.types.indexOf("neighborhood") >= 0) {
26698                 result.city = component.short_name;
26699             } else if (component.types.indexOf("locality") >= 0) {
26700                 result.city = component.short_name;
26701             } else if (component.types.indexOf("sublocality") >= 0) {
26702                 result.district = component.short_name;
26703             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26704                 result.stateOrProvince = component.short_name;
26705             } else if (component.types.indexOf("country") >= 0) {
26706                 result.country = component.short_name;
26707             }
26708         }
26709         
26710         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26711         result.addressLine2 = "";
26712         return result;
26713     },
26714     
26715     setZoomLevel: function(zoom)
26716     {
26717         this.gMapContext.map.setZoom(zoom);
26718     },
26719     
26720     show: function()
26721     {
26722         if(!this.el){
26723             return;
26724         }
26725         
26726         this.el.show();
26727         
26728         this.resize();
26729         
26730         this.fireEvent('show', this);
26731     },
26732     
26733     hide: function()
26734     {
26735         if(!this.el){
26736             return;
26737         }
26738         
26739         this.el.hide();
26740         
26741         this.fireEvent('hide', this);
26742     }
26743     
26744 });
26745
26746 Roo.apply(Roo.bootstrap.LocationPicker, {
26747     
26748     OverlayView : function(map, options)
26749     {
26750         options = options || {};
26751         
26752         this.setMap(map);
26753     }
26754     
26755     
26756 });/*
26757  * - LGPL
26758  *
26759  * Alert
26760  * 
26761  */
26762
26763 /**
26764  * @class Roo.bootstrap.Alert
26765  * @extends Roo.bootstrap.Component
26766  * Bootstrap Alert class
26767  * @cfg {String} title The title of alert
26768  * @cfg {String} html The content of alert
26769  * @cfg {String} weight (  success | info | warning | danger )
26770  * @cfg {String} faicon font-awesomeicon
26771  * 
26772  * @constructor
26773  * Create a new alert
26774  * @param {Object} config The config object
26775  */
26776
26777
26778 Roo.bootstrap.Alert = function(config){
26779     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26780     
26781 };
26782
26783 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26784     
26785     title: '',
26786     html: '',
26787     weight: false,
26788     faicon: false,
26789     
26790     getAutoCreate : function()
26791     {
26792         
26793         var cfg = {
26794             tag : 'div',
26795             cls : 'alert',
26796             cn : [
26797                 {
26798                     tag : 'i',
26799                     cls : 'roo-alert-icon'
26800                     
26801                 },
26802                 {
26803                     tag : 'b',
26804                     cls : 'roo-alert-title',
26805                     html : this.title
26806                 },
26807                 {
26808                     tag : 'span',
26809                     cls : 'roo-alert-text',
26810                     html : this.html
26811                 }
26812             ]
26813         };
26814         
26815         if(this.faicon){
26816             cfg.cn[0].cls += ' fa ' + this.faicon;
26817         }
26818         
26819         if(this.weight){
26820             cfg.cls += ' alert-' + this.weight;
26821         }
26822         
26823         return cfg;
26824     },
26825     
26826     initEvents: function() 
26827     {
26828         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26829     },
26830     
26831     setTitle : function(str)
26832     {
26833         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26834     },
26835     
26836     setText : function(str)
26837     {
26838         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26839     },
26840     
26841     setWeight : function(weight)
26842     {
26843         if(this.weight){
26844             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26845         }
26846         
26847         this.weight = weight;
26848         
26849         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26850     },
26851     
26852     setIcon : function(icon)
26853     {
26854         if(this.faicon){
26855             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26856         }
26857         
26858         this.faicon = icon;
26859         
26860         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26861     },
26862     
26863     hide: function() 
26864     {
26865         this.el.hide();   
26866     },
26867     
26868     show: function() 
26869     {  
26870         this.el.show();   
26871     }
26872     
26873 });
26874
26875  
26876 /*
26877 * Licence: LGPL
26878 */
26879
26880 /**
26881  * @class Roo.bootstrap.UploadCropbox
26882  * @extends Roo.bootstrap.Component
26883  * Bootstrap UploadCropbox class
26884  * @cfg {String} emptyText show when image has been loaded
26885  * @cfg {String} rotateNotify show when image too small to rotate
26886  * @cfg {Number} errorTimeout default 3000
26887  * @cfg {Number} minWidth default 300
26888  * @cfg {Number} minHeight default 300
26889  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26890  * @cfg {Boolean} isDocument (true|false) default false
26891  * @cfg {String} url action url
26892  * @cfg {String} paramName default 'imageUpload'
26893  * @cfg {String} method default POST
26894  * @cfg {Boolean} loadMask (true|false) default true
26895  * @cfg {Boolean} loadingText default 'Loading...'
26896  * 
26897  * @constructor
26898  * Create a new UploadCropbox
26899  * @param {Object} config The config object
26900  */
26901
26902 Roo.bootstrap.UploadCropbox = function(config){
26903     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26904     
26905     this.addEvents({
26906         /**
26907          * @event beforeselectfile
26908          * Fire before select file
26909          * @param {Roo.bootstrap.UploadCropbox} this
26910          */
26911         "beforeselectfile" : true,
26912         /**
26913          * @event initial
26914          * Fire after initEvent
26915          * @param {Roo.bootstrap.UploadCropbox} this
26916          */
26917         "initial" : true,
26918         /**
26919          * @event crop
26920          * Fire after initEvent
26921          * @param {Roo.bootstrap.UploadCropbox} this
26922          * @param {String} data
26923          */
26924         "crop" : true,
26925         /**
26926          * @event prepare
26927          * Fire when preparing the file data
26928          * @param {Roo.bootstrap.UploadCropbox} this
26929          * @param {Object} file
26930          */
26931         "prepare" : true,
26932         /**
26933          * @event exception
26934          * Fire when get exception
26935          * @param {Roo.bootstrap.UploadCropbox} this
26936          * @param {XMLHttpRequest} xhr
26937          */
26938         "exception" : true,
26939         /**
26940          * @event beforeloadcanvas
26941          * Fire before load the canvas
26942          * @param {Roo.bootstrap.UploadCropbox} this
26943          * @param {String} src
26944          */
26945         "beforeloadcanvas" : true,
26946         /**
26947          * @event trash
26948          * Fire when trash image
26949          * @param {Roo.bootstrap.UploadCropbox} this
26950          */
26951         "trash" : true,
26952         /**
26953          * @event download
26954          * Fire when download the image
26955          * @param {Roo.bootstrap.UploadCropbox} this
26956          */
26957         "download" : true,
26958         /**
26959          * @event footerbuttonclick
26960          * Fire when footerbuttonclick
26961          * @param {Roo.bootstrap.UploadCropbox} this
26962          * @param {String} type
26963          */
26964         "footerbuttonclick" : true,
26965         /**
26966          * @event resize
26967          * Fire when resize
26968          * @param {Roo.bootstrap.UploadCropbox} this
26969          */
26970         "resize" : true,
26971         /**
26972          * @event rotate
26973          * Fire when rotate the image
26974          * @param {Roo.bootstrap.UploadCropbox} this
26975          * @param {String} pos
26976          */
26977         "rotate" : true,
26978         /**
26979          * @event inspect
26980          * Fire when inspect the file
26981          * @param {Roo.bootstrap.UploadCropbox} this
26982          * @param {Object} file
26983          */
26984         "inspect" : true,
26985         /**
26986          * @event upload
26987          * Fire when xhr upload the file
26988          * @param {Roo.bootstrap.UploadCropbox} this
26989          * @param {Object} data
26990          */
26991         "upload" : true,
26992         /**
26993          * @event arrange
26994          * Fire when arrange the file data
26995          * @param {Roo.bootstrap.UploadCropbox} this
26996          * @param {Object} formData
26997          */
26998         "arrange" : true
26999     });
27000     
27001     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27002 };
27003
27004 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27005     
27006     emptyText : 'Click to upload image',
27007     rotateNotify : 'Image is too small to rotate',
27008     errorTimeout : 3000,
27009     scale : 0,
27010     baseScale : 1,
27011     rotate : 0,
27012     dragable : false,
27013     pinching : false,
27014     mouseX : 0,
27015     mouseY : 0,
27016     cropData : false,
27017     minWidth : 300,
27018     minHeight : 300,
27019     file : false,
27020     exif : {},
27021     baseRotate : 1,
27022     cropType : 'image/jpeg',
27023     buttons : false,
27024     canvasLoaded : false,
27025     isDocument : false,
27026     method : 'POST',
27027     paramName : 'imageUpload',
27028     loadMask : true,
27029     loadingText : 'Loading...',
27030     maskEl : false,
27031     
27032     getAutoCreate : function()
27033     {
27034         var cfg = {
27035             tag : 'div',
27036             cls : 'roo-upload-cropbox',
27037             cn : [
27038                 {
27039                     tag : 'input',
27040                     cls : 'roo-upload-cropbox-selector',
27041                     type : 'file'
27042                 },
27043                 {
27044                     tag : 'div',
27045                     cls : 'roo-upload-cropbox-body',
27046                     style : 'cursor:pointer',
27047                     cn : [
27048                         {
27049                             tag : 'div',
27050                             cls : 'roo-upload-cropbox-preview'
27051                         },
27052                         {
27053                             tag : 'div',
27054                             cls : 'roo-upload-cropbox-thumb'
27055                         },
27056                         {
27057                             tag : 'div',
27058                             cls : 'roo-upload-cropbox-empty-notify',
27059                             html : this.emptyText
27060                         },
27061                         {
27062                             tag : 'div',
27063                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27064                             html : this.rotateNotify
27065                         }
27066                     ]
27067                 },
27068                 {
27069                     tag : 'div',
27070                     cls : 'roo-upload-cropbox-footer',
27071                     cn : {
27072                         tag : 'div',
27073                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27074                         cn : []
27075                     }
27076                 }
27077             ]
27078         };
27079         
27080         return cfg;
27081     },
27082     
27083     onRender : function(ct, position)
27084     {
27085         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27086         
27087         if (this.buttons.length) {
27088             
27089             Roo.each(this.buttons, function(bb) {
27090                 
27091                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27092                 
27093                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27094                 
27095             }, this);
27096         }
27097         
27098         if(this.loadMask){
27099             this.maskEl = this.el;
27100         }
27101     },
27102     
27103     initEvents : function()
27104     {
27105         this.urlAPI = (window.createObjectURL && window) || 
27106                                 (window.URL && URL.revokeObjectURL && URL) || 
27107                                 (window.webkitURL && webkitURL);
27108                         
27109         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27110         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27111         
27112         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27113         this.selectorEl.hide();
27114         
27115         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27116         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27117         
27118         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27119         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27120         this.thumbEl.hide();
27121         
27122         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27123         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27124         
27125         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27126         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27127         this.errorEl.hide();
27128         
27129         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27130         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27131         this.footerEl.hide();
27132         
27133         this.setThumbBoxSize();
27134         
27135         this.bind();
27136         
27137         this.resize();
27138         
27139         this.fireEvent('initial', this);
27140     },
27141
27142     bind : function()
27143     {
27144         var _this = this;
27145         
27146         window.addEventListener("resize", function() { _this.resize(); } );
27147         
27148         this.bodyEl.on('click', this.beforeSelectFile, this);
27149         
27150         if(Roo.isTouch){
27151             this.bodyEl.on('touchstart', this.onTouchStart, this);
27152             this.bodyEl.on('touchmove', this.onTouchMove, this);
27153             this.bodyEl.on('touchend', this.onTouchEnd, this);
27154         }
27155         
27156         if(!Roo.isTouch){
27157             this.bodyEl.on('mousedown', this.onMouseDown, this);
27158             this.bodyEl.on('mousemove', this.onMouseMove, this);
27159             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27160             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27161             Roo.get(document).on('mouseup', this.onMouseUp, this);
27162         }
27163         
27164         this.selectorEl.on('change', this.onFileSelected, this);
27165     },
27166     
27167     reset : function()
27168     {    
27169         this.scale = 0;
27170         this.baseScale = 1;
27171         this.rotate = 0;
27172         this.baseRotate = 1;
27173         this.dragable = false;
27174         this.pinching = false;
27175         this.mouseX = 0;
27176         this.mouseY = 0;
27177         this.cropData = false;
27178         this.notifyEl.dom.innerHTML = this.emptyText;
27179         
27180         this.selectorEl.dom.value = '';
27181         
27182     },
27183     
27184     resize : function()
27185     {
27186         if(this.fireEvent('resize', this) != false){
27187             this.setThumbBoxPosition();
27188             this.setCanvasPosition();
27189         }
27190     },
27191     
27192     onFooterButtonClick : function(e, el, o, type)
27193     {
27194         switch (type) {
27195             case 'rotate-left' :
27196                 this.onRotateLeft(e);
27197                 break;
27198             case 'rotate-right' :
27199                 this.onRotateRight(e);
27200                 break;
27201             case 'picture' :
27202                 this.beforeSelectFile(e);
27203                 break;
27204             case 'trash' :
27205                 this.trash(e);
27206                 break;
27207             case 'crop' :
27208                 this.crop(e);
27209                 break;
27210             case 'download' :
27211                 this.download(e);
27212                 break;
27213             default :
27214                 break;
27215         }
27216         
27217         this.fireEvent('footerbuttonclick', this, type);
27218     },
27219     
27220     beforeSelectFile : function(e)
27221     {
27222         e.preventDefault();
27223         
27224         if(this.fireEvent('beforeselectfile', this) != false){
27225             this.selectorEl.dom.click();
27226         }
27227     },
27228     
27229     onFileSelected : function(e)
27230     {
27231         e.preventDefault();
27232         
27233         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27234             return;
27235         }
27236         
27237         var file = this.selectorEl.dom.files[0];
27238         
27239         if(this.fireEvent('inspect', this, file) != false){
27240             this.prepare(file);
27241         }
27242         
27243     },
27244     
27245     trash : function(e)
27246     {
27247         this.fireEvent('trash', this);
27248     },
27249     
27250     download : function(e)
27251     {
27252         this.fireEvent('download', this);
27253     },
27254     
27255     loadCanvas : function(src)
27256     {   
27257         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27258             
27259             this.reset();
27260             
27261             this.imageEl = document.createElement('img');
27262             
27263             var _this = this;
27264             
27265             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27266             
27267             this.imageEl.src = src;
27268         }
27269     },
27270     
27271     onLoadCanvas : function()
27272     {   
27273         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27274         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27275         
27276         this.bodyEl.un('click', this.beforeSelectFile, this);
27277         
27278         this.notifyEl.hide();
27279         this.thumbEl.show();
27280         this.footerEl.show();
27281         
27282         this.baseRotateLevel();
27283         
27284         if(this.isDocument){
27285             this.setThumbBoxSize();
27286         }
27287         
27288         this.setThumbBoxPosition();
27289         
27290         this.baseScaleLevel();
27291         
27292         this.draw();
27293         
27294         this.resize();
27295         
27296         this.canvasLoaded = true;
27297         
27298         if(this.loadMask){
27299             this.maskEl.unmask();
27300         }
27301         
27302     },
27303     
27304     setCanvasPosition : function()
27305     {   
27306         if(!this.canvasEl){
27307             return;
27308         }
27309         
27310         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27311         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27312         
27313         this.previewEl.setLeft(pw);
27314         this.previewEl.setTop(ph);
27315         
27316     },
27317     
27318     onMouseDown : function(e)
27319     {   
27320         e.stopEvent();
27321         
27322         this.dragable = true;
27323         this.pinching = false;
27324         
27325         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27326             this.dragable = false;
27327             return;
27328         }
27329         
27330         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27331         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27332         
27333     },
27334     
27335     onMouseMove : function(e)
27336     {   
27337         e.stopEvent();
27338         
27339         if(!this.canvasLoaded){
27340             return;
27341         }
27342         
27343         if (!this.dragable){
27344             return;
27345         }
27346         
27347         var minX = Math.ceil(this.thumbEl.getLeft(true));
27348         var minY = Math.ceil(this.thumbEl.getTop(true));
27349         
27350         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27351         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27352         
27353         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27354         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27355         
27356         x = x - this.mouseX;
27357         y = y - this.mouseY;
27358         
27359         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27360         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27361         
27362         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27363         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27364         
27365         this.previewEl.setLeft(bgX);
27366         this.previewEl.setTop(bgY);
27367         
27368         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27369         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27370     },
27371     
27372     onMouseUp : function(e)
27373     {   
27374         e.stopEvent();
27375         
27376         this.dragable = false;
27377     },
27378     
27379     onMouseWheel : function(e)
27380     {   
27381         e.stopEvent();
27382         
27383         this.startScale = this.scale;
27384         
27385         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27386         
27387         if(!this.zoomable()){
27388             this.scale = this.startScale;
27389             return;
27390         }
27391         
27392         this.draw();
27393         
27394         return;
27395     },
27396     
27397     zoomable : function()
27398     {
27399         var minScale = this.thumbEl.getWidth() / this.minWidth;
27400         
27401         if(this.minWidth < this.minHeight){
27402             minScale = this.thumbEl.getHeight() / this.minHeight;
27403         }
27404         
27405         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27406         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27407         
27408         if(
27409                 this.isDocument &&
27410                 (this.rotate == 0 || this.rotate == 180) && 
27411                 (
27412                     width > this.imageEl.OriginWidth || 
27413                     height > this.imageEl.OriginHeight ||
27414                     (width < this.minWidth && height < this.minHeight)
27415                 )
27416         ){
27417             return false;
27418         }
27419         
27420         if(
27421                 this.isDocument &&
27422                 (this.rotate == 90 || this.rotate == 270) && 
27423                 (
27424                     width > this.imageEl.OriginWidth || 
27425                     height > this.imageEl.OriginHeight ||
27426                     (width < this.minHeight && height < this.minWidth)
27427                 )
27428         ){
27429             return false;
27430         }
27431         
27432         if(
27433                 !this.isDocument &&
27434                 (this.rotate == 0 || this.rotate == 180) && 
27435                 (
27436                     width < this.minWidth || 
27437                     width > this.imageEl.OriginWidth || 
27438                     height < this.minHeight || 
27439                     height > this.imageEl.OriginHeight
27440                 )
27441         ){
27442             return false;
27443         }
27444         
27445         if(
27446                 !this.isDocument &&
27447                 (this.rotate == 90 || this.rotate == 270) && 
27448                 (
27449                     width < this.minHeight || 
27450                     width > this.imageEl.OriginWidth || 
27451                     height < this.minWidth || 
27452                     height > this.imageEl.OriginHeight
27453                 )
27454         ){
27455             return false;
27456         }
27457         
27458         return true;
27459         
27460     },
27461     
27462     onRotateLeft : function(e)
27463     {   
27464         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27465             
27466             var minScale = this.thumbEl.getWidth() / this.minWidth;
27467             
27468             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27469             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27470             
27471             this.startScale = this.scale;
27472             
27473             while (this.getScaleLevel() < minScale){
27474             
27475                 this.scale = this.scale + 1;
27476                 
27477                 if(!this.zoomable()){
27478                     break;
27479                 }
27480                 
27481                 if(
27482                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27483                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27484                 ){
27485                     continue;
27486                 }
27487                 
27488                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27489
27490                 this.draw();
27491                 
27492                 return;
27493             }
27494             
27495             this.scale = this.startScale;
27496             
27497             this.onRotateFail();
27498             
27499             return false;
27500         }
27501         
27502         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27503
27504         if(this.isDocument){
27505             this.setThumbBoxSize();
27506             this.setThumbBoxPosition();
27507             this.setCanvasPosition();
27508         }
27509         
27510         this.draw();
27511         
27512         this.fireEvent('rotate', this, 'left');
27513         
27514     },
27515     
27516     onRotateRight : function(e)
27517     {
27518         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27519             
27520             var minScale = this.thumbEl.getWidth() / this.minWidth;
27521         
27522             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27523             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27524             
27525             this.startScale = this.scale;
27526             
27527             while (this.getScaleLevel() < minScale){
27528             
27529                 this.scale = this.scale + 1;
27530                 
27531                 if(!this.zoomable()){
27532                     break;
27533                 }
27534                 
27535                 if(
27536                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27537                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27538                 ){
27539                     continue;
27540                 }
27541                 
27542                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27543
27544                 this.draw();
27545                 
27546                 return;
27547             }
27548             
27549             this.scale = this.startScale;
27550             
27551             this.onRotateFail();
27552             
27553             return false;
27554         }
27555         
27556         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27557
27558         if(this.isDocument){
27559             this.setThumbBoxSize();
27560             this.setThumbBoxPosition();
27561             this.setCanvasPosition();
27562         }
27563         
27564         this.draw();
27565         
27566         this.fireEvent('rotate', this, 'right');
27567     },
27568     
27569     onRotateFail : function()
27570     {
27571         this.errorEl.show(true);
27572         
27573         var _this = this;
27574         
27575         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27576     },
27577     
27578     draw : function()
27579     {
27580         this.previewEl.dom.innerHTML = '';
27581         
27582         var canvasEl = document.createElement("canvas");
27583         
27584         var contextEl = canvasEl.getContext("2d");
27585         
27586         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27587         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27588         var center = this.imageEl.OriginWidth / 2;
27589         
27590         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27591             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27592             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27593             center = this.imageEl.OriginHeight / 2;
27594         }
27595         
27596         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27597         
27598         contextEl.translate(center, center);
27599         contextEl.rotate(this.rotate * Math.PI / 180);
27600
27601         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27602         
27603         this.canvasEl = document.createElement("canvas");
27604         
27605         this.contextEl = this.canvasEl.getContext("2d");
27606         
27607         switch (this.rotate) {
27608             case 0 :
27609                 
27610                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27611                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27612                 
27613                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27614                 
27615                 break;
27616             case 90 : 
27617                 
27618                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27619                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27620                 
27621                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27622                     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);
27623                     break;
27624                 }
27625                 
27626                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27627                 
27628                 break;
27629             case 180 :
27630                 
27631                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27632                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27633                 
27634                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27635                     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);
27636                     break;
27637                 }
27638                 
27639                 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);
27640                 
27641                 break;
27642             case 270 :
27643                 
27644                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27645                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27646         
27647                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27648                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27649                     break;
27650                 }
27651                 
27652                 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);
27653                 
27654                 break;
27655             default : 
27656                 break;
27657         }
27658         
27659         this.previewEl.appendChild(this.canvasEl);
27660         
27661         this.setCanvasPosition();
27662     },
27663     
27664     crop : function()
27665     {
27666         if(!this.canvasLoaded){
27667             return;
27668         }
27669         
27670         var imageCanvas = document.createElement("canvas");
27671         
27672         var imageContext = imageCanvas.getContext("2d");
27673         
27674         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27675         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27676         
27677         var center = imageCanvas.width / 2;
27678         
27679         imageContext.translate(center, center);
27680         
27681         imageContext.rotate(this.rotate * Math.PI / 180);
27682         
27683         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27684         
27685         var canvas = document.createElement("canvas");
27686         
27687         var context = canvas.getContext("2d");
27688                 
27689         canvas.width = this.minWidth;
27690         canvas.height = this.minHeight;
27691
27692         switch (this.rotate) {
27693             case 0 :
27694                 
27695                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27696                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27697                 
27698                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27699                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27700                 
27701                 var targetWidth = this.minWidth - 2 * x;
27702                 var targetHeight = this.minHeight - 2 * y;
27703                 
27704                 var scale = 1;
27705                 
27706                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27707                     scale = targetWidth / width;
27708                 }
27709                 
27710                 if(x > 0 && y == 0){
27711                     scale = targetHeight / height;
27712                 }
27713                 
27714                 if(x > 0 && y > 0){
27715                     scale = targetWidth / width;
27716                     
27717                     if(width < height){
27718                         scale = targetHeight / height;
27719                     }
27720                 }
27721                 
27722                 context.scale(scale, scale);
27723                 
27724                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27725                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27726
27727                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27728                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27729
27730                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27731                 
27732                 break;
27733             case 90 : 
27734                 
27735                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27736                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27737                 
27738                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27739                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27740                 
27741                 var targetWidth = this.minWidth - 2 * x;
27742                 var targetHeight = this.minHeight - 2 * y;
27743                 
27744                 var scale = 1;
27745                 
27746                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27747                     scale = targetWidth / width;
27748                 }
27749                 
27750                 if(x > 0 && y == 0){
27751                     scale = targetHeight / height;
27752                 }
27753                 
27754                 if(x > 0 && y > 0){
27755                     scale = targetWidth / width;
27756                     
27757                     if(width < height){
27758                         scale = targetHeight / height;
27759                     }
27760                 }
27761                 
27762                 context.scale(scale, scale);
27763                 
27764                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27765                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27766
27767                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27768                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27769                 
27770                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27771                 
27772                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27773                 
27774                 break;
27775             case 180 :
27776                 
27777                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27778                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27779                 
27780                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27781                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27782                 
27783                 var targetWidth = this.minWidth - 2 * x;
27784                 var targetHeight = this.minHeight - 2 * y;
27785                 
27786                 var scale = 1;
27787                 
27788                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27789                     scale = targetWidth / width;
27790                 }
27791                 
27792                 if(x > 0 && y == 0){
27793                     scale = targetHeight / height;
27794                 }
27795                 
27796                 if(x > 0 && y > 0){
27797                     scale = targetWidth / width;
27798                     
27799                     if(width < height){
27800                         scale = targetHeight / height;
27801                     }
27802                 }
27803                 
27804                 context.scale(scale, scale);
27805                 
27806                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27807                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27808
27809                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27810                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27811
27812                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27813                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27814                 
27815                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27816                 
27817                 break;
27818             case 270 :
27819                 
27820                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27821                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27822                 
27823                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27824                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27825                 
27826                 var targetWidth = this.minWidth - 2 * x;
27827                 var targetHeight = this.minHeight - 2 * y;
27828                 
27829                 var scale = 1;
27830                 
27831                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27832                     scale = targetWidth / width;
27833                 }
27834                 
27835                 if(x > 0 && y == 0){
27836                     scale = targetHeight / height;
27837                 }
27838                 
27839                 if(x > 0 && y > 0){
27840                     scale = targetWidth / width;
27841                     
27842                     if(width < height){
27843                         scale = targetHeight / height;
27844                     }
27845                 }
27846                 
27847                 context.scale(scale, scale);
27848                 
27849                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27850                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27851
27852                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27853                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27854                 
27855                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27856                 
27857                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27858                 
27859                 break;
27860             default : 
27861                 break;
27862         }
27863         
27864         this.cropData = canvas.toDataURL(this.cropType);
27865         
27866         if(this.fireEvent('crop', this, this.cropData) !== false){
27867             this.process(this.file, this.cropData);
27868         }
27869         
27870         return;
27871         
27872     },
27873     
27874     setThumbBoxSize : function()
27875     {
27876         var width, height;
27877         
27878         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27879             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27880             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27881             
27882             this.minWidth = width;
27883             this.minHeight = height;
27884             
27885             if(this.rotate == 90 || this.rotate == 270){
27886                 this.minWidth = height;
27887                 this.minHeight = width;
27888             }
27889         }
27890         
27891         height = 300;
27892         width = Math.ceil(this.minWidth * height / this.minHeight);
27893         
27894         if(this.minWidth > this.minHeight){
27895             width = 300;
27896             height = Math.ceil(this.minHeight * width / this.minWidth);
27897         }
27898         
27899         this.thumbEl.setStyle({
27900             width : width + 'px',
27901             height : height + 'px'
27902         });
27903
27904         return;
27905             
27906     },
27907     
27908     setThumbBoxPosition : function()
27909     {
27910         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27911         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27912         
27913         this.thumbEl.setLeft(x);
27914         this.thumbEl.setTop(y);
27915         
27916     },
27917     
27918     baseRotateLevel : function()
27919     {
27920         this.baseRotate = 1;
27921         
27922         if(
27923                 typeof(this.exif) != 'undefined' &&
27924                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27925                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27926         ){
27927             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27928         }
27929         
27930         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27931         
27932     },
27933     
27934     baseScaleLevel : function()
27935     {
27936         var width, height;
27937         
27938         if(this.isDocument){
27939             
27940             if(this.baseRotate == 6 || this.baseRotate == 8){
27941             
27942                 height = this.thumbEl.getHeight();
27943                 this.baseScale = height / this.imageEl.OriginWidth;
27944
27945                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27946                     width = this.thumbEl.getWidth();
27947                     this.baseScale = width / this.imageEl.OriginHeight;
27948                 }
27949
27950                 return;
27951             }
27952
27953             height = this.thumbEl.getHeight();
27954             this.baseScale = height / this.imageEl.OriginHeight;
27955
27956             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27957                 width = this.thumbEl.getWidth();
27958                 this.baseScale = width / this.imageEl.OriginWidth;
27959             }
27960
27961             return;
27962         }
27963         
27964         if(this.baseRotate == 6 || this.baseRotate == 8){
27965             
27966             width = this.thumbEl.getHeight();
27967             this.baseScale = width / this.imageEl.OriginHeight;
27968             
27969             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27970                 height = this.thumbEl.getWidth();
27971                 this.baseScale = height / this.imageEl.OriginHeight;
27972             }
27973             
27974             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27975                 height = this.thumbEl.getWidth();
27976                 this.baseScale = height / this.imageEl.OriginHeight;
27977                 
27978                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27979                     width = this.thumbEl.getHeight();
27980                     this.baseScale = width / this.imageEl.OriginWidth;
27981                 }
27982             }
27983             
27984             return;
27985         }
27986         
27987         width = this.thumbEl.getWidth();
27988         this.baseScale = width / this.imageEl.OriginWidth;
27989         
27990         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27991             height = this.thumbEl.getHeight();
27992             this.baseScale = height / this.imageEl.OriginHeight;
27993         }
27994         
27995         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27996             
27997             height = this.thumbEl.getHeight();
27998             this.baseScale = height / this.imageEl.OriginHeight;
27999             
28000             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28001                 width = this.thumbEl.getWidth();
28002                 this.baseScale = width / this.imageEl.OriginWidth;
28003             }
28004             
28005         }
28006         
28007         return;
28008     },
28009     
28010     getScaleLevel : function()
28011     {
28012         return this.baseScale * Math.pow(1.1, this.scale);
28013     },
28014     
28015     onTouchStart : function(e)
28016     {
28017         if(!this.canvasLoaded){
28018             this.beforeSelectFile(e);
28019             return;
28020         }
28021         
28022         var touches = e.browserEvent.touches;
28023         
28024         if(!touches){
28025             return;
28026         }
28027         
28028         if(touches.length == 1){
28029             this.onMouseDown(e);
28030             return;
28031         }
28032         
28033         if(touches.length != 2){
28034             return;
28035         }
28036         
28037         var coords = [];
28038         
28039         for(var i = 0, finger; finger = touches[i]; i++){
28040             coords.push(finger.pageX, finger.pageY);
28041         }
28042         
28043         var x = Math.pow(coords[0] - coords[2], 2);
28044         var y = Math.pow(coords[1] - coords[3], 2);
28045         
28046         this.startDistance = Math.sqrt(x + y);
28047         
28048         this.startScale = this.scale;
28049         
28050         this.pinching = true;
28051         this.dragable = false;
28052         
28053     },
28054     
28055     onTouchMove : function(e)
28056     {
28057         if(!this.pinching && !this.dragable){
28058             return;
28059         }
28060         
28061         var touches = e.browserEvent.touches;
28062         
28063         if(!touches){
28064             return;
28065         }
28066         
28067         if(this.dragable){
28068             this.onMouseMove(e);
28069             return;
28070         }
28071         
28072         var coords = [];
28073         
28074         for(var i = 0, finger; finger = touches[i]; i++){
28075             coords.push(finger.pageX, finger.pageY);
28076         }
28077         
28078         var x = Math.pow(coords[0] - coords[2], 2);
28079         var y = Math.pow(coords[1] - coords[3], 2);
28080         
28081         this.endDistance = Math.sqrt(x + y);
28082         
28083         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28084         
28085         if(!this.zoomable()){
28086             this.scale = this.startScale;
28087             return;
28088         }
28089         
28090         this.draw();
28091         
28092     },
28093     
28094     onTouchEnd : function(e)
28095     {
28096         this.pinching = false;
28097         this.dragable = false;
28098         
28099     },
28100     
28101     process : function(file, crop)
28102     {
28103         if(this.loadMask){
28104             this.maskEl.mask(this.loadingText);
28105         }
28106         
28107         this.xhr = new XMLHttpRequest();
28108         
28109         file.xhr = this.xhr;
28110
28111         this.xhr.open(this.method, this.url, true);
28112         
28113         var headers = {
28114             "Accept": "application/json",
28115             "Cache-Control": "no-cache",
28116             "X-Requested-With": "XMLHttpRequest"
28117         };
28118         
28119         for (var headerName in headers) {
28120             var headerValue = headers[headerName];
28121             if (headerValue) {
28122                 this.xhr.setRequestHeader(headerName, headerValue);
28123             }
28124         }
28125         
28126         var _this = this;
28127         
28128         this.xhr.onload = function()
28129         {
28130             _this.xhrOnLoad(_this.xhr);
28131         }
28132         
28133         this.xhr.onerror = function()
28134         {
28135             _this.xhrOnError(_this.xhr);
28136         }
28137         
28138         var formData = new FormData();
28139
28140         formData.append('returnHTML', 'NO');
28141         
28142         if(crop){
28143             formData.append('crop', crop);
28144         }
28145         
28146         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28147             formData.append(this.paramName, file, file.name);
28148         }
28149         
28150         if(typeof(file.filename) != 'undefined'){
28151             formData.append('filename', file.filename);
28152         }
28153         
28154         if(typeof(file.mimetype) != 'undefined'){
28155             formData.append('mimetype', file.mimetype);
28156         }
28157         
28158         if(this.fireEvent('arrange', this, formData) != false){
28159             this.xhr.send(formData);
28160         };
28161     },
28162     
28163     xhrOnLoad : function(xhr)
28164     {
28165         if(this.loadMask){
28166             this.maskEl.unmask();
28167         }
28168         
28169         if (xhr.readyState !== 4) {
28170             this.fireEvent('exception', this, xhr);
28171             return;
28172         }
28173
28174         var response = Roo.decode(xhr.responseText);
28175         
28176         if(!response.success){
28177             this.fireEvent('exception', this, xhr);
28178             return;
28179         }
28180         
28181         var response = Roo.decode(xhr.responseText);
28182         
28183         this.fireEvent('upload', this, response);
28184         
28185     },
28186     
28187     xhrOnError : function()
28188     {
28189         if(this.loadMask){
28190             this.maskEl.unmask();
28191         }
28192         
28193         Roo.log('xhr on error');
28194         
28195         var response = Roo.decode(xhr.responseText);
28196           
28197         Roo.log(response);
28198         
28199     },
28200     
28201     prepare : function(file)
28202     {   
28203         if(this.loadMask){
28204             this.maskEl.mask(this.loadingText);
28205         }
28206         
28207         this.file = false;
28208         this.exif = {};
28209         
28210         if(typeof(file) === 'string'){
28211             this.loadCanvas(file);
28212             return;
28213         }
28214         
28215         if(!file || !this.urlAPI){
28216             return;
28217         }
28218         
28219         this.file = file;
28220         this.cropType = file.type;
28221         
28222         var _this = this;
28223         
28224         if(this.fireEvent('prepare', this, this.file) != false){
28225             
28226             var reader = new FileReader();
28227             
28228             reader.onload = function (e) {
28229                 if (e.target.error) {
28230                     Roo.log(e.target.error);
28231                     return;
28232                 }
28233                 
28234                 var buffer = e.target.result,
28235                     dataView = new DataView(buffer),
28236                     offset = 2,
28237                     maxOffset = dataView.byteLength - 4,
28238                     markerBytes,
28239                     markerLength;
28240                 
28241                 if (dataView.getUint16(0) === 0xffd8) {
28242                     while (offset < maxOffset) {
28243                         markerBytes = dataView.getUint16(offset);
28244                         
28245                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28246                             markerLength = dataView.getUint16(offset + 2) + 2;
28247                             if (offset + markerLength > dataView.byteLength) {
28248                                 Roo.log('Invalid meta data: Invalid segment size.');
28249                                 break;
28250                             }
28251                             
28252                             if(markerBytes == 0xffe1){
28253                                 _this.parseExifData(
28254                                     dataView,
28255                                     offset,
28256                                     markerLength
28257                                 );
28258                             }
28259                             
28260                             offset += markerLength;
28261                             
28262                             continue;
28263                         }
28264                         
28265                         break;
28266                     }
28267                     
28268                 }
28269                 
28270                 var url = _this.urlAPI.createObjectURL(_this.file);
28271                 
28272                 _this.loadCanvas(url);
28273                 
28274                 return;
28275             }
28276             
28277             reader.readAsArrayBuffer(this.file);
28278             
28279         }
28280         
28281     },
28282     
28283     parseExifData : function(dataView, offset, length)
28284     {
28285         var tiffOffset = offset + 10,
28286             littleEndian,
28287             dirOffset;
28288     
28289         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28290             // No Exif data, might be XMP data instead
28291             return;
28292         }
28293         
28294         // Check for the ASCII code for "Exif" (0x45786966):
28295         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28296             // No Exif data, might be XMP data instead
28297             return;
28298         }
28299         if (tiffOffset + 8 > dataView.byteLength) {
28300             Roo.log('Invalid Exif data: Invalid segment size.');
28301             return;
28302         }
28303         // Check for the two null bytes:
28304         if (dataView.getUint16(offset + 8) !== 0x0000) {
28305             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28306             return;
28307         }
28308         // Check the byte alignment:
28309         switch (dataView.getUint16(tiffOffset)) {
28310         case 0x4949:
28311             littleEndian = true;
28312             break;
28313         case 0x4D4D:
28314             littleEndian = false;
28315             break;
28316         default:
28317             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28318             return;
28319         }
28320         // Check for the TIFF tag marker (0x002A):
28321         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28322             Roo.log('Invalid Exif data: Missing TIFF marker.');
28323             return;
28324         }
28325         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28326         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28327         
28328         this.parseExifTags(
28329             dataView,
28330             tiffOffset,
28331             tiffOffset + dirOffset,
28332             littleEndian
28333         );
28334     },
28335     
28336     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28337     {
28338         var tagsNumber,
28339             dirEndOffset,
28340             i;
28341         if (dirOffset + 6 > dataView.byteLength) {
28342             Roo.log('Invalid Exif data: Invalid directory offset.');
28343             return;
28344         }
28345         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28346         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28347         if (dirEndOffset + 4 > dataView.byteLength) {
28348             Roo.log('Invalid Exif data: Invalid directory size.');
28349             return;
28350         }
28351         for (i = 0; i < tagsNumber; i += 1) {
28352             this.parseExifTag(
28353                 dataView,
28354                 tiffOffset,
28355                 dirOffset + 2 + 12 * i, // tag offset
28356                 littleEndian
28357             );
28358         }
28359         // Return the offset to the next directory:
28360         return dataView.getUint32(dirEndOffset, littleEndian);
28361     },
28362     
28363     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28364     {
28365         var tag = dataView.getUint16(offset, littleEndian);
28366         
28367         this.exif[tag] = this.getExifValue(
28368             dataView,
28369             tiffOffset,
28370             offset,
28371             dataView.getUint16(offset + 2, littleEndian), // tag type
28372             dataView.getUint32(offset + 4, littleEndian), // tag length
28373             littleEndian
28374         );
28375     },
28376     
28377     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28378     {
28379         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28380             tagSize,
28381             dataOffset,
28382             values,
28383             i,
28384             str,
28385             c;
28386     
28387         if (!tagType) {
28388             Roo.log('Invalid Exif data: Invalid tag type.');
28389             return;
28390         }
28391         
28392         tagSize = tagType.size * length;
28393         // Determine if the value is contained in the dataOffset bytes,
28394         // or if the value at the dataOffset is a pointer to the actual data:
28395         dataOffset = tagSize > 4 ?
28396                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28397         if (dataOffset + tagSize > dataView.byteLength) {
28398             Roo.log('Invalid Exif data: Invalid data offset.');
28399             return;
28400         }
28401         if (length === 1) {
28402             return tagType.getValue(dataView, dataOffset, littleEndian);
28403         }
28404         values = [];
28405         for (i = 0; i < length; i += 1) {
28406             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28407         }
28408         
28409         if (tagType.ascii) {
28410             str = '';
28411             // Concatenate the chars:
28412             for (i = 0; i < values.length; i += 1) {
28413                 c = values[i];
28414                 // Ignore the terminating NULL byte(s):
28415                 if (c === '\u0000') {
28416                     break;
28417                 }
28418                 str += c;
28419             }
28420             return str;
28421         }
28422         return values;
28423     }
28424     
28425 });
28426
28427 Roo.apply(Roo.bootstrap.UploadCropbox, {
28428     tags : {
28429         'Orientation': 0x0112
28430     },
28431     
28432     Orientation: {
28433             1: 0, //'top-left',
28434 //            2: 'top-right',
28435             3: 180, //'bottom-right',
28436 //            4: 'bottom-left',
28437 //            5: 'left-top',
28438             6: 90, //'right-top',
28439 //            7: 'right-bottom',
28440             8: 270 //'left-bottom'
28441     },
28442     
28443     exifTagTypes : {
28444         // byte, 8-bit unsigned int:
28445         1: {
28446             getValue: function (dataView, dataOffset) {
28447                 return dataView.getUint8(dataOffset);
28448             },
28449             size: 1
28450         },
28451         // ascii, 8-bit byte:
28452         2: {
28453             getValue: function (dataView, dataOffset) {
28454                 return String.fromCharCode(dataView.getUint8(dataOffset));
28455             },
28456             size: 1,
28457             ascii: true
28458         },
28459         // short, 16 bit int:
28460         3: {
28461             getValue: function (dataView, dataOffset, littleEndian) {
28462                 return dataView.getUint16(dataOffset, littleEndian);
28463             },
28464             size: 2
28465         },
28466         // long, 32 bit int:
28467         4: {
28468             getValue: function (dataView, dataOffset, littleEndian) {
28469                 return dataView.getUint32(dataOffset, littleEndian);
28470             },
28471             size: 4
28472         },
28473         // rational = two long values, first is numerator, second is denominator:
28474         5: {
28475             getValue: function (dataView, dataOffset, littleEndian) {
28476                 return dataView.getUint32(dataOffset, littleEndian) /
28477                     dataView.getUint32(dataOffset + 4, littleEndian);
28478             },
28479             size: 8
28480         },
28481         // slong, 32 bit signed int:
28482         9: {
28483             getValue: function (dataView, dataOffset, littleEndian) {
28484                 return dataView.getInt32(dataOffset, littleEndian);
28485             },
28486             size: 4
28487         },
28488         // srational, two slongs, first is numerator, second is denominator:
28489         10: {
28490             getValue: function (dataView, dataOffset, littleEndian) {
28491                 return dataView.getInt32(dataOffset, littleEndian) /
28492                     dataView.getInt32(dataOffset + 4, littleEndian);
28493             },
28494             size: 8
28495         }
28496     },
28497     
28498     footer : {
28499         STANDARD : [
28500             {
28501                 tag : 'div',
28502                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28503                 action : 'rotate-left',
28504                 cn : [
28505                     {
28506                         tag : 'button',
28507                         cls : 'btn btn-default',
28508                         html : '<i class="fa fa-undo"></i>'
28509                     }
28510                 ]
28511             },
28512             {
28513                 tag : 'div',
28514                 cls : 'btn-group roo-upload-cropbox-picture',
28515                 action : 'picture',
28516                 cn : [
28517                     {
28518                         tag : 'button',
28519                         cls : 'btn btn-default',
28520                         html : '<i class="fa fa-picture-o"></i>'
28521                     }
28522                 ]
28523             },
28524             {
28525                 tag : 'div',
28526                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28527                 action : 'rotate-right',
28528                 cn : [
28529                     {
28530                         tag : 'button',
28531                         cls : 'btn btn-default',
28532                         html : '<i class="fa fa-repeat"></i>'
28533                     }
28534                 ]
28535             }
28536         ],
28537         DOCUMENT : [
28538             {
28539                 tag : 'div',
28540                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28541                 action : 'rotate-left',
28542                 cn : [
28543                     {
28544                         tag : 'button',
28545                         cls : 'btn btn-default',
28546                         html : '<i class="fa fa-undo"></i>'
28547                     }
28548                 ]
28549             },
28550             {
28551                 tag : 'div',
28552                 cls : 'btn-group roo-upload-cropbox-download',
28553                 action : 'download',
28554                 cn : [
28555                     {
28556                         tag : 'button',
28557                         cls : 'btn btn-default',
28558                         html : '<i class="fa fa-download"></i>'
28559                     }
28560                 ]
28561             },
28562             {
28563                 tag : 'div',
28564                 cls : 'btn-group roo-upload-cropbox-crop',
28565                 action : 'crop',
28566                 cn : [
28567                     {
28568                         tag : 'button',
28569                         cls : 'btn btn-default',
28570                         html : '<i class="fa fa-crop"></i>'
28571                     }
28572                 ]
28573             },
28574             {
28575                 tag : 'div',
28576                 cls : 'btn-group roo-upload-cropbox-trash',
28577                 action : 'trash',
28578                 cn : [
28579                     {
28580                         tag : 'button',
28581                         cls : 'btn btn-default',
28582                         html : '<i class="fa fa-trash"></i>'
28583                     }
28584                 ]
28585             },
28586             {
28587                 tag : 'div',
28588                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28589                 action : 'rotate-right',
28590                 cn : [
28591                     {
28592                         tag : 'button',
28593                         cls : 'btn btn-default',
28594                         html : '<i class="fa fa-repeat"></i>'
28595                     }
28596                 ]
28597             }
28598         ],
28599         ROTATOR : [
28600             {
28601                 tag : 'div',
28602                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28603                 action : 'rotate-left',
28604                 cn : [
28605                     {
28606                         tag : 'button',
28607                         cls : 'btn btn-default',
28608                         html : '<i class="fa fa-undo"></i>'
28609                     }
28610                 ]
28611             },
28612             {
28613                 tag : 'div',
28614                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28615                 action : 'rotate-right',
28616                 cn : [
28617                     {
28618                         tag : 'button',
28619                         cls : 'btn btn-default',
28620                         html : '<i class="fa fa-repeat"></i>'
28621                     }
28622                 ]
28623             }
28624         ]
28625     }
28626 });
28627
28628 /*
28629 * Licence: LGPL
28630 */
28631
28632 /**
28633  * @class Roo.bootstrap.DocumentManager
28634  * @extends Roo.bootstrap.Component
28635  * Bootstrap DocumentManager class
28636  * @cfg {String} paramName default 'imageUpload'
28637  * @cfg {String} toolTipName default 'filename'
28638  * @cfg {String} method default POST
28639  * @cfg {String} url action url
28640  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28641  * @cfg {Boolean} multiple multiple upload default true
28642  * @cfg {Number} thumbSize default 300
28643  * @cfg {String} fieldLabel
28644  * @cfg {Number} labelWidth default 4
28645  * @cfg {String} labelAlign (left|top) default left
28646  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28647 * @cfg {Number} labellg set the width of label (1-12)
28648  * @cfg {Number} labelmd set the width of label (1-12)
28649  * @cfg {Number} labelsm set the width of label (1-12)
28650  * @cfg {Number} labelxs set the width of label (1-12)
28651  * 
28652  * @constructor
28653  * Create a new DocumentManager
28654  * @param {Object} config The config object
28655  */
28656
28657 Roo.bootstrap.DocumentManager = function(config){
28658     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28659     
28660     this.files = [];
28661     this.delegates = [];
28662     
28663     this.addEvents({
28664         /**
28665          * @event initial
28666          * Fire when initial the DocumentManager
28667          * @param {Roo.bootstrap.DocumentManager} this
28668          */
28669         "initial" : true,
28670         /**
28671          * @event inspect
28672          * inspect selected file
28673          * @param {Roo.bootstrap.DocumentManager} this
28674          * @param {File} file
28675          */
28676         "inspect" : true,
28677         /**
28678          * @event exception
28679          * Fire when xhr load exception
28680          * @param {Roo.bootstrap.DocumentManager} this
28681          * @param {XMLHttpRequest} xhr
28682          */
28683         "exception" : true,
28684         /**
28685          * @event afterupload
28686          * Fire when xhr load exception
28687          * @param {Roo.bootstrap.DocumentManager} this
28688          * @param {XMLHttpRequest} xhr
28689          */
28690         "afterupload" : true,
28691         /**
28692          * @event prepare
28693          * prepare the form data
28694          * @param {Roo.bootstrap.DocumentManager} this
28695          * @param {Object} formData
28696          */
28697         "prepare" : true,
28698         /**
28699          * @event remove
28700          * Fire when remove the file
28701          * @param {Roo.bootstrap.DocumentManager} this
28702          * @param {Object} file
28703          */
28704         "remove" : true,
28705         /**
28706          * @event refresh
28707          * Fire after refresh the file
28708          * @param {Roo.bootstrap.DocumentManager} this
28709          */
28710         "refresh" : true,
28711         /**
28712          * @event click
28713          * Fire after click the image
28714          * @param {Roo.bootstrap.DocumentManager} this
28715          * @param {Object} file
28716          */
28717         "click" : true,
28718         /**
28719          * @event edit
28720          * Fire when upload a image and editable set to true
28721          * @param {Roo.bootstrap.DocumentManager} this
28722          * @param {Object} file
28723          */
28724         "edit" : true,
28725         /**
28726          * @event beforeselectfile
28727          * Fire before select file
28728          * @param {Roo.bootstrap.DocumentManager} this
28729          */
28730         "beforeselectfile" : true,
28731         /**
28732          * @event process
28733          * Fire before process file
28734          * @param {Roo.bootstrap.DocumentManager} this
28735          * @param {Object} file
28736          */
28737         "process" : true,
28738         /**
28739          * @event previewrendered
28740          * Fire when preview rendered
28741          * @param {Roo.bootstrap.DocumentManager} this
28742          * @param {Object} file
28743          */
28744         "previewrendered" : true,
28745         /**
28746          */
28747         "previewResize" : true
28748         
28749     });
28750 };
28751
28752 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28753     
28754     boxes : 0,
28755     inputName : '',
28756     thumbSize : 300,
28757     multiple : true,
28758     files : false,
28759     method : 'POST',
28760     url : '',
28761     paramName : 'imageUpload',
28762     toolTipName : 'filename',
28763     fieldLabel : '',
28764     labelWidth : 4,
28765     labelAlign : 'left',
28766     editable : true,
28767     delegates : false,
28768     xhr : false, 
28769     
28770     labellg : 0,
28771     labelmd : 0,
28772     labelsm : 0,
28773     labelxs : 0,
28774     
28775     getAutoCreate : function()
28776     {   
28777         var managerWidget = {
28778             tag : 'div',
28779             cls : 'roo-document-manager',
28780             cn : [
28781                 {
28782                     tag : 'input',
28783                     cls : 'roo-document-manager-selector',
28784                     type : 'file'
28785                 },
28786                 {
28787                     tag : 'div',
28788                     cls : 'roo-document-manager-uploader',
28789                     cn : [
28790                         {
28791                             tag : 'div',
28792                             cls : 'roo-document-manager-upload-btn',
28793                             html : '<i class="fa fa-plus"></i>'
28794                         }
28795                     ]
28796                     
28797                 }
28798             ]
28799         };
28800         
28801         var content = [
28802             {
28803                 tag : 'div',
28804                 cls : 'column col-md-12',
28805                 cn : managerWidget
28806             }
28807         ];
28808         
28809         if(this.fieldLabel.length){
28810             
28811             content = [
28812                 {
28813                     tag : 'div',
28814                     cls : 'column col-md-12',
28815                     html : this.fieldLabel
28816                 },
28817                 {
28818                     tag : 'div',
28819                     cls : 'column col-md-12',
28820                     cn : managerWidget
28821                 }
28822             ];
28823
28824             if(this.labelAlign == 'left'){
28825                 content = [
28826                     {
28827                         tag : 'div',
28828                         cls : 'column',
28829                         html : this.fieldLabel
28830                     },
28831                     {
28832                         tag : 'div',
28833                         cls : 'column',
28834                         cn : managerWidget
28835                     }
28836                 ];
28837                 
28838                 if(this.labelWidth > 12){
28839                     content[0].style = "width: " + this.labelWidth + 'px';
28840                 }
28841
28842                 if(this.labelWidth < 13 && this.labelmd == 0){
28843                     this.labelmd = this.labelWidth;
28844                 }
28845
28846                 if(this.labellg > 0){
28847                     content[0].cls += ' col-lg-' + this.labellg;
28848                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28849                 }
28850
28851                 if(this.labelmd > 0){
28852                     content[0].cls += ' col-md-' + this.labelmd;
28853                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28854                 }
28855
28856                 if(this.labelsm > 0){
28857                     content[0].cls += ' col-sm-' + this.labelsm;
28858                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28859                 }
28860
28861                 if(this.labelxs > 0){
28862                     content[0].cls += ' col-xs-' + this.labelxs;
28863                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28864                 }
28865                 
28866             }
28867         }
28868         
28869         var cfg = {
28870             tag : 'div',
28871             cls : 'row clearfix',
28872             cn : content
28873         };
28874         
28875         return cfg;
28876         
28877     },
28878     
28879     initEvents : function()
28880     {
28881         this.managerEl = this.el.select('.roo-document-manager', true).first();
28882         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28883         
28884         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28885         this.selectorEl.hide();
28886         
28887         if(this.multiple){
28888             this.selectorEl.attr('multiple', 'multiple');
28889         }
28890         
28891         this.selectorEl.on('change', this.onFileSelected, this);
28892         
28893         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28894         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28895         
28896         this.uploader.on('click', this.onUploaderClick, this);
28897         
28898         this.renderProgressDialog();
28899         
28900         var _this = this;
28901         
28902         window.addEventListener("resize", function() { _this.refresh(); } );
28903         
28904         this.fireEvent('initial', this);
28905     },
28906     
28907     renderProgressDialog : function()
28908     {
28909         var _this = this;
28910         
28911         this.progressDialog = new Roo.bootstrap.Modal({
28912             cls : 'roo-document-manager-progress-dialog',
28913             allow_close : false,
28914             title : '',
28915             buttons : [
28916                 {
28917                     name  :'cancel',
28918                     weight : 'danger',
28919                     html : 'Cancel'
28920                 }
28921             ], 
28922             listeners : { 
28923                 btnclick : function() {
28924                     _this.uploadCancel();
28925                     this.hide();
28926                 }
28927             }
28928         });
28929          
28930         this.progressDialog.render(Roo.get(document.body));
28931          
28932         this.progress = new Roo.bootstrap.Progress({
28933             cls : 'roo-document-manager-progress',
28934             active : true,
28935             striped : true
28936         });
28937         
28938         this.progress.render(this.progressDialog.getChildContainer());
28939         
28940         this.progressBar = new Roo.bootstrap.ProgressBar({
28941             cls : 'roo-document-manager-progress-bar',
28942             aria_valuenow : 0,
28943             aria_valuemin : 0,
28944             aria_valuemax : 12,
28945             panel : 'success'
28946         });
28947         
28948         this.progressBar.render(this.progress.getChildContainer());
28949     },
28950     
28951     onUploaderClick : function(e)
28952     {
28953         e.preventDefault();
28954      
28955         if(this.fireEvent('beforeselectfile', this) != false){
28956             this.selectorEl.dom.click();
28957         }
28958         
28959     },
28960     
28961     onFileSelected : function(e)
28962     {
28963         e.preventDefault();
28964         
28965         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28966             return;
28967         }
28968         
28969         Roo.each(this.selectorEl.dom.files, function(file){
28970             if(this.fireEvent('inspect', this, file) != false){
28971                 this.files.push(file);
28972             }
28973         }, this);
28974         
28975         this.queue();
28976         
28977     },
28978     
28979     queue : function()
28980     {
28981         this.selectorEl.dom.value = '';
28982         
28983         if(!this.files || !this.files.length){
28984             return;
28985         }
28986         
28987         if(this.boxes > 0 && this.files.length > this.boxes){
28988             this.files = this.files.slice(0, this.boxes);
28989         }
28990         
28991         this.uploader.show();
28992         
28993         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28994             this.uploader.hide();
28995         }
28996         
28997         var _this = this;
28998         
28999         var files = [];
29000         
29001         var docs = [];
29002         
29003         Roo.each(this.files, function(file){
29004             
29005             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29006                 var f = this.renderPreview(file);
29007                 files.push(f);
29008                 return;
29009             }
29010             
29011             if(file.type.indexOf('image') != -1){
29012                 this.delegates.push(
29013                     (function(){
29014                         _this.process(file);
29015                     }).createDelegate(this)
29016                 );
29017         
29018                 return;
29019             }
29020             
29021             docs.push(
29022                 (function(){
29023                     _this.process(file);
29024                 }).createDelegate(this)
29025             );
29026             
29027         }, this);
29028         
29029         this.files = files;
29030         
29031         this.delegates = this.delegates.concat(docs);
29032         
29033         if(!this.delegates.length){
29034             this.refresh();
29035             return;
29036         }
29037         
29038         this.progressBar.aria_valuemax = this.delegates.length;
29039         
29040         this.arrange();
29041         
29042         return;
29043     },
29044     
29045     arrange : function()
29046     {
29047         if(!this.delegates.length){
29048             this.progressDialog.hide();
29049             this.refresh();
29050             return;
29051         }
29052         
29053         var delegate = this.delegates.shift();
29054         
29055         this.progressDialog.show();
29056         
29057         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29058         
29059         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29060         
29061         delegate();
29062     },
29063     
29064     refresh : function()
29065     {
29066         this.uploader.show();
29067         
29068         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29069             this.uploader.hide();
29070         }
29071         
29072         Roo.isTouch ? this.closable(false) : this.closable(true);
29073         
29074         this.fireEvent('refresh', this);
29075     },
29076     
29077     onRemove : function(e, el, o)
29078     {
29079         e.preventDefault();
29080         
29081         this.fireEvent('remove', this, o);
29082         
29083     },
29084     
29085     remove : function(o)
29086     {
29087         var files = [];
29088         
29089         Roo.each(this.files, function(file){
29090             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29091                 files.push(file);
29092                 return;
29093             }
29094
29095             o.target.remove();
29096
29097         }, this);
29098         
29099         this.files = files;
29100         
29101         this.refresh();
29102     },
29103     
29104     clear : function()
29105     {
29106         Roo.each(this.files, function(file){
29107             if(!file.target){
29108                 return;
29109             }
29110             
29111             file.target.remove();
29112
29113         }, this);
29114         
29115         this.files = [];
29116         
29117         this.refresh();
29118     },
29119     
29120     onClick : function(e, el, o)
29121     {
29122         e.preventDefault();
29123         
29124         this.fireEvent('click', this, o);
29125         
29126     },
29127     
29128     closable : function(closable)
29129     {
29130         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29131             
29132             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29133             
29134             if(closable){
29135                 el.show();
29136                 return;
29137             }
29138             
29139             el.hide();
29140             
29141         }, this);
29142     },
29143     
29144     xhrOnLoad : function(xhr)
29145     {
29146         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29147             el.remove();
29148         }, this);
29149         
29150         if (xhr.readyState !== 4) {
29151             this.arrange();
29152             this.fireEvent('exception', this, xhr);
29153             return;
29154         }
29155
29156         var response = Roo.decode(xhr.responseText);
29157         
29158         if(!response.success){
29159             this.arrange();
29160             this.fireEvent('exception', this, xhr);
29161             return;
29162         }
29163         
29164         var file = this.renderPreview(response.data);
29165         
29166         this.files.push(file);
29167         
29168         this.arrange();
29169         
29170         this.fireEvent('afterupload', this, xhr);
29171         
29172     },
29173     
29174     xhrOnError : function(xhr)
29175     {
29176         Roo.log('xhr on error');
29177         
29178         var response = Roo.decode(xhr.responseText);
29179           
29180         Roo.log(response);
29181         
29182         this.arrange();
29183     },
29184     
29185     process : function(file)
29186     {
29187         if(this.fireEvent('process', this, file) !== false){
29188             if(this.editable && file.type.indexOf('image') != -1){
29189                 this.fireEvent('edit', this, file);
29190                 return;
29191             }
29192
29193             this.uploadStart(file, false);
29194
29195             return;
29196         }
29197         
29198     },
29199     
29200     uploadStart : function(file, crop)
29201     {
29202         this.xhr = new XMLHttpRequest();
29203         
29204         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29205             this.arrange();
29206             return;
29207         }
29208         
29209         file.xhr = this.xhr;
29210             
29211         this.managerEl.createChild({
29212             tag : 'div',
29213             cls : 'roo-document-manager-loading',
29214             cn : [
29215                 {
29216                     tag : 'div',
29217                     tooltip : file.name,
29218                     cls : 'roo-document-manager-thumb',
29219                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29220                 }
29221             ]
29222
29223         });
29224
29225         this.xhr.open(this.method, this.url, true);
29226         
29227         var headers = {
29228             "Accept": "application/json",
29229             "Cache-Control": "no-cache",
29230             "X-Requested-With": "XMLHttpRequest"
29231         };
29232         
29233         for (var headerName in headers) {
29234             var headerValue = headers[headerName];
29235             if (headerValue) {
29236                 this.xhr.setRequestHeader(headerName, headerValue);
29237             }
29238         }
29239         
29240         var _this = this;
29241         
29242         this.xhr.onload = function()
29243         {
29244             _this.xhrOnLoad(_this.xhr);
29245         }
29246         
29247         this.xhr.onerror = function()
29248         {
29249             _this.xhrOnError(_this.xhr);
29250         }
29251         
29252         var formData = new FormData();
29253
29254         formData.append('returnHTML', 'NO');
29255         
29256         if(crop){
29257             formData.append('crop', crop);
29258         }
29259         
29260         formData.append(this.paramName, file, file.name);
29261         
29262         var options = {
29263             file : file, 
29264             manually : false
29265         };
29266         
29267         if(this.fireEvent('prepare', this, formData, options) != false){
29268             
29269             if(options.manually){
29270                 return;
29271             }
29272             
29273             this.xhr.send(formData);
29274             return;
29275         };
29276         
29277         this.uploadCancel();
29278     },
29279     
29280     uploadCancel : function()
29281     {
29282         if (this.xhr) {
29283             this.xhr.abort();
29284         }
29285         
29286         this.delegates = [];
29287         
29288         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29289             el.remove();
29290         }, this);
29291         
29292         this.arrange();
29293     },
29294     
29295     renderPreview : function(file)
29296     {
29297         if(typeof(file.target) != 'undefined' && file.target){
29298             return file;
29299         }
29300         
29301         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29302         
29303         var previewEl = this.managerEl.createChild({
29304             tag : 'div',
29305             cls : 'roo-document-manager-preview',
29306             cn : [
29307                 {
29308                     tag : 'div',
29309                     tooltip : file[this.toolTipName],
29310                     cls : 'roo-document-manager-thumb',
29311                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29312                 },
29313                 {
29314                     tag : 'button',
29315                     cls : 'close',
29316                     html : '<i class="fa fa-times-circle"></i>'
29317                 }
29318             ]
29319         });
29320
29321         var close = previewEl.select('button.close', true).first();
29322
29323         close.on('click', this.onRemove, this, file);
29324
29325         file.target = previewEl;
29326
29327         var image = previewEl.select('img', true).first();
29328         
29329         var _this = this;
29330         
29331         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29332         
29333         image.on('click', this.onClick, this, file);
29334         
29335         this.fireEvent('previewrendered', this, file);
29336         
29337         return file;
29338         
29339     },
29340     
29341     onPreviewLoad : function(file, image)
29342     {
29343         if(typeof(file.target) == 'undefined' || !file.target){
29344             return;
29345         }
29346         
29347         var width = image.dom.naturalWidth || image.dom.width;
29348         var height = image.dom.naturalHeight || image.dom.height;
29349         
29350         if(!this.previewResize) {
29351             return;
29352         }
29353         
29354         if(width > height){
29355             file.target.addClass('wide');
29356             return;
29357         }
29358         
29359         file.target.addClass('tall');
29360         return;
29361         
29362     },
29363     
29364     uploadFromSource : function(file, crop)
29365     {
29366         this.xhr = new XMLHttpRequest();
29367         
29368         this.managerEl.createChild({
29369             tag : 'div',
29370             cls : 'roo-document-manager-loading',
29371             cn : [
29372                 {
29373                     tag : 'div',
29374                     tooltip : file.name,
29375                     cls : 'roo-document-manager-thumb',
29376                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29377                 }
29378             ]
29379
29380         });
29381
29382         this.xhr.open(this.method, this.url, true);
29383         
29384         var headers = {
29385             "Accept": "application/json",
29386             "Cache-Control": "no-cache",
29387             "X-Requested-With": "XMLHttpRequest"
29388         };
29389         
29390         for (var headerName in headers) {
29391             var headerValue = headers[headerName];
29392             if (headerValue) {
29393                 this.xhr.setRequestHeader(headerName, headerValue);
29394             }
29395         }
29396         
29397         var _this = this;
29398         
29399         this.xhr.onload = function()
29400         {
29401             _this.xhrOnLoad(_this.xhr);
29402         }
29403         
29404         this.xhr.onerror = function()
29405         {
29406             _this.xhrOnError(_this.xhr);
29407         }
29408         
29409         var formData = new FormData();
29410
29411         formData.append('returnHTML', 'NO');
29412         
29413         formData.append('crop', crop);
29414         
29415         if(typeof(file.filename) != 'undefined'){
29416             formData.append('filename', file.filename);
29417         }
29418         
29419         if(typeof(file.mimetype) != 'undefined'){
29420             formData.append('mimetype', file.mimetype);
29421         }
29422         
29423         Roo.log(formData);
29424         
29425         if(this.fireEvent('prepare', this, formData) != false){
29426             this.xhr.send(formData);
29427         };
29428     }
29429 });
29430
29431 /*
29432 * Licence: LGPL
29433 */
29434
29435 /**
29436  * @class Roo.bootstrap.DocumentViewer
29437  * @extends Roo.bootstrap.Component
29438  * Bootstrap DocumentViewer class
29439  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29440  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29441  * 
29442  * @constructor
29443  * Create a new DocumentViewer
29444  * @param {Object} config The config object
29445  */
29446
29447 Roo.bootstrap.DocumentViewer = function(config){
29448     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29449     
29450     this.addEvents({
29451         /**
29452          * @event initial
29453          * Fire after initEvent
29454          * @param {Roo.bootstrap.DocumentViewer} this
29455          */
29456         "initial" : true,
29457         /**
29458          * @event click
29459          * Fire after click
29460          * @param {Roo.bootstrap.DocumentViewer} this
29461          */
29462         "click" : true,
29463         /**
29464          * @event download
29465          * Fire after download button
29466          * @param {Roo.bootstrap.DocumentViewer} this
29467          */
29468         "download" : true,
29469         /**
29470          * @event trash
29471          * Fire after trash button
29472          * @param {Roo.bootstrap.DocumentViewer} this
29473          */
29474         "trash" : true
29475         
29476     });
29477 };
29478
29479 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29480     
29481     showDownload : true,
29482     
29483     showTrash : true,
29484     
29485     getAutoCreate : function()
29486     {
29487         var cfg = {
29488             tag : 'div',
29489             cls : 'roo-document-viewer',
29490             cn : [
29491                 {
29492                     tag : 'div',
29493                     cls : 'roo-document-viewer-body',
29494                     cn : [
29495                         {
29496                             tag : 'div',
29497                             cls : 'roo-document-viewer-thumb',
29498                             cn : [
29499                                 {
29500                                     tag : 'img',
29501                                     cls : 'roo-document-viewer-image'
29502                                 }
29503                             ]
29504                         }
29505                     ]
29506                 },
29507                 {
29508                     tag : 'div',
29509                     cls : 'roo-document-viewer-footer',
29510                     cn : {
29511                         tag : 'div',
29512                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29513                         cn : [
29514                             {
29515                                 tag : 'div',
29516                                 cls : 'btn-group roo-document-viewer-download',
29517                                 cn : [
29518                                     {
29519                                         tag : 'button',
29520                                         cls : 'btn btn-default',
29521                                         html : '<i class="fa fa-download"></i>'
29522                                     }
29523                                 ]
29524                             },
29525                             {
29526                                 tag : 'div',
29527                                 cls : 'btn-group roo-document-viewer-trash',
29528                                 cn : [
29529                                     {
29530                                         tag : 'button',
29531                                         cls : 'btn btn-default',
29532                                         html : '<i class="fa fa-trash"></i>'
29533                                     }
29534                                 ]
29535                             }
29536                         ]
29537                     }
29538                 }
29539             ]
29540         };
29541         
29542         return cfg;
29543     },
29544     
29545     initEvents : function()
29546     {
29547         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29548         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29549         
29550         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29551         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29552         
29553         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29554         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29555         
29556         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29557         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29558         
29559         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29560         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29561         
29562         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29563         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29564         
29565         this.bodyEl.on('click', this.onClick, this);
29566         this.downloadBtn.on('click', this.onDownload, this);
29567         this.trashBtn.on('click', this.onTrash, this);
29568         
29569         this.downloadBtn.hide();
29570         this.trashBtn.hide();
29571         
29572         if(this.showDownload){
29573             this.downloadBtn.show();
29574         }
29575         
29576         if(this.showTrash){
29577             this.trashBtn.show();
29578         }
29579         
29580         if(!this.showDownload && !this.showTrash) {
29581             this.footerEl.hide();
29582         }
29583         
29584     },
29585     
29586     initial : function()
29587     {
29588         this.fireEvent('initial', this);
29589         
29590     },
29591     
29592     onClick : function(e)
29593     {
29594         e.preventDefault();
29595         
29596         this.fireEvent('click', this);
29597     },
29598     
29599     onDownload : function(e)
29600     {
29601         e.preventDefault();
29602         
29603         this.fireEvent('download', this);
29604     },
29605     
29606     onTrash : function(e)
29607     {
29608         e.preventDefault();
29609         
29610         this.fireEvent('trash', this);
29611     }
29612     
29613 });
29614 /*
29615  * - LGPL
29616  *
29617  * nav progress bar
29618  * 
29619  */
29620
29621 /**
29622  * @class Roo.bootstrap.NavProgressBar
29623  * @extends Roo.bootstrap.Component
29624  * Bootstrap NavProgressBar class
29625  * 
29626  * @constructor
29627  * Create a new nav progress bar
29628  * @param {Object} config The config object
29629  */
29630
29631 Roo.bootstrap.NavProgressBar = function(config){
29632     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29633
29634     this.bullets = this.bullets || [];
29635    
29636 //    Roo.bootstrap.NavProgressBar.register(this);
29637      this.addEvents({
29638         /**
29639              * @event changed
29640              * Fires when the active item changes
29641              * @param {Roo.bootstrap.NavProgressBar} this
29642              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29643              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29644          */
29645         'changed': true
29646      });
29647     
29648 };
29649
29650 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29651     
29652     bullets : [],
29653     barItems : [],
29654     
29655     getAutoCreate : function()
29656     {
29657         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29658         
29659         cfg = {
29660             tag : 'div',
29661             cls : 'roo-navigation-bar-group',
29662             cn : [
29663                 {
29664                     tag : 'div',
29665                     cls : 'roo-navigation-top-bar'
29666                 },
29667                 {
29668                     tag : 'div',
29669                     cls : 'roo-navigation-bullets-bar',
29670                     cn : [
29671                         {
29672                             tag : 'ul',
29673                             cls : 'roo-navigation-bar'
29674                         }
29675                     ]
29676                 },
29677                 
29678                 {
29679                     tag : 'div',
29680                     cls : 'roo-navigation-bottom-bar'
29681                 }
29682             ]
29683             
29684         };
29685         
29686         return cfg;
29687         
29688     },
29689     
29690     initEvents: function() 
29691     {
29692         
29693     },
29694     
29695     onRender : function(ct, position) 
29696     {
29697         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29698         
29699         if(this.bullets.length){
29700             Roo.each(this.bullets, function(b){
29701                this.addItem(b);
29702             }, this);
29703         }
29704         
29705         this.format();
29706         
29707     },
29708     
29709     addItem : function(cfg)
29710     {
29711         var item = new Roo.bootstrap.NavProgressItem(cfg);
29712         
29713         item.parentId = this.id;
29714         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29715         
29716         if(cfg.html){
29717             var top = new Roo.bootstrap.Element({
29718                 tag : 'div',
29719                 cls : 'roo-navigation-bar-text'
29720             });
29721             
29722             var bottom = new Roo.bootstrap.Element({
29723                 tag : 'div',
29724                 cls : 'roo-navigation-bar-text'
29725             });
29726             
29727             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29728             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29729             
29730             var topText = new Roo.bootstrap.Element({
29731                 tag : 'span',
29732                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29733             });
29734             
29735             var bottomText = new Roo.bootstrap.Element({
29736                 tag : 'span',
29737                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29738             });
29739             
29740             topText.onRender(top.el, null);
29741             bottomText.onRender(bottom.el, null);
29742             
29743             item.topEl = top;
29744             item.bottomEl = bottom;
29745         }
29746         
29747         this.barItems.push(item);
29748         
29749         return item;
29750     },
29751     
29752     getActive : function()
29753     {
29754         var active = false;
29755         
29756         Roo.each(this.barItems, function(v){
29757             
29758             if (!v.isActive()) {
29759                 return;
29760             }
29761             
29762             active = v;
29763             return false;
29764             
29765         });
29766         
29767         return active;
29768     },
29769     
29770     setActiveItem : function(item)
29771     {
29772         var prev = false;
29773         
29774         Roo.each(this.barItems, function(v){
29775             if (v.rid == item.rid) {
29776                 return ;
29777             }
29778             
29779             if (v.isActive()) {
29780                 v.setActive(false);
29781                 prev = v;
29782             }
29783         });
29784
29785         item.setActive(true);
29786         
29787         this.fireEvent('changed', this, item, prev);
29788     },
29789     
29790     getBarItem: function(rid)
29791     {
29792         var ret = false;
29793         
29794         Roo.each(this.barItems, function(e) {
29795             if (e.rid != rid) {
29796                 return;
29797             }
29798             
29799             ret =  e;
29800             return false;
29801         });
29802         
29803         return ret;
29804     },
29805     
29806     indexOfItem : function(item)
29807     {
29808         var index = false;
29809         
29810         Roo.each(this.barItems, function(v, i){
29811             
29812             if (v.rid != item.rid) {
29813                 return;
29814             }
29815             
29816             index = i;
29817             return false
29818         });
29819         
29820         return index;
29821     },
29822     
29823     setActiveNext : function()
29824     {
29825         var i = this.indexOfItem(this.getActive());
29826         
29827         if (i > this.barItems.length) {
29828             return;
29829         }
29830         
29831         this.setActiveItem(this.barItems[i+1]);
29832     },
29833     
29834     setActivePrev : function()
29835     {
29836         var i = this.indexOfItem(this.getActive());
29837         
29838         if (i  < 1) {
29839             return;
29840         }
29841         
29842         this.setActiveItem(this.barItems[i-1]);
29843     },
29844     
29845     format : function()
29846     {
29847         if(!this.barItems.length){
29848             return;
29849         }
29850      
29851         var width = 100 / this.barItems.length;
29852         
29853         Roo.each(this.barItems, function(i){
29854             i.el.setStyle('width', width + '%');
29855             i.topEl.el.setStyle('width', width + '%');
29856             i.bottomEl.el.setStyle('width', width + '%');
29857         }, this);
29858         
29859     }
29860     
29861 });
29862 /*
29863  * - LGPL
29864  *
29865  * Nav Progress Item
29866  * 
29867  */
29868
29869 /**
29870  * @class Roo.bootstrap.NavProgressItem
29871  * @extends Roo.bootstrap.Component
29872  * Bootstrap NavProgressItem class
29873  * @cfg {String} rid the reference id
29874  * @cfg {Boolean} active (true|false) Is item active default false
29875  * @cfg {Boolean} disabled (true|false) Is item active default false
29876  * @cfg {String} html
29877  * @cfg {String} position (top|bottom) text position default bottom
29878  * @cfg {String} icon show icon instead of number
29879  * 
29880  * @constructor
29881  * Create a new NavProgressItem
29882  * @param {Object} config The config object
29883  */
29884 Roo.bootstrap.NavProgressItem = function(config){
29885     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29886     this.addEvents({
29887         // raw events
29888         /**
29889          * @event click
29890          * The raw click event for the entire grid.
29891          * @param {Roo.bootstrap.NavProgressItem} this
29892          * @param {Roo.EventObject} e
29893          */
29894         "click" : true
29895     });
29896    
29897 };
29898
29899 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29900     
29901     rid : '',
29902     active : false,
29903     disabled : false,
29904     html : '',
29905     position : 'bottom',
29906     icon : false,
29907     
29908     getAutoCreate : function()
29909     {
29910         var iconCls = 'roo-navigation-bar-item-icon';
29911         
29912         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29913         
29914         var cfg = {
29915             tag: 'li',
29916             cls: 'roo-navigation-bar-item',
29917             cn : [
29918                 {
29919                     tag : 'i',
29920                     cls : iconCls
29921                 }
29922             ]
29923         };
29924         
29925         if(this.active){
29926             cfg.cls += ' active';
29927         }
29928         if(this.disabled){
29929             cfg.cls += ' disabled';
29930         }
29931         
29932         return cfg;
29933     },
29934     
29935     disable : function()
29936     {
29937         this.setDisabled(true);
29938     },
29939     
29940     enable : function()
29941     {
29942         this.setDisabled(false);
29943     },
29944     
29945     initEvents: function() 
29946     {
29947         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29948         
29949         this.iconEl.on('click', this.onClick, this);
29950     },
29951     
29952     onClick : function(e)
29953     {
29954         e.preventDefault();
29955         
29956         if(this.disabled){
29957             return;
29958         }
29959         
29960         if(this.fireEvent('click', this, e) === false){
29961             return;
29962         };
29963         
29964         this.parent().setActiveItem(this);
29965     },
29966     
29967     isActive: function () 
29968     {
29969         return this.active;
29970     },
29971     
29972     setActive : function(state)
29973     {
29974         if(this.active == state){
29975             return;
29976         }
29977         
29978         this.active = state;
29979         
29980         if (state) {
29981             this.el.addClass('active');
29982             return;
29983         }
29984         
29985         this.el.removeClass('active');
29986         
29987         return;
29988     },
29989     
29990     setDisabled : function(state)
29991     {
29992         if(this.disabled == state){
29993             return;
29994         }
29995         
29996         this.disabled = state;
29997         
29998         if (state) {
29999             this.el.addClass('disabled');
30000             return;
30001         }
30002         
30003         this.el.removeClass('disabled');
30004     },
30005     
30006     tooltipEl : function()
30007     {
30008         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30009     }
30010 });
30011  
30012
30013  /*
30014  * - LGPL
30015  *
30016  * FieldLabel
30017  * 
30018  */
30019
30020 /**
30021  * @class Roo.bootstrap.FieldLabel
30022  * @extends Roo.bootstrap.Component
30023  * Bootstrap FieldLabel class
30024  * @cfg {String} html contents of the element
30025  * @cfg {String} tag tag of the element default label
30026  * @cfg {String} cls class of the element
30027  * @cfg {String} target label target 
30028  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30029  * @cfg {String} invalidClass default "text-warning"
30030  * @cfg {String} validClass default "text-success"
30031  * @cfg {String} iconTooltip default "This field is required"
30032  * @cfg {String} indicatorpos (left|right) default left
30033  * 
30034  * @constructor
30035  * Create a new FieldLabel
30036  * @param {Object} config The config object
30037  */
30038
30039 Roo.bootstrap.FieldLabel = function(config){
30040     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30041     
30042     this.addEvents({
30043             /**
30044              * @event invalid
30045              * Fires after the field has been marked as invalid.
30046              * @param {Roo.form.FieldLabel} this
30047              * @param {String} msg The validation message
30048              */
30049             invalid : true,
30050             /**
30051              * @event valid
30052              * Fires after the field has been validated with no errors.
30053              * @param {Roo.form.FieldLabel} this
30054              */
30055             valid : true
30056         });
30057 };
30058
30059 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30060     
30061     tag: 'label',
30062     cls: '',
30063     html: '',
30064     target: '',
30065     allowBlank : true,
30066     invalidClass : 'has-warning',
30067     validClass : 'has-success',
30068     iconTooltip : 'This field is required',
30069     indicatorpos : 'left',
30070     
30071     getAutoCreate : function(){
30072         
30073         var cls = "";
30074         if (!this.allowBlank) {
30075             cls  = "visible";
30076         }
30077         
30078         var cfg = {
30079             tag : this.tag,
30080             cls : 'roo-bootstrap-field-label ' + this.cls,
30081             for : this.target,
30082             cn : [
30083                 {
30084                     tag : 'i',
30085                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30086                     tooltip : this.iconTooltip
30087                 },
30088                 {
30089                     tag : 'span',
30090                     html : this.html
30091                 }
30092             ] 
30093         };
30094         
30095         if(this.indicatorpos == 'right'){
30096             var cfg = {
30097                 tag : this.tag,
30098                 cls : 'roo-bootstrap-field-label ' + this.cls,
30099                 for : this.target,
30100                 cn : [
30101                     {
30102                         tag : 'span',
30103                         html : this.html
30104                     },
30105                     {
30106                         tag : 'i',
30107                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30108                         tooltip : this.iconTooltip
30109                     }
30110                 ] 
30111             };
30112         }
30113         
30114         return cfg;
30115     },
30116     
30117     initEvents: function() 
30118     {
30119         Roo.bootstrap.Element.superclass.initEvents.call(this);
30120         
30121         this.indicator = this.indicatorEl();
30122         
30123         if(this.indicator){
30124             this.indicator.removeClass('visible');
30125             this.indicator.addClass('invisible');
30126         }
30127         
30128         Roo.bootstrap.FieldLabel.register(this);
30129     },
30130     
30131     indicatorEl : function()
30132     {
30133         var indicator = this.el.select('i.roo-required-indicator',true).first();
30134         
30135         if(!indicator){
30136             return false;
30137         }
30138         
30139         return indicator;
30140         
30141     },
30142     
30143     /**
30144      * Mark this field as valid
30145      */
30146     markValid : function()
30147     {
30148         if(this.indicator){
30149             this.indicator.removeClass('visible');
30150             this.indicator.addClass('invisible');
30151         }
30152         
30153         this.el.removeClass(this.invalidClass);
30154         
30155         this.el.addClass(this.validClass);
30156         
30157         this.fireEvent('valid', this);
30158     },
30159     
30160     /**
30161      * Mark this field as invalid
30162      * @param {String} msg The validation message
30163      */
30164     markInvalid : function(msg)
30165     {
30166         if(this.indicator){
30167             this.indicator.removeClass('invisible');
30168             this.indicator.addClass('visible');
30169         }
30170         
30171         this.el.removeClass(this.validClass);
30172         
30173         this.el.addClass(this.invalidClass);
30174         
30175         this.fireEvent('invalid', this, msg);
30176     }
30177     
30178    
30179 });
30180
30181 Roo.apply(Roo.bootstrap.FieldLabel, {
30182     
30183     groups: {},
30184     
30185      /**
30186     * register a FieldLabel Group
30187     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30188     */
30189     register : function(label)
30190     {
30191         if(this.groups.hasOwnProperty(label.target)){
30192             return;
30193         }
30194      
30195         this.groups[label.target] = label;
30196         
30197     },
30198     /**
30199     * fetch a FieldLabel Group based on the target
30200     * @param {string} target
30201     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30202     */
30203     get: function(target) {
30204         if (typeof(this.groups[target]) == 'undefined') {
30205             return false;
30206         }
30207         
30208         return this.groups[target] ;
30209     }
30210 });
30211
30212  
30213
30214  /*
30215  * - LGPL
30216  *
30217  * page DateSplitField.
30218  * 
30219  */
30220
30221
30222 /**
30223  * @class Roo.bootstrap.DateSplitField
30224  * @extends Roo.bootstrap.Component
30225  * Bootstrap DateSplitField class
30226  * @cfg {string} fieldLabel - the label associated
30227  * @cfg {Number} labelWidth set the width of label (0-12)
30228  * @cfg {String} labelAlign (top|left)
30229  * @cfg {Boolean} dayAllowBlank (true|false) default false
30230  * @cfg {Boolean} monthAllowBlank (true|false) default false
30231  * @cfg {Boolean} yearAllowBlank (true|false) default false
30232  * @cfg {string} dayPlaceholder 
30233  * @cfg {string} monthPlaceholder
30234  * @cfg {string} yearPlaceholder
30235  * @cfg {string} dayFormat default 'd'
30236  * @cfg {string} monthFormat default 'm'
30237  * @cfg {string} yearFormat default 'Y'
30238  * @cfg {Number} labellg set the width of label (1-12)
30239  * @cfg {Number} labelmd set the width of label (1-12)
30240  * @cfg {Number} labelsm set the width of label (1-12)
30241  * @cfg {Number} labelxs set the width of label (1-12)
30242
30243  *     
30244  * @constructor
30245  * Create a new DateSplitField
30246  * @param {Object} config The config object
30247  */
30248
30249 Roo.bootstrap.DateSplitField = function(config){
30250     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30251     
30252     this.addEvents({
30253         // raw events
30254          /**
30255          * @event years
30256          * getting the data of years
30257          * @param {Roo.bootstrap.DateSplitField} this
30258          * @param {Object} years
30259          */
30260         "years" : true,
30261         /**
30262          * @event days
30263          * getting the data of days
30264          * @param {Roo.bootstrap.DateSplitField} this
30265          * @param {Object} days
30266          */
30267         "days" : true,
30268         /**
30269          * @event invalid
30270          * Fires after the field has been marked as invalid.
30271          * @param {Roo.form.Field} this
30272          * @param {String} msg The validation message
30273          */
30274         invalid : true,
30275        /**
30276          * @event valid
30277          * Fires after the field has been validated with no errors.
30278          * @param {Roo.form.Field} this
30279          */
30280         valid : true
30281     });
30282 };
30283
30284 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30285     
30286     fieldLabel : '',
30287     labelAlign : 'top',
30288     labelWidth : 3,
30289     dayAllowBlank : false,
30290     monthAllowBlank : false,
30291     yearAllowBlank : false,
30292     dayPlaceholder : '',
30293     monthPlaceholder : '',
30294     yearPlaceholder : '',
30295     dayFormat : 'd',
30296     monthFormat : 'm',
30297     yearFormat : 'Y',
30298     isFormField : true,
30299     labellg : 0,
30300     labelmd : 0,
30301     labelsm : 0,
30302     labelxs : 0,
30303     
30304     getAutoCreate : function()
30305     {
30306         var cfg = {
30307             tag : 'div',
30308             cls : 'row roo-date-split-field-group',
30309             cn : [
30310                 {
30311                     tag : 'input',
30312                     type : 'hidden',
30313                     cls : 'form-hidden-field roo-date-split-field-group-value',
30314                     name : this.name
30315                 }
30316             ]
30317         };
30318         
30319         var labelCls = 'col-md-12';
30320         var contentCls = 'col-md-4';
30321         
30322         if(this.fieldLabel){
30323             
30324             var label = {
30325                 tag : 'div',
30326                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30327                 cn : [
30328                     {
30329                         tag : 'label',
30330                         html : this.fieldLabel
30331                     }
30332                 ]
30333             };
30334             
30335             if(this.labelAlign == 'left'){
30336             
30337                 if(this.labelWidth > 12){
30338                     label.style = "width: " + this.labelWidth + 'px';
30339                 }
30340
30341                 if(this.labelWidth < 13 && this.labelmd == 0){
30342                     this.labelmd = this.labelWidth;
30343                 }
30344
30345                 if(this.labellg > 0){
30346                     labelCls = ' col-lg-' + this.labellg;
30347                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30348                 }
30349
30350                 if(this.labelmd > 0){
30351                     labelCls = ' col-md-' + this.labelmd;
30352                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30353                 }
30354
30355                 if(this.labelsm > 0){
30356                     labelCls = ' col-sm-' + this.labelsm;
30357                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30358                 }
30359
30360                 if(this.labelxs > 0){
30361                     labelCls = ' col-xs-' + this.labelxs;
30362                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30363                 }
30364             }
30365             
30366             label.cls += ' ' + labelCls;
30367             
30368             cfg.cn.push(label);
30369         }
30370         
30371         Roo.each(['day', 'month', 'year'], function(t){
30372             cfg.cn.push({
30373                 tag : 'div',
30374                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30375             });
30376         }, this);
30377         
30378         return cfg;
30379     },
30380     
30381     inputEl: function ()
30382     {
30383         return this.el.select('.roo-date-split-field-group-value', true).first();
30384     },
30385     
30386     onRender : function(ct, position) 
30387     {
30388         var _this = this;
30389         
30390         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30391         
30392         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30393         
30394         this.dayField = new Roo.bootstrap.ComboBox({
30395             allowBlank : this.dayAllowBlank,
30396             alwaysQuery : true,
30397             displayField : 'value',
30398             editable : false,
30399             fieldLabel : '',
30400             forceSelection : true,
30401             mode : 'local',
30402             placeholder : this.dayPlaceholder,
30403             selectOnFocus : true,
30404             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30405             triggerAction : 'all',
30406             typeAhead : true,
30407             valueField : 'value',
30408             store : new Roo.data.SimpleStore({
30409                 data : (function() {    
30410                     var days = [];
30411                     _this.fireEvent('days', _this, days);
30412                     return days;
30413                 })(),
30414                 fields : [ 'value' ]
30415             }),
30416             listeners : {
30417                 select : function (_self, record, index)
30418                 {
30419                     _this.setValue(_this.getValue());
30420                 }
30421             }
30422         });
30423
30424         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30425         
30426         this.monthField = new Roo.bootstrap.MonthField({
30427             after : '<i class=\"fa fa-calendar\"></i>',
30428             allowBlank : this.monthAllowBlank,
30429             placeholder : this.monthPlaceholder,
30430             readOnly : true,
30431             listeners : {
30432                 render : function (_self)
30433                 {
30434                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30435                         e.preventDefault();
30436                         _self.focus();
30437                     });
30438                 },
30439                 select : function (_self, oldvalue, newvalue)
30440                 {
30441                     _this.setValue(_this.getValue());
30442                 }
30443             }
30444         });
30445         
30446         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30447         
30448         this.yearField = new Roo.bootstrap.ComboBox({
30449             allowBlank : this.yearAllowBlank,
30450             alwaysQuery : true,
30451             displayField : 'value',
30452             editable : false,
30453             fieldLabel : '',
30454             forceSelection : true,
30455             mode : 'local',
30456             placeholder : this.yearPlaceholder,
30457             selectOnFocus : true,
30458             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30459             triggerAction : 'all',
30460             typeAhead : true,
30461             valueField : 'value',
30462             store : new Roo.data.SimpleStore({
30463                 data : (function() {
30464                     var years = [];
30465                     _this.fireEvent('years', _this, years);
30466                     return years;
30467                 })(),
30468                 fields : [ 'value' ]
30469             }),
30470             listeners : {
30471                 select : function (_self, record, index)
30472                 {
30473                     _this.setValue(_this.getValue());
30474                 }
30475             }
30476         });
30477
30478         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30479     },
30480     
30481     setValue : function(v, format)
30482     {
30483         this.inputEl.dom.value = v;
30484         
30485         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30486         
30487         var d = Date.parseDate(v, f);
30488         
30489         if(!d){
30490             this.validate();
30491             return;
30492         }
30493         
30494         this.setDay(d.format(this.dayFormat));
30495         this.setMonth(d.format(this.monthFormat));
30496         this.setYear(d.format(this.yearFormat));
30497         
30498         this.validate();
30499         
30500         return;
30501     },
30502     
30503     setDay : function(v)
30504     {
30505         this.dayField.setValue(v);
30506         this.inputEl.dom.value = this.getValue();
30507         this.validate();
30508         return;
30509     },
30510     
30511     setMonth : function(v)
30512     {
30513         this.monthField.setValue(v, true);
30514         this.inputEl.dom.value = this.getValue();
30515         this.validate();
30516         return;
30517     },
30518     
30519     setYear : function(v)
30520     {
30521         this.yearField.setValue(v);
30522         this.inputEl.dom.value = this.getValue();
30523         this.validate();
30524         return;
30525     },
30526     
30527     getDay : function()
30528     {
30529         return this.dayField.getValue();
30530     },
30531     
30532     getMonth : function()
30533     {
30534         return this.monthField.getValue();
30535     },
30536     
30537     getYear : function()
30538     {
30539         return this.yearField.getValue();
30540     },
30541     
30542     getValue : function()
30543     {
30544         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30545         
30546         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30547         
30548         return date;
30549     },
30550     
30551     reset : function()
30552     {
30553         this.setDay('');
30554         this.setMonth('');
30555         this.setYear('');
30556         this.inputEl.dom.value = '';
30557         this.validate();
30558         return;
30559     },
30560     
30561     validate : function()
30562     {
30563         var d = this.dayField.validate();
30564         var m = this.monthField.validate();
30565         var y = this.yearField.validate();
30566         
30567         var valid = true;
30568         
30569         if(
30570                 (!this.dayAllowBlank && !d) ||
30571                 (!this.monthAllowBlank && !m) ||
30572                 (!this.yearAllowBlank && !y)
30573         ){
30574             valid = false;
30575         }
30576         
30577         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30578             return valid;
30579         }
30580         
30581         if(valid){
30582             this.markValid();
30583             return valid;
30584         }
30585         
30586         this.markInvalid();
30587         
30588         return valid;
30589     },
30590     
30591     markValid : function()
30592     {
30593         
30594         var label = this.el.select('label', true).first();
30595         var icon = this.el.select('i.fa-star', true).first();
30596
30597         if(label && icon){
30598             icon.remove();
30599         }
30600         
30601         this.fireEvent('valid', this);
30602     },
30603     
30604      /**
30605      * Mark this field as invalid
30606      * @param {String} msg The validation message
30607      */
30608     markInvalid : function(msg)
30609     {
30610         
30611         var label = this.el.select('label', true).first();
30612         var icon = this.el.select('i.fa-star', true).first();
30613
30614         if(label && !icon){
30615             this.el.select('.roo-date-split-field-label', true).createChild({
30616                 tag : 'i',
30617                 cls : 'text-danger fa fa-lg fa-star',
30618                 tooltip : 'This field is required',
30619                 style : 'margin-right:5px;'
30620             }, label, true);
30621         }
30622         
30623         this.fireEvent('invalid', this, msg);
30624     },
30625     
30626     clearInvalid : function()
30627     {
30628         var label = this.el.select('label', true).first();
30629         var icon = this.el.select('i.fa-star', true).first();
30630
30631         if(label && icon){
30632             icon.remove();
30633         }
30634         
30635         this.fireEvent('valid', this);
30636     },
30637     
30638     getName: function()
30639     {
30640         return this.name;
30641     }
30642     
30643 });
30644
30645  /**
30646  *
30647  * This is based on 
30648  * http://masonry.desandro.com
30649  *
30650  * The idea is to render all the bricks based on vertical width...
30651  *
30652  * The original code extends 'outlayer' - we might need to use that....
30653  * 
30654  */
30655
30656
30657 /**
30658  * @class Roo.bootstrap.LayoutMasonry
30659  * @extends Roo.bootstrap.Component
30660  * Bootstrap Layout Masonry class
30661  * 
30662  * @constructor
30663  * Create a new Element
30664  * @param {Object} config The config object
30665  */
30666
30667 Roo.bootstrap.LayoutMasonry = function(config){
30668     
30669     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30670     
30671     this.bricks = [];
30672     
30673     Roo.bootstrap.LayoutMasonry.register(this);
30674     
30675     this.addEvents({
30676         // raw events
30677         /**
30678          * @event layout
30679          * Fire after layout the items
30680          * @param {Roo.bootstrap.LayoutMasonry} this
30681          * @param {Roo.EventObject} e
30682          */
30683         "layout" : true
30684     });
30685     
30686 };
30687
30688 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30689     
30690     /**
30691      * @cfg {Boolean} isLayoutInstant = no animation?
30692      */   
30693     isLayoutInstant : false, // needed?
30694    
30695     /**
30696      * @cfg {Number} boxWidth  width of the columns
30697      */   
30698     boxWidth : 450,
30699     
30700       /**
30701      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30702      */   
30703     boxHeight : 0,
30704     
30705     /**
30706      * @cfg {Number} padWidth padding below box..
30707      */   
30708     padWidth : 10, 
30709     
30710     /**
30711      * @cfg {Number} gutter gutter width..
30712      */   
30713     gutter : 10,
30714     
30715      /**
30716      * @cfg {Number} maxCols maximum number of columns
30717      */   
30718     
30719     maxCols: 0,
30720     
30721     /**
30722      * @cfg {Boolean} isAutoInitial defalut true
30723      */   
30724     isAutoInitial : true, 
30725     
30726     containerWidth: 0,
30727     
30728     /**
30729      * @cfg {Boolean} isHorizontal defalut false
30730      */   
30731     isHorizontal : false, 
30732
30733     currentSize : null,
30734     
30735     tag: 'div',
30736     
30737     cls: '',
30738     
30739     bricks: null, //CompositeElement
30740     
30741     cols : 1,
30742     
30743     _isLayoutInited : false,
30744     
30745 //    isAlternative : false, // only use for vertical layout...
30746     
30747     /**
30748      * @cfg {Number} alternativePadWidth padding below box..
30749      */   
30750     alternativePadWidth : 50,
30751     
30752     selectedBrick : [],
30753     
30754     getAutoCreate : function(){
30755         
30756         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30757         
30758         var cfg = {
30759             tag: this.tag,
30760             cls: 'blog-masonary-wrapper ' + this.cls,
30761             cn : {
30762                 cls : 'mas-boxes masonary'
30763             }
30764         };
30765         
30766         return cfg;
30767     },
30768     
30769     getChildContainer: function( )
30770     {
30771         if (this.boxesEl) {
30772             return this.boxesEl;
30773         }
30774         
30775         this.boxesEl = this.el.select('.mas-boxes').first();
30776         
30777         return this.boxesEl;
30778     },
30779     
30780     
30781     initEvents : function()
30782     {
30783         var _this = this;
30784         
30785         if(this.isAutoInitial){
30786             Roo.log('hook children rendered');
30787             this.on('childrenrendered', function() {
30788                 Roo.log('children rendered');
30789                 _this.initial();
30790             } ,this);
30791         }
30792     },
30793     
30794     initial : function()
30795     {
30796         this.selectedBrick = [];
30797         
30798         this.currentSize = this.el.getBox(true);
30799         
30800         Roo.EventManager.onWindowResize(this.resize, this); 
30801
30802         if(!this.isAutoInitial){
30803             this.layout();
30804             return;
30805         }
30806         
30807         this.layout();
30808         
30809         return;
30810         //this.layout.defer(500,this);
30811         
30812     },
30813     
30814     resize : function()
30815     {
30816         var cs = this.el.getBox(true);
30817         
30818         if (
30819                 this.currentSize.width == cs.width && 
30820                 this.currentSize.x == cs.x && 
30821                 this.currentSize.height == cs.height && 
30822                 this.currentSize.y == cs.y 
30823         ) {
30824             Roo.log("no change in with or X or Y");
30825             return;
30826         }
30827         
30828         this.currentSize = cs;
30829         
30830         this.layout();
30831         
30832     },
30833     
30834     layout : function()
30835     {   
30836         this._resetLayout();
30837         
30838         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30839         
30840         this.layoutItems( isInstant );
30841       
30842         this._isLayoutInited = true;
30843         
30844         this.fireEvent('layout', this);
30845         
30846     },
30847     
30848     _resetLayout : function()
30849     {
30850         if(this.isHorizontal){
30851             this.horizontalMeasureColumns();
30852             return;
30853         }
30854         
30855         this.verticalMeasureColumns();
30856         
30857     },
30858     
30859     verticalMeasureColumns : function()
30860     {
30861         this.getContainerWidth();
30862         
30863 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30864 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30865 //            return;
30866 //        }
30867         
30868         var boxWidth = this.boxWidth + this.padWidth;
30869         
30870         if(this.containerWidth < this.boxWidth){
30871             boxWidth = this.containerWidth
30872         }
30873         
30874         var containerWidth = this.containerWidth;
30875         
30876         var cols = Math.floor(containerWidth / boxWidth);
30877         
30878         this.cols = Math.max( cols, 1 );
30879         
30880         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30881         
30882         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30883         
30884         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30885         
30886         this.colWidth = boxWidth + avail - this.padWidth;
30887         
30888         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30889         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30890     },
30891     
30892     horizontalMeasureColumns : function()
30893     {
30894         this.getContainerWidth();
30895         
30896         var boxWidth = this.boxWidth;
30897         
30898         if(this.containerWidth < boxWidth){
30899             boxWidth = this.containerWidth;
30900         }
30901         
30902         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30903         
30904         this.el.setHeight(boxWidth);
30905         
30906     },
30907     
30908     getContainerWidth : function()
30909     {
30910         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30911     },
30912     
30913     layoutItems : function( isInstant )
30914     {
30915         Roo.log(this.bricks);
30916         
30917         var items = Roo.apply([], this.bricks);
30918         
30919         if(this.isHorizontal){
30920             this._horizontalLayoutItems( items , isInstant );
30921             return;
30922         }
30923         
30924 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30925 //            this._verticalAlternativeLayoutItems( items , isInstant );
30926 //            return;
30927 //        }
30928         
30929         this._verticalLayoutItems( items , isInstant );
30930         
30931     },
30932     
30933     _verticalLayoutItems : function ( items , isInstant)
30934     {
30935         if ( !items || !items.length ) {
30936             return;
30937         }
30938         
30939         var standard = [
30940             ['xs', 'xs', 'xs', 'tall'],
30941             ['xs', 'xs', 'tall'],
30942             ['xs', 'xs', 'sm'],
30943             ['xs', 'xs', 'xs'],
30944             ['xs', 'tall'],
30945             ['xs', 'sm'],
30946             ['xs', 'xs'],
30947             ['xs'],
30948             
30949             ['sm', 'xs', 'xs'],
30950             ['sm', 'xs'],
30951             ['sm'],
30952             
30953             ['tall', 'xs', 'xs', 'xs'],
30954             ['tall', 'xs', 'xs'],
30955             ['tall', 'xs'],
30956             ['tall']
30957             
30958         ];
30959         
30960         var queue = [];
30961         
30962         var boxes = [];
30963         
30964         var box = [];
30965         
30966         Roo.each(items, function(item, k){
30967             
30968             switch (item.size) {
30969                 // these layouts take up a full box,
30970                 case 'md' :
30971                 case 'md-left' :
30972                 case 'md-right' :
30973                 case 'wide' :
30974                     
30975                     if(box.length){
30976                         boxes.push(box);
30977                         box = [];
30978                     }
30979                     
30980                     boxes.push([item]);
30981                     
30982                     break;
30983                     
30984                 case 'xs' :
30985                 case 'sm' :
30986                 case 'tall' :
30987                     
30988                     box.push(item);
30989                     
30990                     break;
30991                 default :
30992                     break;
30993                     
30994             }
30995             
30996         }, this);
30997         
30998         if(box.length){
30999             boxes.push(box);
31000             box = [];
31001         }
31002         
31003         var filterPattern = function(box, length)
31004         {
31005             if(!box.length){
31006                 return;
31007             }
31008             
31009             var match = false;
31010             
31011             var pattern = box.slice(0, length);
31012             
31013             var format = [];
31014             
31015             Roo.each(pattern, function(i){
31016                 format.push(i.size);
31017             }, this);
31018             
31019             Roo.each(standard, function(s){
31020                 
31021                 if(String(s) != String(format)){
31022                     return;
31023                 }
31024                 
31025                 match = true;
31026                 return false;
31027                 
31028             }, this);
31029             
31030             if(!match && length == 1){
31031                 return;
31032             }
31033             
31034             if(!match){
31035                 filterPattern(box, length - 1);
31036                 return;
31037             }
31038                 
31039             queue.push(pattern);
31040
31041             box = box.slice(length, box.length);
31042
31043             filterPattern(box, 4);
31044
31045             return;
31046             
31047         }
31048         
31049         Roo.each(boxes, function(box, k){
31050             
31051             if(!box.length){
31052                 return;
31053             }
31054             
31055             if(box.length == 1){
31056                 queue.push(box);
31057                 return;
31058             }
31059             
31060             filterPattern(box, 4);
31061             
31062         }, this);
31063         
31064         this._processVerticalLayoutQueue( queue, isInstant );
31065         
31066     },
31067     
31068 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31069 //    {
31070 //        if ( !items || !items.length ) {
31071 //            return;
31072 //        }
31073 //
31074 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31075 //        
31076 //    },
31077     
31078     _horizontalLayoutItems : function ( items , isInstant)
31079     {
31080         if ( !items || !items.length || items.length < 3) {
31081             return;
31082         }
31083         
31084         items.reverse();
31085         
31086         var eItems = items.slice(0, 3);
31087         
31088         items = items.slice(3, items.length);
31089         
31090         var standard = [
31091             ['xs', 'xs', 'xs', 'wide'],
31092             ['xs', 'xs', 'wide'],
31093             ['xs', 'xs', 'sm'],
31094             ['xs', 'xs', 'xs'],
31095             ['xs', 'wide'],
31096             ['xs', 'sm'],
31097             ['xs', 'xs'],
31098             ['xs'],
31099             
31100             ['sm', 'xs', 'xs'],
31101             ['sm', 'xs'],
31102             ['sm'],
31103             
31104             ['wide', 'xs', 'xs', 'xs'],
31105             ['wide', 'xs', 'xs'],
31106             ['wide', 'xs'],
31107             ['wide'],
31108             
31109             ['wide-thin']
31110         ];
31111         
31112         var queue = [];
31113         
31114         var boxes = [];
31115         
31116         var box = [];
31117         
31118         Roo.each(items, function(item, k){
31119             
31120             switch (item.size) {
31121                 case 'md' :
31122                 case 'md-left' :
31123                 case 'md-right' :
31124                 case 'tall' :
31125                     
31126                     if(box.length){
31127                         boxes.push(box);
31128                         box = [];
31129                     }
31130                     
31131                     boxes.push([item]);
31132                     
31133                     break;
31134                     
31135                 case 'xs' :
31136                 case 'sm' :
31137                 case 'wide' :
31138                 case 'wide-thin' :
31139                     
31140                     box.push(item);
31141                     
31142                     break;
31143                 default :
31144                     break;
31145                     
31146             }
31147             
31148         }, this);
31149         
31150         if(box.length){
31151             boxes.push(box);
31152             box = [];
31153         }
31154         
31155         var filterPattern = function(box, length)
31156         {
31157             if(!box.length){
31158                 return;
31159             }
31160             
31161             var match = false;
31162             
31163             var pattern = box.slice(0, length);
31164             
31165             var format = [];
31166             
31167             Roo.each(pattern, function(i){
31168                 format.push(i.size);
31169             }, this);
31170             
31171             Roo.each(standard, function(s){
31172                 
31173                 if(String(s) != String(format)){
31174                     return;
31175                 }
31176                 
31177                 match = true;
31178                 return false;
31179                 
31180             }, this);
31181             
31182             if(!match && length == 1){
31183                 return;
31184             }
31185             
31186             if(!match){
31187                 filterPattern(box, length - 1);
31188                 return;
31189             }
31190                 
31191             queue.push(pattern);
31192
31193             box = box.slice(length, box.length);
31194
31195             filterPattern(box, 4);
31196
31197             return;
31198             
31199         }
31200         
31201         Roo.each(boxes, function(box, k){
31202             
31203             if(!box.length){
31204                 return;
31205             }
31206             
31207             if(box.length == 1){
31208                 queue.push(box);
31209                 return;
31210             }
31211             
31212             filterPattern(box, 4);
31213             
31214         }, this);
31215         
31216         
31217         var prune = [];
31218         
31219         var pos = this.el.getBox(true);
31220         
31221         var minX = pos.x;
31222         
31223         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31224         
31225         var hit_end = false;
31226         
31227         Roo.each(queue, function(box){
31228             
31229             if(hit_end){
31230                 
31231                 Roo.each(box, function(b){
31232                 
31233                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31234                     b.el.hide();
31235
31236                 }, this);
31237
31238                 return;
31239             }
31240             
31241             var mx = 0;
31242             
31243             Roo.each(box, function(b){
31244                 
31245                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31246                 b.el.show();
31247
31248                 mx = Math.max(mx, b.x);
31249                 
31250             }, this);
31251             
31252             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31253             
31254             if(maxX < minX){
31255                 
31256                 Roo.each(box, function(b){
31257                 
31258                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31259                     b.el.hide();
31260                     
31261                 }, this);
31262                 
31263                 hit_end = true;
31264                 
31265                 return;
31266             }
31267             
31268             prune.push(box);
31269             
31270         }, this);
31271         
31272         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31273     },
31274     
31275     /** Sets position of item in DOM
31276     * @param {Element} item
31277     * @param {Number} x - horizontal position
31278     * @param {Number} y - vertical position
31279     * @param {Boolean} isInstant - disables transitions
31280     */
31281     _processVerticalLayoutQueue : function( queue, isInstant )
31282     {
31283         var pos = this.el.getBox(true);
31284         var x = pos.x;
31285         var y = pos.y;
31286         var maxY = [];
31287         
31288         for (var i = 0; i < this.cols; i++){
31289             maxY[i] = pos.y;
31290         }
31291         
31292         Roo.each(queue, function(box, k){
31293             
31294             var col = k % this.cols;
31295             
31296             Roo.each(box, function(b,kk){
31297                 
31298                 b.el.position('absolute');
31299                 
31300                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31301                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31302                 
31303                 if(b.size == 'md-left' || b.size == 'md-right'){
31304                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31305                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31306                 }
31307                 
31308                 b.el.setWidth(width);
31309                 b.el.setHeight(height);
31310                 // iframe?
31311                 b.el.select('iframe',true).setSize(width,height);
31312                 
31313             }, this);
31314             
31315             for (var i = 0; i < this.cols; i++){
31316                 
31317                 if(maxY[i] < maxY[col]){
31318                     col = i;
31319                     continue;
31320                 }
31321                 
31322                 col = Math.min(col, i);
31323                 
31324             }
31325             
31326             x = pos.x + col * (this.colWidth + this.padWidth);
31327             
31328             y = maxY[col];
31329             
31330             var positions = [];
31331             
31332             switch (box.length){
31333                 case 1 :
31334                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31335                     break;
31336                 case 2 :
31337                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31338                     break;
31339                 case 3 :
31340                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31341                     break;
31342                 case 4 :
31343                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31344                     break;
31345                 default :
31346                     break;
31347             }
31348             
31349             Roo.each(box, function(b,kk){
31350                 
31351                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31352                 
31353                 var sz = b.el.getSize();
31354                 
31355                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31356                 
31357             }, this);
31358             
31359         }, this);
31360         
31361         var mY = 0;
31362         
31363         for (var i = 0; i < this.cols; i++){
31364             mY = Math.max(mY, maxY[i]);
31365         }
31366         
31367         this.el.setHeight(mY - pos.y);
31368         
31369     },
31370     
31371 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31372 //    {
31373 //        var pos = this.el.getBox(true);
31374 //        var x = pos.x;
31375 //        var y = pos.y;
31376 //        var maxX = pos.right;
31377 //        
31378 //        var maxHeight = 0;
31379 //        
31380 //        Roo.each(items, function(item, k){
31381 //            
31382 //            var c = k % 2;
31383 //            
31384 //            item.el.position('absolute');
31385 //                
31386 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31387 //
31388 //            item.el.setWidth(width);
31389 //
31390 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31391 //
31392 //            item.el.setHeight(height);
31393 //            
31394 //            if(c == 0){
31395 //                item.el.setXY([x, y], isInstant ? false : true);
31396 //            } else {
31397 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31398 //            }
31399 //            
31400 //            y = y + height + this.alternativePadWidth;
31401 //            
31402 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31403 //            
31404 //        }, this);
31405 //        
31406 //        this.el.setHeight(maxHeight);
31407 //        
31408 //    },
31409     
31410     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31411     {
31412         var pos = this.el.getBox(true);
31413         
31414         var minX = pos.x;
31415         var minY = pos.y;
31416         
31417         var maxX = pos.right;
31418         
31419         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31420         
31421         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31422         
31423         Roo.each(queue, function(box, k){
31424             
31425             Roo.each(box, function(b, kk){
31426                 
31427                 b.el.position('absolute');
31428                 
31429                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31430                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31431                 
31432                 if(b.size == 'md-left' || b.size == 'md-right'){
31433                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31434                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31435                 }
31436                 
31437                 b.el.setWidth(width);
31438                 b.el.setHeight(height);
31439                 
31440             }, this);
31441             
31442             if(!box.length){
31443                 return;
31444             }
31445             
31446             var positions = [];
31447             
31448             switch (box.length){
31449                 case 1 :
31450                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31451                     break;
31452                 case 2 :
31453                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31454                     break;
31455                 case 3 :
31456                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31457                     break;
31458                 case 4 :
31459                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31460                     break;
31461                 default :
31462                     break;
31463             }
31464             
31465             Roo.each(box, function(b,kk){
31466                 
31467                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31468                 
31469                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31470                 
31471             }, this);
31472             
31473         }, this);
31474         
31475     },
31476     
31477     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31478     {
31479         Roo.each(eItems, function(b,k){
31480             
31481             b.size = (k == 0) ? 'sm' : 'xs';
31482             b.x = (k == 0) ? 2 : 1;
31483             b.y = (k == 0) ? 2 : 1;
31484             
31485             b.el.position('absolute');
31486             
31487             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31488                 
31489             b.el.setWidth(width);
31490             
31491             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31492             
31493             b.el.setHeight(height);
31494             
31495         }, this);
31496
31497         var positions = [];
31498         
31499         positions.push({
31500             x : maxX - this.unitWidth * 2 - this.gutter,
31501             y : minY
31502         });
31503         
31504         positions.push({
31505             x : maxX - this.unitWidth,
31506             y : minY + (this.unitWidth + this.gutter) * 2
31507         });
31508         
31509         positions.push({
31510             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31511             y : minY
31512         });
31513         
31514         Roo.each(eItems, function(b,k){
31515             
31516             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31517
31518         }, this);
31519         
31520     },
31521     
31522     getVerticalOneBoxColPositions : function(x, y, box)
31523     {
31524         var pos = [];
31525         
31526         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31527         
31528         if(box[0].size == 'md-left'){
31529             rand = 0;
31530         }
31531         
31532         if(box[0].size == 'md-right'){
31533             rand = 1;
31534         }
31535         
31536         pos.push({
31537             x : x + (this.unitWidth + this.gutter) * rand,
31538             y : y
31539         });
31540         
31541         return pos;
31542     },
31543     
31544     getVerticalTwoBoxColPositions : function(x, y, box)
31545     {
31546         var pos = [];
31547         
31548         if(box[0].size == 'xs'){
31549             
31550             pos.push({
31551                 x : x,
31552                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31553             });
31554
31555             pos.push({
31556                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31557                 y : y
31558             });
31559             
31560             return pos;
31561             
31562         }
31563         
31564         pos.push({
31565             x : x,
31566             y : y
31567         });
31568
31569         pos.push({
31570             x : x + (this.unitWidth + this.gutter) * 2,
31571             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31572         });
31573         
31574         return pos;
31575         
31576     },
31577     
31578     getVerticalThreeBoxColPositions : function(x, y, box)
31579     {
31580         var pos = [];
31581         
31582         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31583             
31584             pos.push({
31585                 x : x,
31586                 y : y
31587             });
31588
31589             pos.push({
31590                 x : x + (this.unitWidth + this.gutter) * 1,
31591                 y : y
31592             });
31593             
31594             pos.push({
31595                 x : x + (this.unitWidth + this.gutter) * 2,
31596                 y : y
31597             });
31598             
31599             return pos;
31600             
31601         }
31602         
31603         if(box[0].size == 'xs' && box[1].size == 'xs'){
31604             
31605             pos.push({
31606                 x : x,
31607                 y : y
31608             });
31609
31610             pos.push({
31611                 x : x,
31612                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31613             });
31614             
31615             pos.push({
31616                 x : x + (this.unitWidth + this.gutter) * 1,
31617                 y : y
31618             });
31619             
31620             return pos;
31621             
31622         }
31623         
31624         pos.push({
31625             x : x,
31626             y : y
31627         });
31628
31629         pos.push({
31630             x : x + (this.unitWidth + this.gutter) * 2,
31631             y : y
31632         });
31633
31634         pos.push({
31635             x : x + (this.unitWidth + this.gutter) * 2,
31636             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31637         });
31638             
31639         return pos;
31640         
31641     },
31642     
31643     getVerticalFourBoxColPositions : function(x, y, box)
31644     {
31645         var pos = [];
31646         
31647         if(box[0].size == 'xs'){
31648             
31649             pos.push({
31650                 x : x,
31651                 y : y
31652             });
31653
31654             pos.push({
31655                 x : x,
31656                 y : y + (this.unitHeight + this.gutter) * 1
31657             });
31658             
31659             pos.push({
31660                 x : x,
31661                 y : y + (this.unitHeight + this.gutter) * 2
31662             });
31663             
31664             pos.push({
31665                 x : x + (this.unitWidth + this.gutter) * 1,
31666                 y : y
31667             });
31668             
31669             return pos;
31670             
31671         }
31672         
31673         pos.push({
31674             x : x,
31675             y : y
31676         });
31677
31678         pos.push({
31679             x : x + (this.unitWidth + this.gutter) * 2,
31680             y : y
31681         });
31682
31683         pos.push({
31684             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31685             y : y + (this.unitHeight + this.gutter) * 1
31686         });
31687
31688         pos.push({
31689             x : x + (this.unitWidth + this.gutter) * 2,
31690             y : y + (this.unitWidth + this.gutter) * 2
31691         });
31692
31693         return pos;
31694         
31695     },
31696     
31697     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31698     {
31699         var pos = [];
31700         
31701         if(box[0].size == 'md-left'){
31702             pos.push({
31703                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31704                 y : minY
31705             });
31706             
31707             return pos;
31708         }
31709         
31710         if(box[0].size == 'md-right'){
31711             pos.push({
31712                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31713                 y : minY + (this.unitWidth + this.gutter) * 1
31714             });
31715             
31716             return pos;
31717         }
31718         
31719         var rand = Math.floor(Math.random() * (4 - box[0].y));
31720         
31721         pos.push({
31722             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31723             y : minY + (this.unitWidth + this.gutter) * rand
31724         });
31725         
31726         return pos;
31727         
31728     },
31729     
31730     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31731     {
31732         var pos = [];
31733         
31734         if(box[0].size == 'xs'){
31735             
31736             pos.push({
31737                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31738                 y : minY
31739             });
31740
31741             pos.push({
31742                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31743                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31744             });
31745             
31746             return pos;
31747             
31748         }
31749         
31750         pos.push({
31751             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31752             y : minY
31753         });
31754
31755         pos.push({
31756             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31757             y : minY + (this.unitWidth + this.gutter) * 2
31758         });
31759         
31760         return pos;
31761         
31762     },
31763     
31764     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31765     {
31766         var pos = [];
31767         
31768         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31769             
31770             pos.push({
31771                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31772                 y : minY
31773             });
31774
31775             pos.push({
31776                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31777                 y : minY + (this.unitWidth + this.gutter) * 1
31778             });
31779             
31780             pos.push({
31781                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31782                 y : minY + (this.unitWidth + this.gutter) * 2
31783             });
31784             
31785             return pos;
31786             
31787         }
31788         
31789         if(box[0].size == 'xs' && box[1].size == 'xs'){
31790             
31791             pos.push({
31792                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31793                 y : minY
31794             });
31795
31796             pos.push({
31797                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31798                 y : minY
31799             });
31800             
31801             pos.push({
31802                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31803                 y : minY + (this.unitWidth + this.gutter) * 1
31804             });
31805             
31806             return pos;
31807             
31808         }
31809         
31810         pos.push({
31811             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31812             y : minY
31813         });
31814
31815         pos.push({
31816             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31817             y : minY + (this.unitWidth + this.gutter) * 2
31818         });
31819
31820         pos.push({
31821             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31822             y : minY + (this.unitWidth + this.gutter) * 2
31823         });
31824             
31825         return pos;
31826         
31827     },
31828     
31829     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31830     {
31831         var pos = [];
31832         
31833         if(box[0].size == 'xs'){
31834             
31835             pos.push({
31836                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31837                 y : minY
31838             });
31839
31840             pos.push({
31841                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31842                 y : minY
31843             });
31844             
31845             pos.push({
31846                 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),
31847                 y : minY
31848             });
31849             
31850             pos.push({
31851                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31852                 y : minY + (this.unitWidth + this.gutter) * 1
31853             });
31854             
31855             return pos;
31856             
31857         }
31858         
31859         pos.push({
31860             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31861             y : minY
31862         });
31863         
31864         pos.push({
31865             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31866             y : minY + (this.unitWidth + this.gutter) * 2
31867         });
31868         
31869         pos.push({
31870             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31871             y : minY + (this.unitWidth + this.gutter) * 2
31872         });
31873         
31874         pos.push({
31875             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),
31876             y : minY + (this.unitWidth + this.gutter) * 2
31877         });
31878
31879         return pos;
31880         
31881     },
31882     
31883     /**
31884     * remove a Masonry Brick
31885     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31886     */
31887     removeBrick : function(brick_id)
31888     {
31889         if (!brick_id) {
31890             return;
31891         }
31892         
31893         for (var i = 0; i<this.bricks.length; i++) {
31894             if (this.bricks[i].id == brick_id) {
31895                 this.bricks.splice(i,1);
31896                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31897                 this.initial();
31898             }
31899         }
31900     },
31901     
31902     /**
31903     * adds a Masonry Brick
31904     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31905     */
31906     addBrick : function(cfg)
31907     {
31908         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31909         //this.register(cn);
31910         cn.parentId = this.id;
31911         cn.onRender(this.el, null);
31912         return cn;
31913     },
31914     
31915     /**
31916     * register a Masonry Brick
31917     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31918     */
31919     
31920     register : function(brick)
31921     {
31922         this.bricks.push(brick);
31923         brick.masonryId = this.id;
31924     },
31925     
31926     /**
31927     * clear all the Masonry Brick
31928     */
31929     clearAll : function()
31930     {
31931         this.bricks = [];
31932         //this.getChildContainer().dom.innerHTML = "";
31933         this.el.dom.innerHTML = '';
31934     },
31935     
31936     getSelected : function()
31937     {
31938         if (!this.selectedBrick) {
31939             return false;
31940         }
31941         
31942         return this.selectedBrick;
31943     }
31944 });
31945
31946 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31947     
31948     groups: {},
31949      /**
31950     * register a Masonry Layout
31951     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31952     */
31953     
31954     register : function(layout)
31955     {
31956         this.groups[layout.id] = layout;
31957     },
31958     /**
31959     * fetch a  Masonry Layout based on the masonry layout ID
31960     * @param {string} the masonry layout to add
31961     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31962     */
31963     
31964     get: function(layout_id) {
31965         if (typeof(this.groups[layout_id]) == 'undefined') {
31966             return false;
31967         }
31968         return this.groups[layout_id] ;
31969     }
31970     
31971     
31972     
31973 });
31974
31975  
31976
31977  /**
31978  *
31979  * This is based on 
31980  * http://masonry.desandro.com
31981  *
31982  * The idea is to render all the bricks based on vertical width...
31983  *
31984  * The original code extends 'outlayer' - we might need to use that....
31985  * 
31986  */
31987
31988
31989 /**
31990  * @class Roo.bootstrap.LayoutMasonryAuto
31991  * @extends Roo.bootstrap.Component
31992  * Bootstrap Layout Masonry class
31993  * 
31994  * @constructor
31995  * Create a new Element
31996  * @param {Object} config The config object
31997  */
31998
31999 Roo.bootstrap.LayoutMasonryAuto = function(config){
32000     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32001 };
32002
32003 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32004     
32005       /**
32006      * @cfg {Boolean} isFitWidth  - resize the width..
32007      */   
32008     isFitWidth : false,  // options..
32009     /**
32010      * @cfg {Boolean} isOriginLeft = left align?
32011      */   
32012     isOriginLeft : true,
32013     /**
32014      * @cfg {Boolean} isOriginTop = top align?
32015      */   
32016     isOriginTop : false,
32017     /**
32018      * @cfg {Boolean} isLayoutInstant = no animation?
32019      */   
32020     isLayoutInstant : false, // needed?
32021     /**
32022      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32023      */   
32024     isResizingContainer : true,
32025     /**
32026      * @cfg {Number} columnWidth  width of the columns 
32027      */   
32028     
32029     columnWidth : 0,
32030     
32031     /**
32032      * @cfg {Number} maxCols maximum number of columns
32033      */   
32034     
32035     maxCols: 0,
32036     /**
32037      * @cfg {Number} padHeight padding below box..
32038      */   
32039     
32040     padHeight : 10, 
32041     
32042     /**
32043      * @cfg {Boolean} isAutoInitial defalut true
32044      */   
32045     
32046     isAutoInitial : true, 
32047     
32048     // private?
32049     gutter : 0,
32050     
32051     containerWidth: 0,
32052     initialColumnWidth : 0,
32053     currentSize : null,
32054     
32055     colYs : null, // array.
32056     maxY : 0,
32057     padWidth: 10,
32058     
32059     
32060     tag: 'div',
32061     cls: '',
32062     bricks: null, //CompositeElement
32063     cols : 0, // array?
32064     // element : null, // wrapped now this.el
32065     _isLayoutInited : null, 
32066     
32067     
32068     getAutoCreate : function(){
32069         
32070         var cfg = {
32071             tag: this.tag,
32072             cls: 'blog-masonary-wrapper ' + this.cls,
32073             cn : {
32074                 cls : 'mas-boxes masonary'
32075             }
32076         };
32077         
32078         return cfg;
32079     },
32080     
32081     getChildContainer: function( )
32082     {
32083         if (this.boxesEl) {
32084             return this.boxesEl;
32085         }
32086         
32087         this.boxesEl = this.el.select('.mas-boxes').first();
32088         
32089         return this.boxesEl;
32090     },
32091     
32092     
32093     initEvents : function()
32094     {
32095         var _this = this;
32096         
32097         if(this.isAutoInitial){
32098             Roo.log('hook children rendered');
32099             this.on('childrenrendered', function() {
32100                 Roo.log('children rendered');
32101                 _this.initial();
32102             } ,this);
32103         }
32104         
32105     },
32106     
32107     initial : function()
32108     {
32109         this.reloadItems();
32110
32111         this.currentSize = this.el.getBox(true);
32112
32113         /// was window resize... - let's see if this works..
32114         Roo.EventManager.onWindowResize(this.resize, this); 
32115
32116         if(!this.isAutoInitial){
32117             this.layout();
32118             return;
32119         }
32120         
32121         this.layout.defer(500,this);
32122     },
32123     
32124     reloadItems: function()
32125     {
32126         this.bricks = this.el.select('.masonry-brick', true);
32127         
32128         this.bricks.each(function(b) {
32129             //Roo.log(b.getSize());
32130             if (!b.attr('originalwidth')) {
32131                 b.attr('originalwidth',  b.getSize().width);
32132             }
32133             
32134         });
32135         
32136         Roo.log(this.bricks.elements.length);
32137     },
32138     
32139     resize : function()
32140     {
32141         Roo.log('resize');
32142         var cs = this.el.getBox(true);
32143         
32144         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32145             Roo.log("no change in with or X");
32146             return;
32147         }
32148         this.currentSize = cs;
32149         this.layout();
32150     },
32151     
32152     layout : function()
32153     {
32154          Roo.log('layout');
32155         this._resetLayout();
32156         //this._manageStamps();
32157       
32158         // don't animate first layout
32159         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32160         this.layoutItems( isInstant );
32161       
32162         // flag for initalized
32163         this._isLayoutInited = true;
32164     },
32165     
32166     layoutItems : function( isInstant )
32167     {
32168         //var items = this._getItemsForLayout( this.items );
32169         // original code supports filtering layout items.. we just ignore it..
32170         
32171         this._layoutItems( this.bricks , isInstant );
32172       
32173         this._postLayout();
32174     },
32175     _layoutItems : function ( items , isInstant)
32176     {
32177        //this.fireEvent( 'layout', this, items );
32178     
32179
32180         if ( !items || !items.elements.length ) {
32181           // no items, emit event with empty array
32182             return;
32183         }
32184
32185         var queue = [];
32186         items.each(function(item) {
32187             Roo.log("layout item");
32188             Roo.log(item);
32189             // get x/y object from method
32190             var position = this._getItemLayoutPosition( item );
32191             // enqueue
32192             position.item = item;
32193             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32194             queue.push( position );
32195         }, this);
32196       
32197         this._processLayoutQueue( queue );
32198     },
32199     /** Sets position of item in DOM
32200     * @param {Element} item
32201     * @param {Number} x - horizontal position
32202     * @param {Number} y - vertical position
32203     * @param {Boolean} isInstant - disables transitions
32204     */
32205     _processLayoutQueue : function( queue )
32206     {
32207         for ( var i=0, len = queue.length; i < len; i++ ) {
32208             var obj = queue[i];
32209             obj.item.position('absolute');
32210             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32211         }
32212     },
32213       
32214     
32215     /**
32216     * Any logic you want to do after each layout,
32217     * i.e. size the container
32218     */
32219     _postLayout : function()
32220     {
32221         this.resizeContainer();
32222     },
32223     
32224     resizeContainer : function()
32225     {
32226         if ( !this.isResizingContainer ) {
32227             return;
32228         }
32229         var size = this._getContainerSize();
32230         if ( size ) {
32231             this.el.setSize(size.width,size.height);
32232             this.boxesEl.setSize(size.width,size.height);
32233         }
32234     },
32235     
32236     
32237     
32238     _resetLayout : function()
32239     {
32240         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32241         this.colWidth = this.el.getWidth();
32242         //this.gutter = this.el.getWidth(); 
32243         
32244         this.measureColumns();
32245
32246         // reset column Y
32247         var i = this.cols;
32248         this.colYs = [];
32249         while (i--) {
32250             this.colYs.push( 0 );
32251         }
32252     
32253         this.maxY = 0;
32254     },
32255
32256     measureColumns : function()
32257     {
32258         this.getContainerWidth();
32259       // if columnWidth is 0, default to outerWidth of first item
32260         if ( !this.columnWidth ) {
32261             var firstItem = this.bricks.first();
32262             Roo.log(firstItem);
32263             this.columnWidth  = this.containerWidth;
32264             if (firstItem && firstItem.attr('originalwidth') ) {
32265                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32266             }
32267             // columnWidth fall back to item of first element
32268             Roo.log("set column width?");
32269                         this.initialColumnWidth = this.columnWidth  ;
32270
32271             // if first elem has no width, default to size of container
32272             
32273         }
32274         
32275         
32276         if (this.initialColumnWidth) {
32277             this.columnWidth = this.initialColumnWidth;
32278         }
32279         
32280         
32281             
32282         // column width is fixed at the top - however if container width get's smaller we should
32283         // reduce it...
32284         
32285         // this bit calcs how man columns..
32286             
32287         var columnWidth = this.columnWidth += this.gutter;
32288       
32289         // calculate columns
32290         var containerWidth = this.containerWidth + this.gutter;
32291         
32292         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32293         // fix rounding errors, typically with gutters
32294         var excess = columnWidth - containerWidth % columnWidth;
32295         
32296         
32297         // if overshoot is less than a pixel, round up, otherwise floor it
32298         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32299         cols = Math[ mathMethod ]( cols );
32300         this.cols = Math.max( cols, 1 );
32301         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32302         
32303          // padding positioning..
32304         var totalColWidth = this.cols * this.columnWidth;
32305         var padavail = this.containerWidth - totalColWidth;
32306         // so for 2 columns - we need 3 'pads'
32307         
32308         var padNeeded = (1+this.cols) * this.padWidth;
32309         
32310         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32311         
32312         this.columnWidth += padExtra
32313         //this.padWidth = Math.floor(padavail /  ( this.cols));
32314         
32315         // adjust colum width so that padding is fixed??
32316         
32317         // we have 3 columns ... total = width * 3
32318         // we have X left over... that should be used by 
32319         
32320         //if (this.expandC) {
32321             
32322         //}
32323         
32324         
32325         
32326     },
32327     
32328     getContainerWidth : function()
32329     {
32330        /* // container is parent if fit width
32331         var container = this.isFitWidth ? this.element.parentNode : this.element;
32332         // check that this.size and size are there
32333         // IE8 triggers resize on body size change, so they might not be
32334         
32335         var size = getSize( container );  //FIXME
32336         this.containerWidth = size && size.innerWidth; //FIXME
32337         */
32338          
32339         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32340         
32341     },
32342     
32343     _getItemLayoutPosition : function( item )  // what is item?
32344     {
32345         // we resize the item to our columnWidth..
32346       
32347         item.setWidth(this.columnWidth);
32348         item.autoBoxAdjust  = false;
32349         
32350         var sz = item.getSize();
32351  
32352         // how many columns does this brick span
32353         var remainder = this.containerWidth % this.columnWidth;
32354         
32355         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32356         // round if off by 1 pixel, otherwise use ceil
32357         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32358         colSpan = Math.min( colSpan, this.cols );
32359         
32360         // normally this should be '1' as we dont' currently allow multi width columns..
32361         
32362         var colGroup = this._getColGroup( colSpan );
32363         // get the minimum Y value from the columns
32364         var minimumY = Math.min.apply( Math, colGroup );
32365         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32366         
32367         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32368          
32369         // position the brick
32370         var position = {
32371             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32372             y: this.currentSize.y + minimumY + this.padHeight
32373         };
32374         
32375         Roo.log(position);
32376         // apply setHeight to necessary columns
32377         var setHeight = minimumY + sz.height + this.padHeight;
32378         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32379         
32380         var setSpan = this.cols + 1 - colGroup.length;
32381         for ( var i = 0; i < setSpan; i++ ) {
32382           this.colYs[ shortColIndex + i ] = setHeight ;
32383         }
32384       
32385         return position;
32386     },
32387     
32388     /**
32389      * @param {Number} colSpan - number of columns the element spans
32390      * @returns {Array} colGroup
32391      */
32392     _getColGroup : function( colSpan )
32393     {
32394         if ( colSpan < 2 ) {
32395           // if brick spans only one column, use all the column Ys
32396           return this.colYs;
32397         }
32398       
32399         var colGroup = [];
32400         // how many different places could this brick fit horizontally
32401         var groupCount = this.cols + 1 - colSpan;
32402         // for each group potential horizontal position
32403         for ( var i = 0; i < groupCount; i++ ) {
32404           // make an array of colY values for that one group
32405           var groupColYs = this.colYs.slice( i, i + colSpan );
32406           // and get the max value of the array
32407           colGroup[i] = Math.max.apply( Math, groupColYs );
32408         }
32409         return colGroup;
32410     },
32411     /*
32412     _manageStamp : function( stamp )
32413     {
32414         var stampSize =  stamp.getSize();
32415         var offset = stamp.getBox();
32416         // get the columns that this stamp affects
32417         var firstX = this.isOriginLeft ? offset.x : offset.right;
32418         var lastX = firstX + stampSize.width;
32419         var firstCol = Math.floor( firstX / this.columnWidth );
32420         firstCol = Math.max( 0, firstCol );
32421         
32422         var lastCol = Math.floor( lastX / this.columnWidth );
32423         // lastCol should not go over if multiple of columnWidth #425
32424         lastCol -= lastX % this.columnWidth ? 0 : 1;
32425         lastCol = Math.min( this.cols - 1, lastCol );
32426         
32427         // set colYs to bottom of the stamp
32428         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32429             stampSize.height;
32430             
32431         for ( var i = firstCol; i <= lastCol; i++ ) {
32432           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32433         }
32434     },
32435     */
32436     
32437     _getContainerSize : function()
32438     {
32439         this.maxY = Math.max.apply( Math, this.colYs );
32440         var size = {
32441             height: this.maxY
32442         };
32443       
32444         if ( this.isFitWidth ) {
32445             size.width = this._getContainerFitWidth();
32446         }
32447       
32448         return size;
32449     },
32450     
32451     _getContainerFitWidth : function()
32452     {
32453         var unusedCols = 0;
32454         // count unused columns
32455         var i = this.cols;
32456         while ( --i ) {
32457           if ( this.colYs[i] !== 0 ) {
32458             break;
32459           }
32460           unusedCols++;
32461         }
32462         // fit container to columns that have been used
32463         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32464     },
32465     
32466     needsResizeLayout : function()
32467     {
32468         var previousWidth = this.containerWidth;
32469         this.getContainerWidth();
32470         return previousWidth !== this.containerWidth;
32471     }
32472  
32473 });
32474
32475  
32476
32477  /*
32478  * - LGPL
32479  *
32480  * element
32481  * 
32482  */
32483
32484 /**
32485  * @class Roo.bootstrap.MasonryBrick
32486  * @extends Roo.bootstrap.Component
32487  * Bootstrap MasonryBrick class
32488  * 
32489  * @constructor
32490  * Create a new MasonryBrick
32491  * @param {Object} config The config object
32492  */
32493
32494 Roo.bootstrap.MasonryBrick = function(config){
32495     
32496     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32497     
32498     Roo.bootstrap.MasonryBrick.register(this);
32499     
32500     this.addEvents({
32501         // raw events
32502         /**
32503          * @event click
32504          * When a MasonryBrick is clcik
32505          * @param {Roo.bootstrap.MasonryBrick} this
32506          * @param {Roo.EventObject} e
32507          */
32508         "click" : true
32509     });
32510 };
32511
32512 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32513     
32514     /**
32515      * @cfg {String} title
32516      */   
32517     title : '',
32518     /**
32519      * @cfg {String} html
32520      */   
32521     html : '',
32522     /**
32523      * @cfg {String} bgimage
32524      */   
32525     bgimage : '',
32526     /**
32527      * @cfg {String} videourl
32528      */   
32529     videourl : '',
32530     /**
32531      * @cfg {String} cls
32532      */   
32533     cls : '',
32534     /**
32535      * @cfg {String} href
32536      */   
32537     href : '',
32538     /**
32539      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32540      */   
32541     size : 'xs',
32542     
32543     /**
32544      * @cfg {String} placetitle (center|bottom)
32545      */   
32546     placetitle : '',
32547     
32548     /**
32549      * @cfg {Boolean} isFitContainer defalut true
32550      */   
32551     isFitContainer : true, 
32552     
32553     /**
32554      * @cfg {Boolean} preventDefault defalut false
32555      */   
32556     preventDefault : false, 
32557     
32558     /**
32559      * @cfg {Boolean} inverse defalut false
32560      */   
32561     maskInverse : false, 
32562     
32563     getAutoCreate : function()
32564     {
32565         if(!this.isFitContainer){
32566             return this.getSplitAutoCreate();
32567         }
32568         
32569         var cls = 'masonry-brick masonry-brick-full';
32570         
32571         if(this.href.length){
32572             cls += ' masonry-brick-link';
32573         }
32574         
32575         if(this.bgimage.length){
32576             cls += ' masonry-brick-image';
32577         }
32578         
32579         if(this.maskInverse){
32580             cls += ' mask-inverse';
32581         }
32582         
32583         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32584             cls += ' enable-mask';
32585         }
32586         
32587         if(this.size){
32588             cls += ' masonry-' + this.size + '-brick';
32589         }
32590         
32591         if(this.placetitle.length){
32592             
32593             switch (this.placetitle) {
32594                 case 'center' :
32595                     cls += ' masonry-center-title';
32596                     break;
32597                 case 'bottom' :
32598                     cls += ' masonry-bottom-title';
32599                     break;
32600                 default:
32601                     break;
32602             }
32603             
32604         } else {
32605             if(!this.html.length && !this.bgimage.length){
32606                 cls += ' masonry-center-title';
32607             }
32608
32609             if(!this.html.length && this.bgimage.length){
32610                 cls += ' masonry-bottom-title';
32611             }
32612         }
32613         
32614         if(this.cls){
32615             cls += ' ' + this.cls;
32616         }
32617         
32618         var cfg = {
32619             tag: (this.href.length) ? 'a' : 'div',
32620             cls: cls,
32621             cn: [
32622                 {
32623                     tag: 'div',
32624                     cls: 'masonry-brick-mask'
32625                 },
32626                 {
32627                     tag: 'div',
32628                     cls: 'masonry-brick-paragraph',
32629                     cn: []
32630                 }
32631             ]
32632         };
32633         
32634         if(this.href.length){
32635             cfg.href = this.href;
32636         }
32637         
32638         var cn = cfg.cn[1].cn;
32639         
32640         if(this.title.length){
32641             cn.push({
32642                 tag: 'h4',
32643                 cls: 'masonry-brick-title',
32644                 html: this.title
32645             });
32646         }
32647         
32648         if(this.html.length){
32649             cn.push({
32650                 tag: 'p',
32651                 cls: 'masonry-brick-text',
32652                 html: this.html
32653             });
32654         }
32655         
32656         if (!this.title.length && !this.html.length) {
32657             cfg.cn[1].cls += ' hide';
32658         }
32659         
32660         if(this.bgimage.length){
32661             cfg.cn.push({
32662                 tag: 'img',
32663                 cls: 'masonry-brick-image-view',
32664                 src: this.bgimage
32665             });
32666         }
32667         
32668         if(this.videourl.length){
32669             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32670             // youtube support only?
32671             cfg.cn.push({
32672                 tag: 'iframe',
32673                 cls: 'masonry-brick-image-view',
32674                 src: vurl,
32675                 frameborder : 0,
32676                 allowfullscreen : true
32677             });
32678         }
32679         
32680         return cfg;
32681         
32682     },
32683     
32684     getSplitAutoCreate : function()
32685     {
32686         var cls = 'masonry-brick masonry-brick-split';
32687         
32688         if(this.href.length){
32689             cls += ' masonry-brick-link';
32690         }
32691         
32692         if(this.bgimage.length){
32693             cls += ' masonry-brick-image';
32694         }
32695         
32696         if(this.size){
32697             cls += ' masonry-' + this.size + '-brick';
32698         }
32699         
32700         switch (this.placetitle) {
32701             case 'center' :
32702                 cls += ' masonry-center-title';
32703                 break;
32704             case 'bottom' :
32705                 cls += ' masonry-bottom-title';
32706                 break;
32707             default:
32708                 if(!this.bgimage.length){
32709                     cls += ' masonry-center-title';
32710                 }
32711
32712                 if(this.bgimage.length){
32713                     cls += ' masonry-bottom-title';
32714                 }
32715                 break;
32716         }
32717         
32718         if(this.cls){
32719             cls += ' ' + this.cls;
32720         }
32721         
32722         var cfg = {
32723             tag: (this.href.length) ? 'a' : 'div',
32724             cls: cls,
32725             cn: [
32726                 {
32727                     tag: 'div',
32728                     cls: 'masonry-brick-split-head',
32729                     cn: [
32730                         {
32731                             tag: 'div',
32732                             cls: 'masonry-brick-paragraph',
32733                             cn: []
32734                         }
32735                     ]
32736                 },
32737                 {
32738                     tag: 'div',
32739                     cls: 'masonry-brick-split-body',
32740                     cn: []
32741                 }
32742             ]
32743         };
32744         
32745         if(this.href.length){
32746             cfg.href = this.href;
32747         }
32748         
32749         if(this.title.length){
32750             cfg.cn[0].cn[0].cn.push({
32751                 tag: 'h4',
32752                 cls: 'masonry-brick-title',
32753                 html: this.title
32754             });
32755         }
32756         
32757         if(this.html.length){
32758             cfg.cn[1].cn.push({
32759                 tag: 'p',
32760                 cls: 'masonry-brick-text',
32761                 html: this.html
32762             });
32763         }
32764
32765         if(this.bgimage.length){
32766             cfg.cn[0].cn.push({
32767                 tag: 'img',
32768                 cls: 'masonry-brick-image-view',
32769                 src: this.bgimage
32770             });
32771         }
32772         
32773         if(this.videourl.length){
32774             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32775             // youtube support only?
32776             cfg.cn[0].cn.cn.push({
32777                 tag: 'iframe',
32778                 cls: 'masonry-brick-image-view',
32779                 src: vurl,
32780                 frameborder : 0,
32781                 allowfullscreen : true
32782             });
32783         }
32784         
32785         return cfg;
32786     },
32787     
32788     initEvents: function() 
32789     {
32790         switch (this.size) {
32791             case 'xs' :
32792                 this.x = 1;
32793                 this.y = 1;
32794                 break;
32795             case 'sm' :
32796                 this.x = 2;
32797                 this.y = 2;
32798                 break;
32799             case 'md' :
32800             case 'md-left' :
32801             case 'md-right' :
32802                 this.x = 3;
32803                 this.y = 3;
32804                 break;
32805             case 'tall' :
32806                 this.x = 2;
32807                 this.y = 3;
32808                 break;
32809             case 'wide' :
32810                 this.x = 3;
32811                 this.y = 2;
32812                 break;
32813             case 'wide-thin' :
32814                 this.x = 3;
32815                 this.y = 1;
32816                 break;
32817                         
32818             default :
32819                 break;
32820         }
32821         
32822         if(Roo.isTouch){
32823             this.el.on('touchstart', this.onTouchStart, this);
32824             this.el.on('touchmove', this.onTouchMove, this);
32825             this.el.on('touchend', this.onTouchEnd, this);
32826             this.el.on('contextmenu', this.onContextMenu, this);
32827         } else {
32828             this.el.on('mouseenter'  ,this.enter, this);
32829             this.el.on('mouseleave', this.leave, this);
32830             this.el.on('click', this.onClick, this);
32831         }
32832         
32833         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32834             this.parent().bricks.push(this);   
32835         }
32836         
32837     },
32838     
32839     onClick: function(e, el)
32840     {
32841         var time = this.endTimer - this.startTimer;
32842         // Roo.log(e.preventDefault());
32843         if(Roo.isTouch){
32844             if(time > 1000){
32845                 e.preventDefault();
32846                 return;
32847             }
32848         }
32849         
32850         if(!this.preventDefault){
32851             return;
32852         }
32853         
32854         e.preventDefault();
32855         
32856         if (this.activeClass != '') {
32857             this.selectBrick();
32858         }
32859         
32860         this.fireEvent('click', this, e);
32861     },
32862     
32863     enter: function(e, el)
32864     {
32865         e.preventDefault();
32866         
32867         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32868             return;
32869         }
32870         
32871         if(this.bgimage.length && this.html.length){
32872             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32873         }
32874     },
32875     
32876     leave: function(e, el)
32877     {
32878         e.preventDefault();
32879         
32880         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32881             return;
32882         }
32883         
32884         if(this.bgimage.length && this.html.length){
32885             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32886         }
32887     },
32888     
32889     onTouchStart: function(e, el)
32890     {
32891 //        e.preventDefault();
32892         
32893         this.touchmoved = false;
32894         
32895         if(!this.isFitContainer){
32896             return;
32897         }
32898         
32899         if(!this.bgimage.length || !this.html.length){
32900             return;
32901         }
32902         
32903         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32904         
32905         this.timer = new Date().getTime();
32906         
32907     },
32908     
32909     onTouchMove: function(e, el)
32910     {
32911         this.touchmoved = true;
32912     },
32913     
32914     onContextMenu : function(e,el)
32915     {
32916         e.preventDefault();
32917         e.stopPropagation();
32918         return false;
32919     },
32920     
32921     onTouchEnd: function(e, el)
32922     {
32923 //        e.preventDefault();
32924         
32925         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32926         
32927             this.leave(e,el);
32928             
32929             return;
32930         }
32931         
32932         if(!this.bgimage.length || !this.html.length){
32933             
32934             if(this.href.length){
32935                 window.location.href = this.href;
32936             }
32937             
32938             return;
32939         }
32940         
32941         if(!this.isFitContainer){
32942             return;
32943         }
32944         
32945         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32946         
32947         window.location.href = this.href;
32948     },
32949     
32950     //selection on single brick only
32951     selectBrick : function() {
32952         
32953         if (!this.parentId) {
32954             return;
32955         }
32956         
32957         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32958         var index = m.selectedBrick.indexOf(this.id);
32959         
32960         if ( index > -1) {
32961             m.selectedBrick.splice(index,1);
32962             this.el.removeClass(this.activeClass);
32963             return;
32964         }
32965         
32966         for(var i = 0; i < m.selectedBrick.length; i++) {
32967             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32968             b.el.removeClass(b.activeClass);
32969         }
32970         
32971         m.selectedBrick = [];
32972         
32973         m.selectedBrick.push(this.id);
32974         this.el.addClass(this.activeClass);
32975         return;
32976     },
32977     
32978     isSelected : function(){
32979         return this.el.hasClass(this.activeClass);
32980         
32981     }
32982 });
32983
32984 Roo.apply(Roo.bootstrap.MasonryBrick, {
32985     
32986     //groups: {},
32987     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32988      /**
32989     * register a Masonry Brick
32990     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32991     */
32992     
32993     register : function(brick)
32994     {
32995         //this.groups[brick.id] = brick;
32996         this.groups.add(brick.id, brick);
32997     },
32998     /**
32999     * fetch a  masonry brick based on the masonry brick ID
33000     * @param {string} the masonry brick to add
33001     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33002     */
33003     
33004     get: function(brick_id) 
33005     {
33006         // if (typeof(this.groups[brick_id]) == 'undefined') {
33007         //     return false;
33008         // }
33009         // return this.groups[brick_id] ;
33010         
33011         if(this.groups.key(brick_id)) {
33012             return this.groups.key(brick_id);
33013         }
33014         
33015         return false;
33016     }
33017     
33018     
33019     
33020 });
33021
33022  /*
33023  * - LGPL
33024  *
33025  * element
33026  * 
33027  */
33028
33029 /**
33030  * @class Roo.bootstrap.Brick
33031  * @extends Roo.bootstrap.Component
33032  * Bootstrap Brick class
33033  * 
33034  * @constructor
33035  * Create a new Brick
33036  * @param {Object} config The config object
33037  */
33038
33039 Roo.bootstrap.Brick = function(config){
33040     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33041     
33042     this.addEvents({
33043         // raw events
33044         /**
33045          * @event click
33046          * When a Brick is click
33047          * @param {Roo.bootstrap.Brick} this
33048          * @param {Roo.EventObject} e
33049          */
33050         "click" : true
33051     });
33052 };
33053
33054 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33055     
33056     /**
33057      * @cfg {String} title
33058      */   
33059     title : '',
33060     /**
33061      * @cfg {String} html
33062      */   
33063     html : '',
33064     /**
33065      * @cfg {String} bgimage
33066      */   
33067     bgimage : '',
33068     /**
33069      * @cfg {String} cls
33070      */   
33071     cls : '',
33072     /**
33073      * @cfg {String} href
33074      */   
33075     href : '',
33076     /**
33077      * @cfg {String} video
33078      */   
33079     video : '',
33080     /**
33081      * @cfg {Boolean} square
33082      */   
33083     square : true,
33084     
33085     getAutoCreate : function()
33086     {
33087         var cls = 'roo-brick';
33088         
33089         if(this.href.length){
33090             cls += ' roo-brick-link';
33091         }
33092         
33093         if(this.bgimage.length){
33094             cls += ' roo-brick-image';
33095         }
33096         
33097         if(!this.html.length && !this.bgimage.length){
33098             cls += ' roo-brick-center-title';
33099         }
33100         
33101         if(!this.html.length && this.bgimage.length){
33102             cls += ' roo-brick-bottom-title';
33103         }
33104         
33105         if(this.cls){
33106             cls += ' ' + this.cls;
33107         }
33108         
33109         var cfg = {
33110             tag: (this.href.length) ? 'a' : 'div',
33111             cls: cls,
33112             cn: [
33113                 {
33114                     tag: 'div',
33115                     cls: 'roo-brick-paragraph',
33116                     cn: []
33117                 }
33118             ]
33119         };
33120         
33121         if(this.href.length){
33122             cfg.href = this.href;
33123         }
33124         
33125         var cn = cfg.cn[0].cn;
33126         
33127         if(this.title.length){
33128             cn.push({
33129                 tag: 'h4',
33130                 cls: 'roo-brick-title',
33131                 html: this.title
33132             });
33133         }
33134         
33135         if(this.html.length){
33136             cn.push({
33137                 tag: 'p',
33138                 cls: 'roo-brick-text',
33139                 html: this.html
33140             });
33141         } else {
33142             cn.cls += ' hide';
33143         }
33144         
33145         if(this.bgimage.length){
33146             cfg.cn.push({
33147                 tag: 'img',
33148                 cls: 'roo-brick-image-view',
33149                 src: this.bgimage
33150             });
33151         }
33152         
33153         return cfg;
33154     },
33155     
33156     initEvents: function() 
33157     {
33158         if(this.title.length || this.html.length){
33159             this.el.on('mouseenter'  ,this.enter, this);
33160             this.el.on('mouseleave', this.leave, this);
33161         }
33162         
33163         Roo.EventManager.onWindowResize(this.resize, this); 
33164         
33165         if(this.bgimage.length){
33166             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33167             this.imageEl.on('load', this.onImageLoad, this);
33168             return;
33169         }
33170         
33171         this.resize();
33172     },
33173     
33174     onImageLoad : function()
33175     {
33176         this.resize();
33177     },
33178     
33179     resize : function()
33180     {
33181         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33182         
33183         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33184         
33185         if(this.bgimage.length){
33186             var image = this.el.select('.roo-brick-image-view', true).first();
33187             
33188             image.setWidth(paragraph.getWidth());
33189             
33190             if(this.square){
33191                 image.setHeight(paragraph.getWidth());
33192             }
33193             
33194             this.el.setHeight(image.getHeight());
33195             paragraph.setHeight(image.getHeight());
33196             
33197         }
33198         
33199     },
33200     
33201     enter: function(e, el)
33202     {
33203         e.preventDefault();
33204         
33205         if(this.bgimage.length){
33206             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33207             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33208         }
33209     },
33210     
33211     leave: function(e, el)
33212     {
33213         e.preventDefault();
33214         
33215         if(this.bgimage.length){
33216             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33217             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33218         }
33219     }
33220     
33221 });
33222
33223  
33224
33225  /*
33226  * - LGPL
33227  *
33228  * Number field 
33229  */
33230
33231 /**
33232  * @class Roo.bootstrap.NumberField
33233  * @extends Roo.bootstrap.Input
33234  * Bootstrap NumberField class
33235  * 
33236  * 
33237  * 
33238  * 
33239  * @constructor
33240  * Create a new NumberField
33241  * @param {Object} config The config object
33242  */
33243
33244 Roo.bootstrap.NumberField = function(config){
33245     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33246 };
33247
33248 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33249     
33250     /**
33251      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33252      */
33253     allowDecimals : true,
33254     /**
33255      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33256      */
33257     decimalSeparator : ".",
33258     /**
33259      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33260      */
33261     decimalPrecision : 2,
33262     /**
33263      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33264      */
33265     allowNegative : true,
33266     
33267     /**
33268      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33269      */
33270     allowZero: true,
33271     /**
33272      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33273      */
33274     minValue : Number.NEGATIVE_INFINITY,
33275     /**
33276      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33277      */
33278     maxValue : Number.MAX_VALUE,
33279     /**
33280      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33281      */
33282     minText : "The minimum value for this field is {0}",
33283     /**
33284      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33285      */
33286     maxText : "The maximum value for this field is {0}",
33287     /**
33288      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33289      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33290      */
33291     nanText : "{0} is not a valid number",
33292     /**
33293      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33294      */
33295     thousandsDelimiter : false,
33296     /**
33297      * @cfg {String} valueAlign alignment of value
33298      */
33299     valueAlign : "left",
33300
33301     getAutoCreate : function()
33302     {
33303         var hiddenInput = {
33304             tag: 'input',
33305             type: 'hidden',
33306             id: Roo.id(),
33307             cls: 'hidden-number-input'
33308         };
33309         
33310         if (this.name) {
33311             hiddenInput.name = this.name;
33312         }
33313         
33314         this.name = '';
33315         
33316         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33317         
33318         this.name = hiddenInput.name;
33319         
33320         if(cfg.cn.length > 0) {
33321             cfg.cn.push(hiddenInput);
33322         }
33323         
33324         return cfg;
33325     },
33326
33327     // private
33328     initEvents : function()
33329     {   
33330         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33331         
33332         var allowed = "0123456789";
33333         
33334         if(this.allowDecimals){
33335             allowed += this.decimalSeparator;
33336         }
33337         
33338         if(this.allowNegative){
33339             allowed += "-";
33340         }
33341         
33342         if(this.thousandsDelimiter) {
33343             allowed += ",";
33344         }
33345         
33346         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33347         
33348         var keyPress = function(e){
33349             
33350             var k = e.getKey();
33351             
33352             var c = e.getCharCode();
33353             
33354             if(
33355                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33356                     allowed.indexOf(String.fromCharCode(c)) === -1
33357             ){
33358                 e.stopEvent();
33359                 return;
33360             }
33361             
33362             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33363                 return;
33364             }
33365             
33366             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33367                 e.stopEvent();
33368             }
33369         };
33370         
33371         this.el.on("keypress", keyPress, this);
33372     },
33373     
33374     validateValue : function(value)
33375     {
33376         
33377         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33378             return false;
33379         }
33380         
33381         var num = this.parseValue(value);
33382         
33383         if(isNaN(num)){
33384             this.markInvalid(String.format(this.nanText, value));
33385             return false;
33386         }
33387         
33388         if(num < this.minValue){
33389             this.markInvalid(String.format(this.minText, this.minValue));
33390             return false;
33391         }
33392         
33393         if(num > this.maxValue){
33394             this.markInvalid(String.format(this.maxText, this.maxValue));
33395             return false;
33396         }
33397         
33398         return true;
33399     },
33400
33401     getValue : function()
33402     {
33403         var v = this.hiddenEl().getValue();
33404         
33405         return this.fixPrecision(this.parseValue(v));
33406     },
33407
33408     parseValue : function(value)
33409     {
33410         if(this.thousandsDelimiter) {
33411             value += "";
33412             r = new RegExp(",", "g");
33413             value = value.replace(r, "");
33414         }
33415         
33416         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33417         return isNaN(value) ? '' : value;
33418     },
33419
33420     fixPrecision : function(value)
33421     {
33422         if(this.thousandsDelimiter) {
33423             value += "";
33424             r = new RegExp(",", "g");
33425             value = value.replace(r, "");
33426         }
33427         
33428         var nan = isNaN(value);
33429         
33430         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33431             return nan ? '' : value;
33432         }
33433         return parseFloat(value).toFixed(this.decimalPrecision);
33434     },
33435
33436     setValue : function(v)
33437     {
33438         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33439         
33440         this.value = v;
33441         
33442         if(this.rendered){
33443             
33444             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33445             
33446             this.inputEl().dom.value = (v == '') ? '' :
33447                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33448             
33449             if(!this.allowZero && v === '0') {
33450                 this.hiddenEl().dom.value = '';
33451                 this.inputEl().dom.value = '';
33452             }
33453             
33454             this.validate();
33455         }
33456     },
33457
33458     decimalPrecisionFcn : function(v)
33459     {
33460         return Math.floor(v);
33461     },
33462
33463     beforeBlur : function()
33464     {
33465         var v = this.parseValue(this.getRawValue());
33466         
33467         if(v || v === 0 || v === ''){
33468             this.setValue(v);
33469         }
33470     },
33471     
33472     hiddenEl : function()
33473     {
33474         return this.el.select('input.hidden-number-input',true).first();
33475     }
33476     
33477 });
33478
33479  
33480
33481 /*
33482 * Licence: LGPL
33483 */
33484
33485 /**
33486  * @class Roo.bootstrap.DocumentSlider
33487  * @extends Roo.bootstrap.Component
33488  * Bootstrap DocumentSlider class
33489  * 
33490  * @constructor
33491  * Create a new DocumentViewer
33492  * @param {Object} config The config object
33493  */
33494
33495 Roo.bootstrap.DocumentSlider = function(config){
33496     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33497     
33498     this.files = [];
33499     
33500     this.addEvents({
33501         /**
33502          * @event initial
33503          * Fire after initEvent
33504          * @param {Roo.bootstrap.DocumentSlider} this
33505          */
33506         "initial" : true,
33507         /**
33508          * @event update
33509          * Fire after update
33510          * @param {Roo.bootstrap.DocumentSlider} this
33511          */
33512         "update" : true,
33513         /**
33514          * @event click
33515          * Fire after click
33516          * @param {Roo.bootstrap.DocumentSlider} this
33517          */
33518         "click" : true
33519     });
33520 };
33521
33522 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33523     
33524     files : false,
33525     
33526     indicator : 0,
33527     
33528     getAutoCreate : function()
33529     {
33530         var cfg = {
33531             tag : 'div',
33532             cls : 'roo-document-slider',
33533             cn : [
33534                 {
33535                     tag : 'div',
33536                     cls : 'roo-document-slider-header',
33537                     cn : [
33538                         {
33539                             tag : 'div',
33540                             cls : 'roo-document-slider-header-title'
33541                         }
33542                     ]
33543                 },
33544                 {
33545                     tag : 'div',
33546                     cls : 'roo-document-slider-body',
33547                     cn : [
33548                         {
33549                             tag : 'div',
33550                             cls : 'roo-document-slider-prev',
33551                             cn : [
33552                                 {
33553                                     tag : 'i',
33554                                     cls : 'fa fa-chevron-left'
33555                                 }
33556                             ]
33557                         },
33558                         {
33559                             tag : 'div',
33560                             cls : 'roo-document-slider-thumb',
33561                             cn : [
33562                                 {
33563                                     tag : 'img',
33564                                     cls : 'roo-document-slider-image'
33565                                 }
33566                             ]
33567                         },
33568                         {
33569                             tag : 'div',
33570                             cls : 'roo-document-slider-next',
33571                             cn : [
33572                                 {
33573                                     tag : 'i',
33574                                     cls : 'fa fa-chevron-right'
33575                                 }
33576                             ]
33577                         }
33578                     ]
33579                 }
33580             ]
33581         };
33582         
33583         return cfg;
33584     },
33585     
33586     initEvents : function()
33587     {
33588         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33589         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33590         
33591         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33592         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33593         
33594         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33595         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33596         
33597         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33598         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33599         
33600         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33601         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33602         
33603         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33604         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33605         
33606         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33607         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33608         
33609         this.thumbEl.on('click', this.onClick, this);
33610         
33611         this.prevIndicator.on('click', this.prev, this);
33612         
33613         this.nextIndicator.on('click', this.next, this);
33614         
33615     },
33616     
33617     initial : function()
33618     {
33619         if(this.files.length){
33620             this.indicator = 1;
33621             this.update()
33622         }
33623         
33624         this.fireEvent('initial', this);
33625     },
33626     
33627     update : function()
33628     {
33629         this.imageEl.attr('src', this.files[this.indicator - 1]);
33630         
33631         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33632         
33633         this.prevIndicator.show();
33634         
33635         if(this.indicator == 1){
33636             this.prevIndicator.hide();
33637         }
33638         
33639         this.nextIndicator.show();
33640         
33641         if(this.indicator == this.files.length){
33642             this.nextIndicator.hide();
33643         }
33644         
33645         this.thumbEl.scrollTo('top');
33646         
33647         this.fireEvent('update', this);
33648     },
33649     
33650     onClick : function(e)
33651     {
33652         e.preventDefault();
33653         
33654         this.fireEvent('click', this);
33655     },
33656     
33657     prev : function(e)
33658     {
33659         e.preventDefault();
33660         
33661         this.indicator = Math.max(1, this.indicator - 1);
33662         
33663         this.update();
33664     },
33665     
33666     next : function(e)
33667     {
33668         e.preventDefault();
33669         
33670         this.indicator = Math.min(this.files.length, this.indicator + 1);
33671         
33672         this.update();
33673     }
33674 });
33675 /*
33676  * - LGPL
33677  *
33678  * RadioSet
33679  *
33680  *
33681  */
33682
33683 /**
33684  * @class Roo.bootstrap.RadioSet
33685  * @extends Roo.bootstrap.Input
33686  * Bootstrap RadioSet class
33687  * @cfg {String} indicatorpos (left|right) default left
33688  * @cfg {Boolean} inline (true|false) inline the element (default true)
33689  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33690  * @constructor
33691  * Create a new RadioSet
33692  * @param {Object} config The config object
33693  */
33694
33695 Roo.bootstrap.RadioSet = function(config){
33696     
33697     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33698     
33699     this.radioes = [];
33700     
33701     Roo.bootstrap.RadioSet.register(this);
33702     
33703     this.addEvents({
33704         /**
33705         * @event check
33706         * Fires when the element is checked or unchecked.
33707         * @param {Roo.bootstrap.RadioSet} this This radio
33708         * @param {Roo.bootstrap.Radio} item The checked item
33709         */
33710        check : true,
33711        /**
33712         * @event click
33713         * Fires when the element is click.
33714         * @param {Roo.bootstrap.RadioSet} this This radio set
33715         * @param {Roo.bootstrap.Radio} item The checked item
33716         * @param {Roo.EventObject} e The event object
33717         */
33718        click : true
33719     });
33720     
33721 };
33722
33723 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33724
33725     radioes : false,
33726     
33727     inline : true,
33728     
33729     weight : '',
33730     
33731     indicatorpos : 'left',
33732     
33733     getAutoCreate : function()
33734     {
33735         var label = {
33736             tag : 'label',
33737             cls : 'roo-radio-set-label',
33738             cn : [
33739                 {
33740                     tag : 'span',
33741                     html : this.fieldLabel
33742                 }
33743             ]
33744         };
33745         
33746         if(this.indicatorpos == 'left'){
33747             label.cn.unshift({
33748                 tag : 'i',
33749                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33750                 tooltip : 'This field is required'
33751             });
33752         } else {
33753             label.cn.push({
33754                 tag : 'i',
33755                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33756                 tooltip : 'This field is required'
33757             });
33758         }
33759         
33760         var items = {
33761             tag : 'div',
33762             cls : 'roo-radio-set-items'
33763         };
33764         
33765         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33766         
33767         if (align === 'left' && this.fieldLabel.length) {
33768             
33769             items = {
33770                 cls : "roo-radio-set-right", 
33771                 cn: [
33772                     items
33773                 ]
33774             };
33775             
33776             if(this.labelWidth > 12){
33777                 label.style = "width: " + this.labelWidth + 'px';
33778             }
33779             
33780             if(this.labelWidth < 13 && this.labelmd == 0){
33781                 this.labelmd = this.labelWidth;
33782             }
33783             
33784             if(this.labellg > 0){
33785                 label.cls += ' col-lg-' + this.labellg;
33786                 items.cls += ' col-lg-' + (12 - this.labellg);
33787             }
33788             
33789             if(this.labelmd > 0){
33790                 label.cls += ' col-md-' + this.labelmd;
33791                 items.cls += ' col-md-' + (12 - this.labelmd);
33792             }
33793             
33794             if(this.labelsm > 0){
33795                 label.cls += ' col-sm-' + this.labelsm;
33796                 items.cls += ' col-sm-' + (12 - this.labelsm);
33797             }
33798             
33799             if(this.labelxs > 0){
33800                 label.cls += ' col-xs-' + this.labelxs;
33801                 items.cls += ' col-xs-' + (12 - this.labelxs);
33802             }
33803         }
33804         
33805         var cfg = {
33806             tag : 'div',
33807             cls : 'roo-radio-set',
33808             cn : [
33809                 {
33810                     tag : 'input',
33811                     cls : 'roo-radio-set-input',
33812                     type : 'hidden',
33813                     name : this.name,
33814                     value : this.value ? this.value :  ''
33815                 },
33816                 label,
33817                 items
33818             ]
33819         };
33820         
33821         if(this.weight.length){
33822             cfg.cls += ' roo-radio-' + this.weight;
33823         }
33824         
33825         if(this.inline) {
33826             cfg.cls += ' roo-radio-set-inline';
33827         }
33828         
33829         var settings=this;
33830         ['xs','sm','md','lg'].map(function(size){
33831             if (settings[size]) {
33832                 cfg.cls += ' col-' + size + '-' + settings[size];
33833             }
33834         });
33835         
33836         return cfg;
33837         
33838     },
33839
33840     initEvents : function()
33841     {
33842         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33843         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33844         
33845         if(!this.fieldLabel.length){
33846             this.labelEl.hide();
33847         }
33848         
33849         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33850         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33851         
33852         this.indicator = this.indicatorEl();
33853         
33854         if(this.indicator){
33855             this.indicator.addClass('invisible');
33856         }
33857         
33858         this.originalValue = this.getValue();
33859         
33860     },
33861     
33862     inputEl: function ()
33863     {
33864         return this.el.select('.roo-radio-set-input', true).first();
33865     },
33866     
33867     getChildContainer : function()
33868     {
33869         return this.itemsEl;
33870     },
33871     
33872     register : function(item)
33873     {
33874         this.radioes.push(item);
33875         
33876     },
33877     
33878     validate : function()
33879     {   
33880         if(this.getVisibilityEl().hasClass('hidden')){
33881             return true;
33882         }
33883         
33884         var valid = false;
33885         
33886         Roo.each(this.radioes, function(i){
33887             if(!i.checked){
33888                 return;
33889             }
33890             
33891             valid = true;
33892             return false;
33893         });
33894         
33895         if(this.allowBlank) {
33896             return true;
33897         }
33898         
33899         if(this.disabled || valid){
33900             this.markValid();
33901             return true;
33902         }
33903         
33904         this.markInvalid();
33905         return false;
33906         
33907     },
33908     
33909     markValid : function()
33910     {
33911         if(this.labelEl.isVisible(true)){
33912             this.indicatorEl().removeClass('visible');
33913             this.indicatorEl().addClass('invisible');
33914         }
33915         
33916         this.el.removeClass([this.invalidClass, this.validClass]);
33917         this.el.addClass(this.validClass);
33918         
33919         this.fireEvent('valid', this);
33920     },
33921     
33922     markInvalid : function(msg)
33923     {
33924         if(this.allowBlank || this.disabled){
33925             return;
33926         }
33927         
33928         if(this.labelEl.isVisible(true)){
33929             this.indicatorEl().removeClass('invisible');
33930             this.indicatorEl().addClass('visible');
33931         }
33932         
33933         this.el.removeClass([this.invalidClass, this.validClass]);
33934         this.el.addClass(this.invalidClass);
33935         
33936         this.fireEvent('invalid', this, msg);
33937         
33938     },
33939     
33940     setValue : function(v, suppressEvent)
33941     {   
33942         if(this.value === v){
33943             return;
33944         }
33945         
33946         this.value = v;
33947         
33948         if(this.rendered){
33949             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33950         }
33951         
33952         Roo.each(this.radioes, function(i){
33953             i.checked = false;
33954             i.el.removeClass('checked');
33955         });
33956         
33957         Roo.each(this.radioes, function(i){
33958             
33959             if(i.value === v || i.value.toString() === v.toString()){
33960                 i.checked = true;
33961                 i.el.addClass('checked');
33962                 
33963                 if(suppressEvent !== true){
33964                     this.fireEvent('check', this, i);
33965                 }
33966                 
33967                 return false;
33968             }
33969             
33970         }, this);
33971         
33972         this.validate();
33973     },
33974     
33975     clearInvalid : function(){
33976         
33977         if(!this.el || this.preventMark){
33978             return;
33979         }
33980         
33981         this.el.removeClass([this.invalidClass]);
33982         
33983         this.fireEvent('valid', this);
33984     }
33985     
33986 });
33987
33988 Roo.apply(Roo.bootstrap.RadioSet, {
33989     
33990     groups: {},
33991     
33992     register : function(set)
33993     {
33994         this.groups[set.name] = set;
33995     },
33996     
33997     get: function(name) 
33998     {
33999         if (typeof(this.groups[name]) == 'undefined') {
34000             return false;
34001         }
34002         
34003         return this.groups[name] ;
34004     }
34005     
34006 });
34007 /*
34008  * Based on:
34009  * Ext JS Library 1.1.1
34010  * Copyright(c) 2006-2007, Ext JS, LLC.
34011  *
34012  * Originally Released Under LGPL - original licence link has changed is not relivant.
34013  *
34014  * Fork - LGPL
34015  * <script type="text/javascript">
34016  */
34017
34018
34019 /**
34020  * @class Roo.bootstrap.SplitBar
34021  * @extends Roo.util.Observable
34022  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34023  * <br><br>
34024  * Usage:
34025  * <pre><code>
34026 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34027                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34028 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34029 split.minSize = 100;
34030 split.maxSize = 600;
34031 split.animate = true;
34032 split.on('moved', splitterMoved);
34033 </code></pre>
34034  * @constructor
34035  * Create a new SplitBar
34036  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34037  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34038  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34039  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34040                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34041                         position of the SplitBar).
34042  */
34043 Roo.bootstrap.SplitBar = function(cfg){
34044     
34045     /** @private */
34046     
34047     //{
34048     //  dragElement : elm
34049     //  resizingElement: el,
34050         // optional..
34051     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34052     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34053         // existingProxy ???
34054     //}
34055     
34056     this.el = Roo.get(cfg.dragElement, true);
34057     this.el.dom.unselectable = "on";
34058     /** @private */
34059     this.resizingEl = Roo.get(cfg.resizingElement, true);
34060
34061     /**
34062      * @private
34063      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34064      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34065      * @type Number
34066      */
34067     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34068     
34069     /**
34070      * The minimum size of the resizing element. (Defaults to 0)
34071      * @type Number
34072      */
34073     this.minSize = 0;
34074     
34075     /**
34076      * The maximum size of the resizing element. (Defaults to 2000)
34077      * @type Number
34078      */
34079     this.maxSize = 2000;
34080     
34081     /**
34082      * Whether to animate the transition to the new size
34083      * @type Boolean
34084      */
34085     this.animate = false;
34086     
34087     /**
34088      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34089      * @type Boolean
34090      */
34091     this.useShim = false;
34092     
34093     /** @private */
34094     this.shim = null;
34095     
34096     if(!cfg.existingProxy){
34097         /** @private */
34098         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34099     }else{
34100         this.proxy = Roo.get(cfg.existingProxy).dom;
34101     }
34102     /** @private */
34103     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34104     
34105     /** @private */
34106     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34107     
34108     /** @private */
34109     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34110     
34111     /** @private */
34112     this.dragSpecs = {};
34113     
34114     /**
34115      * @private The adapter to use to positon and resize elements
34116      */
34117     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34118     this.adapter.init(this);
34119     
34120     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34121         /** @private */
34122         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34123         this.el.addClass("roo-splitbar-h");
34124     }else{
34125         /** @private */
34126         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34127         this.el.addClass("roo-splitbar-v");
34128     }
34129     
34130     this.addEvents({
34131         /**
34132          * @event resize
34133          * Fires when the splitter is moved (alias for {@link #event-moved})
34134          * @param {Roo.bootstrap.SplitBar} this
34135          * @param {Number} newSize the new width or height
34136          */
34137         "resize" : true,
34138         /**
34139          * @event moved
34140          * Fires when the splitter is moved
34141          * @param {Roo.bootstrap.SplitBar} this
34142          * @param {Number} newSize the new width or height
34143          */
34144         "moved" : true,
34145         /**
34146          * @event beforeresize
34147          * Fires before the splitter is dragged
34148          * @param {Roo.bootstrap.SplitBar} this
34149          */
34150         "beforeresize" : true,
34151
34152         "beforeapply" : true
34153     });
34154
34155     Roo.util.Observable.call(this);
34156 };
34157
34158 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34159     onStartProxyDrag : function(x, y){
34160         this.fireEvent("beforeresize", this);
34161         if(!this.overlay){
34162             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34163             o.unselectable();
34164             o.enableDisplayMode("block");
34165             // all splitbars share the same overlay
34166             Roo.bootstrap.SplitBar.prototype.overlay = o;
34167         }
34168         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34169         this.overlay.show();
34170         Roo.get(this.proxy).setDisplayed("block");
34171         var size = this.adapter.getElementSize(this);
34172         this.activeMinSize = this.getMinimumSize();;
34173         this.activeMaxSize = this.getMaximumSize();;
34174         var c1 = size - this.activeMinSize;
34175         var c2 = Math.max(this.activeMaxSize - size, 0);
34176         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34177             this.dd.resetConstraints();
34178             this.dd.setXConstraint(
34179                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34180                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34181             );
34182             this.dd.setYConstraint(0, 0);
34183         }else{
34184             this.dd.resetConstraints();
34185             this.dd.setXConstraint(0, 0);
34186             this.dd.setYConstraint(
34187                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34188                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34189             );
34190          }
34191         this.dragSpecs.startSize = size;
34192         this.dragSpecs.startPoint = [x, y];
34193         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34194     },
34195     
34196     /** 
34197      * @private Called after the drag operation by the DDProxy
34198      */
34199     onEndProxyDrag : function(e){
34200         Roo.get(this.proxy).setDisplayed(false);
34201         var endPoint = Roo.lib.Event.getXY(e);
34202         if(this.overlay){
34203             this.overlay.hide();
34204         }
34205         var newSize;
34206         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34207             newSize = this.dragSpecs.startSize + 
34208                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34209                     endPoint[0] - this.dragSpecs.startPoint[0] :
34210                     this.dragSpecs.startPoint[0] - endPoint[0]
34211                 );
34212         }else{
34213             newSize = this.dragSpecs.startSize + 
34214                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34215                     endPoint[1] - this.dragSpecs.startPoint[1] :
34216                     this.dragSpecs.startPoint[1] - endPoint[1]
34217                 );
34218         }
34219         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34220         if(newSize != this.dragSpecs.startSize){
34221             if(this.fireEvent('beforeapply', this, newSize) !== false){
34222                 this.adapter.setElementSize(this, newSize);
34223                 this.fireEvent("moved", this, newSize);
34224                 this.fireEvent("resize", this, newSize);
34225             }
34226         }
34227     },
34228     
34229     /**
34230      * Get the adapter this SplitBar uses
34231      * @return The adapter object
34232      */
34233     getAdapter : function(){
34234         return this.adapter;
34235     },
34236     
34237     /**
34238      * Set the adapter this SplitBar uses
34239      * @param {Object} adapter A SplitBar adapter object
34240      */
34241     setAdapter : function(adapter){
34242         this.adapter = adapter;
34243         this.adapter.init(this);
34244     },
34245     
34246     /**
34247      * Gets the minimum size for the resizing element
34248      * @return {Number} The minimum size
34249      */
34250     getMinimumSize : function(){
34251         return this.minSize;
34252     },
34253     
34254     /**
34255      * Sets the minimum size for the resizing element
34256      * @param {Number} minSize The minimum size
34257      */
34258     setMinimumSize : function(minSize){
34259         this.minSize = minSize;
34260     },
34261     
34262     /**
34263      * Gets the maximum size for the resizing element
34264      * @return {Number} The maximum size
34265      */
34266     getMaximumSize : function(){
34267         return this.maxSize;
34268     },
34269     
34270     /**
34271      * Sets the maximum size for the resizing element
34272      * @param {Number} maxSize The maximum size
34273      */
34274     setMaximumSize : function(maxSize){
34275         this.maxSize = maxSize;
34276     },
34277     
34278     /**
34279      * Sets the initialize size for the resizing element
34280      * @param {Number} size The initial size
34281      */
34282     setCurrentSize : function(size){
34283         var oldAnimate = this.animate;
34284         this.animate = false;
34285         this.adapter.setElementSize(this, size);
34286         this.animate = oldAnimate;
34287     },
34288     
34289     /**
34290      * Destroy this splitbar. 
34291      * @param {Boolean} removeEl True to remove the element
34292      */
34293     destroy : function(removeEl){
34294         if(this.shim){
34295             this.shim.remove();
34296         }
34297         this.dd.unreg();
34298         this.proxy.parentNode.removeChild(this.proxy);
34299         if(removeEl){
34300             this.el.remove();
34301         }
34302     }
34303 });
34304
34305 /**
34306  * @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.
34307  */
34308 Roo.bootstrap.SplitBar.createProxy = function(dir){
34309     var proxy = new Roo.Element(document.createElement("div"));
34310     proxy.unselectable();
34311     var cls = 'roo-splitbar-proxy';
34312     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34313     document.body.appendChild(proxy.dom);
34314     return proxy.dom;
34315 };
34316
34317 /** 
34318  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34319  * Default Adapter. It assumes the splitter and resizing element are not positioned
34320  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34321  */
34322 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34323 };
34324
34325 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34326     // do nothing for now
34327     init : function(s){
34328     
34329     },
34330     /**
34331      * Called before drag operations to get the current size of the resizing element. 
34332      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34333      */
34334      getElementSize : function(s){
34335         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34336             return s.resizingEl.getWidth();
34337         }else{
34338             return s.resizingEl.getHeight();
34339         }
34340     },
34341     
34342     /**
34343      * Called after drag operations to set the size of the resizing element.
34344      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34345      * @param {Number} newSize The new size to set
34346      * @param {Function} onComplete A function to be invoked when resizing is complete
34347      */
34348     setElementSize : function(s, newSize, onComplete){
34349         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34350             if(!s.animate){
34351                 s.resizingEl.setWidth(newSize);
34352                 if(onComplete){
34353                     onComplete(s, newSize);
34354                 }
34355             }else{
34356                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34357             }
34358         }else{
34359             
34360             if(!s.animate){
34361                 s.resizingEl.setHeight(newSize);
34362                 if(onComplete){
34363                     onComplete(s, newSize);
34364                 }
34365             }else{
34366                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34367             }
34368         }
34369     }
34370 };
34371
34372 /** 
34373  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34374  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34375  * Adapter that  moves the splitter element to align with the resized sizing element. 
34376  * Used with an absolute positioned SplitBar.
34377  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34378  * document.body, make sure you assign an id to the body element.
34379  */
34380 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34381     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34382     this.container = Roo.get(container);
34383 };
34384
34385 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34386     init : function(s){
34387         this.basic.init(s);
34388     },
34389     
34390     getElementSize : function(s){
34391         return this.basic.getElementSize(s);
34392     },
34393     
34394     setElementSize : function(s, newSize, onComplete){
34395         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34396     },
34397     
34398     moveSplitter : function(s){
34399         var yes = Roo.bootstrap.SplitBar;
34400         switch(s.placement){
34401             case yes.LEFT:
34402                 s.el.setX(s.resizingEl.getRight());
34403                 break;
34404             case yes.RIGHT:
34405                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34406                 break;
34407             case yes.TOP:
34408                 s.el.setY(s.resizingEl.getBottom());
34409                 break;
34410             case yes.BOTTOM:
34411                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34412                 break;
34413         }
34414     }
34415 };
34416
34417 /**
34418  * Orientation constant - Create a vertical SplitBar
34419  * @static
34420  * @type Number
34421  */
34422 Roo.bootstrap.SplitBar.VERTICAL = 1;
34423
34424 /**
34425  * Orientation constant - Create a horizontal SplitBar
34426  * @static
34427  * @type Number
34428  */
34429 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34430
34431 /**
34432  * Placement constant - The resizing element is to the left of the splitter element
34433  * @static
34434  * @type Number
34435  */
34436 Roo.bootstrap.SplitBar.LEFT = 1;
34437
34438 /**
34439  * Placement constant - The resizing element is to the right of the splitter element
34440  * @static
34441  * @type Number
34442  */
34443 Roo.bootstrap.SplitBar.RIGHT = 2;
34444
34445 /**
34446  * Placement constant - The resizing element is positioned above the splitter element
34447  * @static
34448  * @type Number
34449  */
34450 Roo.bootstrap.SplitBar.TOP = 3;
34451
34452 /**
34453  * Placement constant - The resizing element is positioned under splitter element
34454  * @static
34455  * @type Number
34456  */
34457 Roo.bootstrap.SplitBar.BOTTOM = 4;
34458 Roo.namespace("Roo.bootstrap.layout");/*
34459  * Based on:
34460  * Ext JS Library 1.1.1
34461  * Copyright(c) 2006-2007, Ext JS, LLC.
34462  *
34463  * Originally Released Under LGPL - original licence link has changed is not relivant.
34464  *
34465  * Fork - LGPL
34466  * <script type="text/javascript">
34467  */
34468
34469 /**
34470  * @class Roo.bootstrap.layout.Manager
34471  * @extends Roo.bootstrap.Component
34472  * Base class for layout managers.
34473  */
34474 Roo.bootstrap.layout.Manager = function(config)
34475 {
34476     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34477
34478
34479
34480
34481
34482     /** false to disable window resize monitoring @type Boolean */
34483     this.monitorWindowResize = true;
34484     this.regions = {};
34485     this.addEvents({
34486         /**
34487          * @event layout
34488          * Fires when a layout is performed.
34489          * @param {Roo.LayoutManager} this
34490          */
34491         "layout" : true,
34492         /**
34493          * @event regionresized
34494          * Fires when the user resizes a region.
34495          * @param {Roo.LayoutRegion} region The resized region
34496          * @param {Number} newSize The new size (width for east/west, height for north/south)
34497          */
34498         "regionresized" : true,
34499         /**
34500          * @event regioncollapsed
34501          * Fires when a region is collapsed.
34502          * @param {Roo.LayoutRegion} region The collapsed region
34503          */
34504         "regioncollapsed" : true,
34505         /**
34506          * @event regionexpanded
34507          * Fires when a region is expanded.
34508          * @param {Roo.LayoutRegion} region The expanded region
34509          */
34510         "regionexpanded" : true
34511     });
34512     this.updating = false;
34513
34514     if (config.el) {
34515         this.el = Roo.get(config.el);
34516         this.initEvents();
34517     }
34518
34519 };
34520
34521 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34522
34523
34524     regions : null,
34525
34526     monitorWindowResize : true,
34527
34528
34529     updating : false,
34530
34531
34532     onRender : function(ct, position)
34533     {
34534         if(!this.el){
34535             this.el = Roo.get(ct);
34536             this.initEvents();
34537         }
34538         //this.fireEvent('render',this);
34539     },
34540
34541
34542     initEvents: function()
34543     {
34544
34545
34546         // ie scrollbar fix
34547         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34548             document.body.scroll = "no";
34549         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34550             this.el.position('relative');
34551         }
34552         this.id = this.el.id;
34553         this.el.addClass("roo-layout-container");
34554         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34555         if(this.el.dom != document.body ) {
34556             this.el.on('resize', this.layout,this);
34557             this.el.on('show', this.layout,this);
34558         }
34559
34560     },
34561
34562     /**
34563      * Returns true if this layout is currently being updated
34564      * @return {Boolean}
34565      */
34566     isUpdating : function(){
34567         return this.updating;
34568     },
34569
34570     /**
34571      * Suspend the LayoutManager from doing auto-layouts while
34572      * making multiple add or remove calls
34573      */
34574     beginUpdate : function(){
34575         this.updating = true;
34576     },
34577
34578     /**
34579      * Restore auto-layouts and optionally disable the manager from performing a layout
34580      * @param {Boolean} noLayout true to disable a layout update
34581      */
34582     endUpdate : function(noLayout){
34583         this.updating = false;
34584         if(!noLayout){
34585             this.layout();
34586         }
34587     },
34588
34589     layout: function(){
34590         // abstract...
34591     },
34592
34593     onRegionResized : function(region, newSize){
34594         this.fireEvent("regionresized", region, newSize);
34595         this.layout();
34596     },
34597
34598     onRegionCollapsed : function(region){
34599         this.fireEvent("regioncollapsed", region);
34600     },
34601
34602     onRegionExpanded : function(region){
34603         this.fireEvent("regionexpanded", region);
34604     },
34605
34606     /**
34607      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34608      * performs box-model adjustments.
34609      * @return {Object} The size as an object {width: (the width), height: (the height)}
34610      */
34611     getViewSize : function()
34612     {
34613         var size;
34614         if(this.el.dom != document.body){
34615             size = this.el.getSize();
34616         }else{
34617             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34618         }
34619         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34620         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34621         return size;
34622     },
34623
34624     /**
34625      * Returns the Element this layout is bound to.
34626      * @return {Roo.Element}
34627      */
34628     getEl : function(){
34629         return this.el;
34630     },
34631
34632     /**
34633      * Returns the specified region.
34634      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34635      * @return {Roo.LayoutRegion}
34636      */
34637     getRegion : function(target){
34638         return this.regions[target.toLowerCase()];
34639     },
34640
34641     onWindowResize : function(){
34642         if(this.monitorWindowResize){
34643             this.layout();
34644         }
34645     }
34646 });
34647 /*
34648  * Based on:
34649  * Ext JS Library 1.1.1
34650  * Copyright(c) 2006-2007, Ext JS, LLC.
34651  *
34652  * Originally Released Under LGPL - original licence link has changed is not relivant.
34653  *
34654  * Fork - LGPL
34655  * <script type="text/javascript">
34656  */
34657 /**
34658  * @class Roo.bootstrap.layout.Border
34659  * @extends Roo.bootstrap.layout.Manager
34660  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34661  * please see: examples/bootstrap/nested.html<br><br>
34662  
34663 <b>The container the layout is rendered into can be either the body element or any other element.
34664 If it is not the body element, the container needs to either be an absolute positioned element,
34665 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34666 the container size if it is not the body element.</b>
34667
34668 * @constructor
34669 * Create a new Border
34670 * @param {Object} config Configuration options
34671  */
34672 Roo.bootstrap.layout.Border = function(config){
34673     config = config || {};
34674     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34675     
34676     
34677     
34678     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34679         if(config[region]){
34680             config[region].region = region;
34681             this.addRegion(config[region]);
34682         }
34683     },this);
34684     
34685 };
34686
34687 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34688
34689 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34690     /**
34691      * Creates and adds a new region if it doesn't already exist.
34692      * @param {String} target The target region key (north, south, east, west or center).
34693      * @param {Object} config The regions config object
34694      * @return {BorderLayoutRegion} The new region
34695      */
34696     addRegion : function(config)
34697     {
34698         if(!this.regions[config.region]){
34699             var r = this.factory(config);
34700             this.bindRegion(r);
34701         }
34702         return this.regions[config.region];
34703     },
34704
34705     // private (kinda)
34706     bindRegion : function(r){
34707         this.regions[r.config.region] = r;
34708         
34709         r.on("visibilitychange",    this.layout, this);
34710         r.on("paneladded",          this.layout, this);
34711         r.on("panelremoved",        this.layout, this);
34712         r.on("invalidated",         this.layout, this);
34713         r.on("resized",             this.onRegionResized, this);
34714         r.on("collapsed",           this.onRegionCollapsed, this);
34715         r.on("expanded",            this.onRegionExpanded, this);
34716     },
34717
34718     /**
34719      * Performs a layout update.
34720      */
34721     layout : function()
34722     {
34723         if(this.updating) {
34724             return;
34725         }
34726         
34727         // render all the rebions if they have not been done alreayd?
34728         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34729             if(this.regions[region] && !this.regions[region].bodyEl){
34730                 this.regions[region].onRender(this.el)
34731             }
34732         },this);
34733         
34734         var size = this.getViewSize();
34735         var w = size.width;
34736         var h = size.height;
34737         var centerW = w;
34738         var centerH = h;
34739         var centerY = 0;
34740         var centerX = 0;
34741         //var x = 0, y = 0;
34742
34743         var rs = this.regions;
34744         var north = rs["north"];
34745         var south = rs["south"]; 
34746         var west = rs["west"];
34747         var east = rs["east"];
34748         var center = rs["center"];
34749         //if(this.hideOnLayout){ // not supported anymore
34750             //c.el.setStyle("display", "none");
34751         //}
34752         if(north && north.isVisible()){
34753             var b = north.getBox();
34754             var m = north.getMargins();
34755             b.width = w - (m.left+m.right);
34756             b.x = m.left;
34757             b.y = m.top;
34758             centerY = b.height + b.y + m.bottom;
34759             centerH -= centerY;
34760             north.updateBox(this.safeBox(b));
34761         }
34762         if(south && south.isVisible()){
34763             var b = south.getBox();
34764             var m = south.getMargins();
34765             b.width = w - (m.left+m.right);
34766             b.x = m.left;
34767             var totalHeight = (b.height + m.top + m.bottom);
34768             b.y = h - totalHeight + m.top;
34769             centerH -= totalHeight;
34770             south.updateBox(this.safeBox(b));
34771         }
34772         if(west && west.isVisible()){
34773             var b = west.getBox();
34774             var m = west.getMargins();
34775             b.height = centerH - (m.top+m.bottom);
34776             b.x = m.left;
34777             b.y = centerY + m.top;
34778             var totalWidth = (b.width + m.left + m.right);
34779             centerX += totalWidth;
34780             centerW -= totalWidth;
34781             west.updateBox(this.safeBox(b));
34782         }
34783         if(east && east.isVisible()){
34784             var b = east.getBox();
34785             var m = east.getMargins();
34786             b.height = centerH - (m.top+m.bottom);
34787             var totalWidth = (b.width + m.left + m.right);
34788             b.x = w - totalWidth + m.left;
34789             b.y = centerY + m.top;
34790             centerW -= totalWidth;
34791             east.updateBox(this.safeBox(b));
34792         }
34793         if(center){
34794             var m = center.getMargins();
34795             var centerBox = {
34796                 x: centerX + m.left,
34797                 y: centerY + m.top,
34798                 width: centerW - (m.left+m.right),
34799                 height: centerH - (m.top+m.bottom)
34800             };
34801             //if(this.hideOnLayout){
34802                 //center.el.setStyle("display", "block");
34803             //}
34804             center.updateBox(this.safeBox(centerBox));
34805         }
34806         this.el.repaint();
34807         this.fireEvent("layout", this);
34808     },
34809
34810     // private
34811     safeBox : function(box){
34812         box.width = Math.max(0, box.width);
34813         box.height = Math.max(0, box.height);
34814         return box;
34815     },
34816
34817     /**
34818      * Adds a ContentPanel (or subclass) to this layout.
34819      * @param {String} target The target region key (north, south, east, west or center).
34820      * @param {Roo.ContentPanel} panel The panel to add
34821      * @return {Roo.ContentPanel} The added panel
34822      */
34823     add : function(target, panel){
34824          
34825         target = target.toLowerCase();
34826         return this.regions[target].add(panel);
34827     },
34828
34829     /**
34830      * Remove a ContentPanel (or subclass) to this layout.
34831      * @param {String} target The target region key (north, south, east, west or center).
34832      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34833      * @return {Roo.ContentPanel} The removed panel
34834      */
34835     remove : function(target, panel){
34836         target = target.toLowerCase();
34837         return this.regions[target].remove(panel);
34838     },
34839
34840     /**
34841      * Searches all regions for a panel with the specified id
34842      * @param {String} panelId
34843      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34844      */
34845     findPanel : function(panelId){
34846         var rs = this.regions;
34847         for(var target in rs){
34848             if(typeof rs[target] != "function"){
34849                 var p = rs[target].getPanel(panelId);
34850                 if(p){
34851                     return p;
34852                 }
34853             }
34854         }
34855         return null;
34856     },
34857
34858     /**
34859      * Searches all regions for a panel with the specified id and activates (shows) it.
34860      * @param {String/ContentPanel} panelId The panels id or the panel itself
34861      * @return {Roo.ContentPanel} The shown panel or null
34862      */
34863     showPanel : function(panelId) {
34864       var rs = this.regions;
34865       for(var target in rs){
34866          var r = rs[target];
34867          if(typeof r != "function"){
34868             if(r.hasPanel(panelId)){
34869                return r.showPanel(panelId);
34870             }
34871          }
34872       }
34873       return null;
34874    },
34875
34876    /**
34877      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34878      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34879      */
34880    /*
34881     restoreState : function(provider){
34882         if(!provider){
34883             provider = Roo.state.Manager;
34884         }
34885         var sm = new Roo.LayoutStateManager();
34886         sm.init(this, provider);
34887     },
34888 */
34889  
34890  
34891     /**
34892      * Adds a xtype elements to the layout.
34893      * <pre><code>
34894
34895 layout.addxtype({
34896        xtype : 'ContentPanel',
34897        region: 'west',
34898        items: [ .... ]
34899    }
34900 );
34901
34902 layout.addxtype({
34903         xtype : 'NestedLayoutPanel',
34904         region: 'west',
34905         layout: {
34906            center: { },
34907            west: { }   
34908         },
34909         items : [ ... list of content panels or nested layout panels.. ]
34910    }
34911 );
34912 </code></pre>
34913      * @param {Object} cfg Xtype definition of item to add.
34914      */
34915     addxtype : function(cfg)
34916     {
34917         // basically accepts a pannel...
34918         // can accept a layout region..!?!?
34919         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34920         
34921         
34922         // theory?  children can only be panels??
34923         
34924         //if (!cfg.xtype.match(/Panel$/)) {
34925         //    return false;
34926         //}
34927         var ret = false;
34928         
34929         if (typeof(cfg.region) == 'undefined') {
34930             Roo.log("Failed to add Panel, region was not set");
34931             Roo.log(cfg);
34932             return false;
34933         }
34934         var region = cfg.region;
34935         delete cfg.region;
34936         
34937           
34938         var xitems = [];
34939         if (cfg.items) {
34940             xitems = cfg.items;
34941             delete cfg.items;
34942         }
34943         var nb = false;
34944         
34945         switch(cfg.xtype) 
34946         {
34947             case 'Content':  // ContentPanel (el, cfg)
34948             case 'Scroll':  // ContentPanel (el, cfg)
34949             case 'View': 
34950                 cfg.autoCreate = true;
34951                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34952                 //} else {
34953                 //    var el = this.el.createChild();
34954                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34955                 //}
34956                 
34957                 this.add(region, ret);
34958                 break;
34959             
34960             /*
34961             case 'TreePanel': // our new panel!
34962                 cfg.el = this.el.createChild();
34963                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34964                 this.add(region, ret);
34965                 break;
34966             */
34967             
34968             case 'Nest': 
34969                 // create a new Layout (which is  a Border Layout...
34970                 
34971                 var clayout = cfg.layout;
34972                 clayout.el  = this.el.createChild();
34973                 clayout.items   = clayout.items  || [];
34974                 
34975                 delete cfg.layout;
34976                 
34977                 // replace this exitems with the clayout ones..
34978                 xitems = clayout.items;
34979                  
34980                 // force background off if it's in center...
34981                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34982                     cfg.background = false;
34983                 }
34984                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34985                 
34986                 
34987                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34988                 //console.log('adding nested layout panel '  + cfg.toSource());
34989                 this.add(region, ret);
34990                 nb = {}; /// find first...
34991                 break;
34992             
34993             case 'Grid':
34994                 
34995                 // needs grid and region
34996                 
34997                 //var el = this.getRegion(region).el.createChild();
34998                 /*
34999                  *var el = this.el.createChild();
35000                 // create the grid first...
35001                 cfg.grid.container = el;
35002                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35003                 */
35004                 
35005                 if (region == 'center' && this.active ) {
35006                     cfg.background = false;
35007                 }
35008                 
35009                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35010                 
35011                 this.add(region, ret);
35012                 /*
35013                 if (cfg.background) {
35014                     // render grid on panel activation (if panel background)
35015                     ret.on('activate', function(gp) {
35016                         if (!gp.grid.rendered) {
35017                     //        gp.grid.render(el);
35018                         }
35019                     });
35020                 } else {
35021                   //  cfg.grid.render(el);
35022                 }
35023                 */
35024                 break;
35025            
35026            
35027             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35028                 // it was the old xcomponent building that caused this before.
35029                 // espeically if border is the top element in the tree.
35030                 ret = this;
35031                 break; 
35032                 
35033                     
35034                 
35035                 
35036                 
35037             default:
35038                 /*
35039                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35040                     
35041                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35042                     this.add(region, ret);
35043                 } else {
35044                 */
35045                     Roo.log(cfg);
35046                     throw "Can not add '" + cfg.xtype + "' to Border";
35047                     return null;
35048              
35049                                 
35050              
35051         }
35052         this.beginUpdate();
35053         // add children..
35054         var region = '';
35055         var abn = {};
35056         Roo.each(xitems, function(i)  {
35057             region = nb && i.region ? i.region : false;
35058             
35059             var add = ret.addxtype(i);
35060            
35061             if (region) {
35062                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35063                 if (!i.background) {
35064                     abn[region] = nb[region] ;
35065                 }
35066             }
35067             
35068         });
35069         this.endUpdate();
35070
35071         // make the last non-background panel active..
35072         //if (nb) { Roo.log(abn); }
35073         if (nb) {
35074             
35075             for(var r in abn) {
35076                 region = this.getRegion(r);
35077                 if (region) {
35078                     // tried using nb[r], but it does not work..
35079                      
35080                     region.showPanel(abn[r]);
35081                    
35082                 }
35083             }
35084         }
35085         return ret;
35086         
35087     },
35088     
35089     
35090 // private
35091     factory : function(cfg)
35092     {
35093         
35094         var validRegions = Roo.bootstrap.layout.Border.regions;
35095
35096         var target = cfg.region;
35097         cfg.mgr = this;
35098         
35099         var r = Roo.bootstrap.layout;
35100         Roo.log(target);
35101         switch(target){
35102             case "north":
35103                 return new r.North(cfg);
35104             case "south":
35105                 return new r.South(cfg);
35106             case "east":
35107                 return new r.East(cfg);
35108             case "west":
35109                 return new r.West(cfg);
35110             case "center":
35111                 return new r.Center(cfg);
35112         }
35113         throw 'Layout region "'+target+'" not supported.';
35114     }
35115     
35116     
35117 });
35118  /*
35119  * Based on:
35120  * Ext JS Library 1.1.1
35121  * Copyright(c) 2006-2007, Ext JS, LLC.
35122  *
35123  * Originally Released Under LGPL - original licence link has changed is not relivant.
35124  *
35125  * Fork - LGPL
35126  * <script type="text/javascript">
35127  */
35128  
35129 /**
35130  * @class Roo.bootstrap.layout.Basic
35131  * @extends Roo.util.Observable
35132  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35133  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35134  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35135  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35136  * @cfg {string}   region  the region that it inhabits..
35137  * @cfg {bool}   skipConfig skip config?
35138  * 
35139
35140  */
35141 Roo.bootstrap.layout.Basic = function(config){
35142     
35143     this.mgr = config.mgr;
35144     
35145     this.position = config.region;
35146     
35147     var skipConfig = config.skipConfig;
35148     
35149     this.events = {
35150         /**
35151          * @scope Roo.BasicLayoutRegion
35152          */
35153         
35154         /**
35155          * @event beforeremove
35156          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35157          * @param {Roo.LayoutRegion} this
35158          * @param {Roo.ContentPanel} panel The panel
35159          * @param {Object} e The cancel event object
35160          */
35161         "beforeremove" : true,
35162         /**
35163          * @event invalidated
35164          * Fires when the layout for this region is changed.
35165          * @param {Roo.LayoutRegion} this
35166          */
35167         "invalidated" : true,
35168         /**
35169          * @event visibilitychange
35170          * Fires when this region is shown or hidden 
35171          * @param {Roo.LayoutRegion} this
35172          * @param {Boolean} visibility true or false
35173          */
35174         "visibilitychange" : true,
35175         /**
35176          * @event paneladded
35177          * Fires when a panel is added. 
35178          * @param {Roo.LayoutRegion} this
35179          * @param {Roo.ContentPanel} panel The panel
35180          */
35181         "paneladded" : true,
35182         /**
35183          * @event panelremoved
35184          * Fires when a panel is removed. 
35185          * @param {Roo.LayoutRegion} this
35186          * @param {Roo.ContentPanel} panel The panel
35187          */
35188         "panelremoved" : true,
35189         /**
35190          * @event beforecollapse
35191          * Fires when this region before collapse.
35192          * @param {Roo.LayoutRegion} this
35193          */
35194         "beforecollapse" : true,
35195         /**
35196          * @event collapsed
35197          * Fires when this region is collapsed.
35198          * @param {Roo.LayoutRegion} this
35199          */
35200         "collapsed" : true,
35201         /**
35202          * @event expanded
35203          * Fires when this region is expanded.
35204          * @param {Roo.LayoutRegion} this
35205          */
35206         "expanded" : true,
35207         /**
35208          * @event slideshow
35209          * Fires when this region is slid into view.
35210          * @param {Roo.LayoutRegion} this
35211          */
35212         "slideshow" : true,
35213         /**
35214          * @event slidehide
35215          * Fires when this region slides out of view. 
35216          * @param {Roo.LayoutRegion} this
35217          */
35218         "slidehide" : true,
35219         /**
35220          * @event panelactivated
35221          * Fires when a panel is activated. 
35222          * @param {Roo.LayoutRegion} this
35223          * @param {Roo.ContentPanel} panel The activated panel
35224          */
35225         "panelactivated" : true,
35226         /**
35227          * @event resized
35228          * Fires when the user resizes this region. 
35229          * @param {Roo.LayoutRegion} this
35230          * @param {Number} newSize The new size (width for east/west, height for north/south)
35231          */
35232         "resized" : true
35233     };
35234     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35235     this.panels = new Roo.util.MixedCollection();
35236     this.panels.getKey = this.getPanelId.createDelegate(this);
35237     this.box = null;
35238     this.activePanel = null;
35239     // ensure listeners are added...
35240     
35241     if (config.listeners || config.events) {
35242         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35243             listeners : config.listeners || {},
35244             events : config.events || {}
35245         });
35246     }
35247     
35248     if(skipConfig !== true){
35249         this.applyConfig(config);
35250     }
35251 };
35252
35253 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35254 {
35255     getPanelId : function(p){
35256         return p.getId();
35257     },
35258     
35259     applyConfig : function(config){
35260         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35261         this.config = config;
35262         
35263     },
35264     
35265     /**
35266      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35267      * the width, for horizontal (north, south) the height.
35268      * @param {Number} newSize The new width or height
35269      */
35270     resizeTo : function(newSize){
35271         var el = this.el ? this.el :
35272                  (this.activePanel ? this.activePanel.getEl() : null);
35273         if(el){
35274             switch(this.position){
35275                 case "east":
35276                 case "west":
35277                     el.setWidth(newSize);
35278                     this.fireEvent("resized", this, newSize);
35279                 break;
35280                 case "north":
35281                 case "south":
35282                     el.setHeight(newSize);
35283                     this.fireEvent("resized", this, newSize);
35284                 break;                
35285             }
35286         }
35287     },
35288     
35289     getBox : function(){
35290         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35291     },
35292     
35293     getMargins : function(){
35294         return this.margins;
35295     },
35296     
35297     updateBox : function(box){
35298         this.box = box;
35299         var el = this.activePanel.getEl();
35300         el.dom.style.left = box.x + "px";
35301         el.dom.style.top = box.y + "px";
35302         this.activePanel.setSize(box.width, box.height);
35303     },
35304     
35305     /**
35306      * Returns the container element for this region.
35307      * @return {Roo.Element}
35308      */
35309     getEl : function(){
35310         return this.activePanel;
35311     },
35312     
35313     /**
35314      * Returns true if this region is currently visible.
35315      * @return {Boolean}
35316      */
35317     isVisible : function(){
35318         return this.activePanel ? true : false;
35319     },
35320     
35321     setActivePanel : function(panel){
35322         panel = this.getPanel(panel);
35323         if(this.activePanel && this.activePanel != panel){
35324             this.activePanel.setActiveState(false);
35325             this.activePanel.getEl().setLeftTop(-10000,-10000);
35326         }
35327         this.activePanel = panel;
35328         panel.setActiveState(true);
35329         if(this.box){
35330             panel.setSize(this.box.width, this.box.height);
35331         }
35332         this.fireEvent("panelactivated", this, panel);
35333         this.fireEvent("invalidated");
35334     },
35335     
35336     /**
35337      * Show the specified panel.
35338      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35339      * @return {Roo.ContentPanel} The shown panel or null
35340      */
35341     showPanel : function(panel){
35342         panel = this.getPanel(panel);
35343         if(panel){
35344             this.setActivePanel(panel);
35345         }
35346         return panel;
35347     },
35348     
35349     /**
35350      * Get the active panel for this region.
35351      * @return {Roo.ContentPanel} The active panel or null
35352      */
35353     getActivePanel : function(){
35354         return this.activePanel;
35355     },
35356     
35357     /**
35358      * Add the passed ContentPanel(s)
35359      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35360      * @return {Roo.ContentPanel} The panel added (if only one was added)
35361      */
35362     add : function(panel){
35363         if(arguments.length > 1){
35364             for(var i = 0, len = arguments.length; i < len; i++) {
35365                 this.add(arguments[i]);
35366             }
35367             return null;
35368         }
35369         if(this.hasPanel(panel)){
35370             this.showPanel(panel);
35371             return panel;
35372         }
35373         var el = panel.getEl();
35374         if(el.dom.parentNode != this.mgr.el.dom){
35375             this.mgr.el.dom.appendChild(el.dom);
35376         }
35377         if(panel.setRegion){
35378             panel.setRegion(this);
35379         }
35380         this.panels.add(panel);
35381         el.setStyle("position", "absolute");
35382         if(!panel.background){
35383             this.setActivePanel(panel);
35384             if(this.config.initialSize && this.panels.getCount()==1){
35385                 this.resizeTo(this.config.initialSize);
35386             }
35387         }
35388         this.fireEvent("paneladded", this, panel);
35389         return panel;
35390     },
35391     
35392     /**
35393      * Returns true if the panel is in this region.
35394      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35395      * @return {Boolean}
35396      */
35397     hasPanel : function(panel){
35398         if(typeof panel == "object"){ // must be panel obj
35399             panel = panel.getId();
35400         }
35401         return this.getPanel(panel) ? true : false;
35402     },
35403     
35404     /**
35405      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35406      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35407      * @param {Boolean} preservePanel Overrides the config preservePanel option
35408      * @return {Roo.ContentPanel} The panel that was removed
35409      */
35410     remove : function(panel, preservePanel){
35411         panel = this.getPanel(panel);
35412         if(!panel){
35413             return null;
35414         }
35415         var e = {};
35416         this.fireEvent("beforeremove", this, panel, e);
35417         if(e.cancel === true){
35418             return null;
35419         }
35420         var panelId = panel.getId();
35421         this.panels.removeKey(panelId);
35422         return panel;
35423     },
35424     
35425     /**
35426      * Returns the panel specified or null if it's not in this region.
35427      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35428      * @return {Roo.ContentPanel}
35429      */
35430     getPanel : function(id){
35431         if(typeof id == "object"){ // must be panel obj
35432             return id;
35433         }
35434         return this.panels.get(id);
35435     },
35436     
35437     /**
35438      * Returns this regions position (north/south/east/west/center).
35439      * @return {String} 
35440      */
35441     getPosition: function(){
35442         return this.position;    
35443     }
35444 });/*
35445  * Based on:
35446  * Ext JS Library 1.1.1
35447  * Copyright(c) 2006-2007, Ext JS, LLC.
35448  *
35449  * Originally Released Under LGPL - original licence link has changed is not relivant.
35450  *
35451  * Fork - LGPL
35452  * <script type="text/javascript">
35453  */
35454  
35455 /**
35456  * @class Roo.bootstrap.layout.Region
35457  * @extends Roo.bootstrap.layout.Basic
35458  * This class represents a region in a layout manager.
35459  
35460  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35461  * @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})
35462  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35463  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35464  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35465  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35466  * @cfg {String}    title           The title for the region (overrides panel titles)
35467  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35468  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35469  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35470  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35471  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35472  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35473  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35474  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35475  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35476  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35477
35478  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35479  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35480  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35481  * @cfg {Number}    width           For East/West panels
35482  * @cfg {Number}    height          For North/South panels
35483  * @cfg {Boolean}   split           To show the splitter
35484  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35485  * 
35486  * @cfg {string}   cls             Extra CSS classes to add to region
35487  * 
35488  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35489  * @cfg {string}   region  the region that it inhabits..
35490  *
35491
35492  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35493  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35494
35495  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35496  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35497  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35498  */
35499 Roo.bootstrap.layout.Region = function(config)
35500 {
35501     this.applyConfig(config);
35502
35503     var mgr = config.mgr;
35504     var pos = config.region;
35505     config.skipConfig = true;
35506     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35507     
35508     if (mgr.el) {
35509         this.onRender(mgr.el);   
35510     }
35511      
35512     this.visible = true;
35513     this.collapsed = false;
35514     this.unrendered_panels = [];
35515 };
35516
35517 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35518
35519     position: '', // set by wrapper (eg. north/south etc..)
35520     unrendered_panels : null,  // unrendered panels.
35521     createBody : function(){
35522         /** This region's body element 
35523         * @type Roo.Element */
35524         this.bodyEl = this.el.createChild({
35525                 tag: "div",
35526                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35527         });
35528     },
35529
35530     onRender: function(ctr, pos)
35531     {
35532         var dh = Roo.DomHelper;
35533         /** This region's container element 
35534         * @type Roo.Element */
35535         this.el = dh.append(ctr.dom, {
35536                 tag: "div",
35537                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35538             }, true);
35539         /** This region's title element 
35540         * @type Roo.Element */
35541     
35542         this.titleEl = dh.append(this.el.dom,
35543             {
35544                     tag: "div",
35545                     unselectable: "on",
35546                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35547                     children:[
35548                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35549                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35550                     ]}, true);
35551         
35552         this.titleEl.enableDisplayMode();
35553         /** This region's title text element 
35554         * @type HTMLElement */
35555         this.titleTextEl = this.titleEl.dom.firstChild;
35556         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35557         /*
35558         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35559         this.closeBtn.enableDisplayMode();
35560         this.closeBtn.on("click", this.closeClicked, this);
35561         this.closeBtn.hide();
35562     */
35563         this.createBody(this.config);
35564         if(this.config.hideWhenEmpty){
35565             this.hide();
35566             this.on("paneladded", this.validateVisibility, this);
35567             this.on("panelremoved", this.validateVisibility, this);
35568         }
35569         if(this.autoScroll){
35570             this.bodyEl.setStyle("overflow", "auto");
35571         }else{
35572             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35573         }
35574         //if(c.titlebar !== false){
35575             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35576                 this.titleEl.hide();
35577             }else{
35578                 this.titleEl.show();
35579                 if(this.config.title){
35580                     this.titleTextEl.innerHTML = this.config.title;
35581                 }
35582             }
35583         //}
35584         if(this.config.collapsed){
35585             this.collapse(true);
35586         }
35587         if(this.config.hidden){
35588             this.hide();
35589         }
35590         
35591         if (this.unrendered_panels && this.unrendered_panels.length) {
35592             for (var i =0;i< this.unrendered_panels.length; i++) {
35593                 this.add(this.unrendered_panels[i]);
35594             }
35595             this.unrendered_panels = null;
35596             
35597         }
35598         
35599     },
35600     
35601     applyConfig : function(c)
35602     {
35603         /*
35604          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35605             var dh = Roo.DomHelper;
35606             if(c.titlebar !== false){
35607                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35608                 this.collapseBtn.on("click", this.collapse, this);
35609                 this.collapseBtn.enableDisplayMode();
35610                 /*
35611                 if(c.showPin === true || this.showPin){
35612                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35613                     this.stickBtn.enableDisplayMode();
35614                     this.stickBtn.on("click", this.expand, this);
35615                     this.stickBtn.hide();
35616                 }
35617                 
35618             }
35619             */
35620             /** This region's collapsed element
35621             * @type Roo.Element */
35622             /*
35623              *
35624             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35625                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35626             ]}, true);
35627             
35628             if(c.floatable !== false){
35629                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35630                this.collapsedEl.on("click", this.collapseClick, this);
35631             }
35632
35633             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35634                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35635                    id: "message", unselectable: "on", style:{"float":"left"}});
35636                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35637              }
35638             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35639             this.expandBtn.on("click", this.expand, this);
35640             
35641         }
35642         
35643         if(this.collapseBtn){
35644             this.collapseBtn.setVisible(c.collapsible == true);
35645         }
35646         
35647         this.cmargins = c.cmargins || this.cmargins ||
35648                          (this.position == "west" || this.position == "east" ?
35649                              {top: 0, left: 2, right:2, bottom: 0} :
35650                              {top: 2, left: 0, right:0, bottom: 2});
35651         */
35652         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35653         
35654         
35655         this.bottomTabs = c.tabPosition != "top";
35656         
35657         this.autoScroll = c.autoScroll || false;
35658         
35659         
35660        
35661         
35662         this.duration = c.duration || .30;
35663         this.slideDuration = c.slideDuration || .45;
35664         this.config = c;
35665        
35666     },
35667     /**
35668      * Returns true if this region is currently visible.
35669      * @return {Boolean}
35670      */
35671     isVisible : function(){
35672         return this.visible;
35673     },
35674
35675     /**
35676      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35677      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35678      */
35679     //setCollapsedTitle : function(title){
35680     //    title = title || "&#160;";
35681      //   if(this.collapsedTitleTextEl){
35682       //      this.collapsedTitleTextEl.innerHTML = title;
35683        // }
35684     //},
35685
35686     getBox : function(){
35687         var b;
35688       //  if(!this.collapsed){
35689             b = this.el.getBox(false, true);
35690        // }else{
35691           //  b = this.collapsedEl.getBox(false, true);
35692         //}
35693         return b;
35694     },
35695
35696     getMargins : function(){
35697         return this.margins;
35698         //return this.collapsed ? this.cmargins : this.margins;
35699     },
35700 /*
35701     highlight : function(){
35702         this.el.addClass("x-layout-panel-dragover");
35703     },
35704
35705     unhighlight : function(){
35706         this.el.removeClass("x-layout-panel-dragover");
35707     },
35708 */
35709     updateBox : function(box)
35710     {
35711         if (!this.bodyEl) {
35712             return; // not rendered yet..
35713         }
35714         
35715         this.box = box;
35716         if(!this.collapsed){
35717             this.el.dom.style.left = box.x + "px";
35718             this.el.dom.style.top = box.y + "px";
35719             this.updateBody(box.width, box.height);
35720         }else{
35721             this.collapsedEl.dom.style.left = box.x + "px";
35722             this.collapsedEl.dom.style.top = box.y + "px";
35723             this.collapsedEl.setSize(box.width, box.height);
35724         }
35725         if(this.tabs){
35726             this.tabs.autoSizeTabs();
35727         }
35728     },
35729
35730     updateBody : function(w, h)
35731     {
35732         if(w !== null){
35733             this.el.setWidth(w);
35734             w -= this.el.getBorderWidth("rl");
35735             if(this.config.adjustments){
35736                 w += this.config.adjustments[0];
35737             }
35738         }
35739         if(h !== null && h > 0){
35740             this.el.setHeight(h);
35741             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35742             h -= this.el.getBorderWidth("tb");
35743             if(this.config.adjustments){
35744                 h += this.config.adjustments[1];
35745             }
35746             this.bodyEl.setHeight(h);
35747             if(this.tabs){
35748                 h = this.tabs.syncHeight(h);
35749             }
35750         }
35751         if(this.panelSize){
35752             w = w !== null ? w : this.panelSize.width;
35753             h = h !== null ? h : this.panelSize.height;
35754         }
35755         if(this.activePanel){
35756             var el = this.activePanel.getEl();
35757             w = w !== null ? w : el.getWidth();
35758             h = h !== null ? h : el.getHeight();
35759             this.panelSize = {width: w, height: h};
35760             this.activePanel.setSize(w, h);
35761         }
35762         if(Roo.isIE && this.tabs){
35763             this.tabs.el.repaint();
35764         }
35765     },
35766
35767     /**
35768      * Returns the container element for this region.
35769      * @return {Roo.Element}
35770      */
35771     getEl : function(){
35772         return this.el;
35773     },
35774
35775     /**
35776      * Hides this region.
35777      */
35778     hide : function(){
35779         //if(!this.collapsed){
35780             this.el.dom.style.left = "-2000px";
35781             this.el.hide();
35782         //}else{
35783          //   this.collapsedEl.dom.style.left = "-2000px";
35784          //   this.collapsedEl.hide();
35785        // }
35786         this.visible = false;
35787         this.fireEvent("visibilitychange", this, false);
35788     },
35789
35790     /**
35791      * Shows this region if it was previously hidden.
35792      */
35793     show : function(){
35794         //if(!this.collapsed){
35795             this.el.show();
35796         //}else{
35797         //    this.collapsedEl.show();
35798        // }
35799         this.visible = true;
35800         this.fireEvent("visibilitychange", this, true);
35801     },
35802 /*
35803     closeClicked : function(){
35804         if(this.activePanel){
35805             this.remove(this.activePanel);
35806         }
35807     },
35808
35809     collapseClick : function(e){
35810         if(this.isSlid){
35811            e.stopPropagation();
35812            this.slideIn();
35813         }else{
35814            e.stopPropagation();
35815            this.slideOut();
35816         }
35817     },
35818 */
35819     /**
35820      * Collapses this region.
35821      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35822      */
35823     /*
35824     collapse : function(skipAnim, skipCheck = false){
35825         if(this.collapsed) {
35826             return;
35827         }
35828         
35829         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35830             
35831             this.collapsed = true;
35832             if(this.split){
35833                 this.split.el.hide();
35834             }
35835             if(this.config.animate && skipAnim !== true){
35836                 this.fireEvent("invalidated", this);
35837                 this.animateCollapse();
35838             }else{
35839                 this.el.setLocation(-20000,-20000);
35840                 this.el.hide();
35841                 this.collapsedEl.show();
35842                 this.fireEvent("collapsed", this);
35843                 this.fireEvent("invalidated", this);
35844             }
35845         }
35846         
35847     },
35848 */
35849     animateCollapse : function(){
35850         // overridden
35851     },
35852
35853     /**
35854      * Expands this region if it was previously collapsed.
35855      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35856      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35857      */
35858     /*
35859     expand : function(e, skipAnim){
35860         if(e) {
35861             e.stopPropagation();
35862         }
35863         if(!this.collapsed || this.el.hasActiveFx()) {
35864             return;
35865         }
35866         if(this.isSlid){
35867             this.afterSlideIn();
35868             skipAnim = true;
35869         }
35870         this.collapsed = false;
35871         if(this.config.animate && skipAnim !== true){
35872             this.animateExpand();
35873         }else{
35874             this.el.show();
35875             if(this.split){
35876                 this.split.el.show();
35877             }
35878             this.collapsedEl.setLocation(-2000,-2000);
35879             this.collapsedEl.hide();
35880             this.fireEvent("invalidated", this);
35881             this.fireEvent("expanded", this);
35882         }
35883     },
35884 */
35885     animateExpand : function(){
35886         // overridden
35887     },
35888
35889     initTabs : function()
35890     {
35891         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35892         
35893         var ts = new Roo.bootstrap.panel.Tabs({
35894                 el: this.bodyEl.dom,
35895                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35896                 disableTooltips: this.config.disableTabTips,
35897                 toolbar : this.config.toolbar
35898             });
35899         
35900         if(this.config.hideTabs){
35901             ts.stripWrap.setDisplayed(false);
35902         }
35903         this.tabs = ts;
35904         ts.resizeTabs = this.config.resizeTabs === true;
35905         ts.minTabWidth = this.config.minTabWidth || 40;
35906         ts.maxTabWidth = this.config.maxTabWidth || 250;
35907         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35908         ts.monitorResize = false;
35909         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35910         ts.bodyEl.addClass('roo-layout-tabs-body');
35911         this.panels.each(this.initPanelAsTab, this);
35912     },
35913
35914     initPanelAsTab : function(panel){
35915         var ti = this.tabs.addTab(
35916             panel.getEl().id,
35917             panel.getTitle(),
35918             null,
35919             this.config.closeOnTab && panel.isClosable(),
35920             panel.tpl
35921         );
35922         if(panel.tabTip !== undefined){
35923             ti.setTooltip(panel.tabTip);
35924         }
35925         ti.on("activate", function(){
35926               this.setActivePanel(panel);
35927         }, this);
35928         
35929         if(this.config.closeOnTab){
35930             ti.on("beforeclose", function(t, e){
35931                 e.cancel = true;
35932                 this.remove(panel);
35933             }, this);
35934         }
35935         
35936         panel.tabItem = ti;
35937         
35938         return ti;
35939     },
35940
35941     updatePanelTitle : function(panel, title)
35942     {
35943         if(this.activePanel == panel){
35944             this.updateTitle(title);
35945         }
35946         if(this.tabs){
35947             var ti = this.tabs.getTab(panel.getEl().id);
35948             ti.setText(title);
35949             if(panel.tabTip !== undefined){
35950                 ti.setTooltip(panel.tabTip);
35951             }
35952         }
35953     },
35954
35955     updateTitle : function(title){
35956         if(this.titleTextEl && !this.config.title){
35957             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35958         }
35959     },
35960
35961     setActivePanel : function(panel)
35962     {
35963         panel = this.getPanel(panel);
35964         if(this.activePanel && this.activePanel != panel){
35965             if(this.activePanel.setActiveState(false) === false){
35966                 return;
35967             }
35968         }
35969         this.activePanel = panel;
35970         panel.setActiveState(true);
35971         if(this.panelSize){
35972             panel.setSize(this.panelSize.width, this.panelSize.height);
35973         }
35974         if(this.closeBtn){
35975             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35976         }
35977         this.updateTitle(panel.getTitle());
35978         if(this.tabs){
35979             this.fireEvent("invalidated", this);
35980         }
35981         this.fireEvent("panelactivated", this, panel);
35982     },
35983
35984     /**
35985      * Shows the specified panel.
35986      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35987      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35988      */
35989     showPanel : function(panel)
35990     {
35991         panel = this.getPanel(panel);
35992         if(panel){
35993             if(this.tabs){
35994                 var tab = this.tabs.getTab(panel.getEl().id);
35995                 if(tab.isHidden()){
35996                     this.tabs.unhideTab(tab.id);
35997                 }
35998                 tab.activate();
35999             }else{
36000                 this.setActivePanel(panel);
36001             }
36002         }
36003         return panel;
36004     },
36005
36006     /**
36007      * Get the active panel for this region.
36008      * @return {Roo.ContentPanel} The active panel or null
36009      */
36010     getActivePanel : function(){
36011         return this.activePanel;
36012     },
36013
36014     validateVisibility : function(){
36015         if(this.panels.getCount() < 1){
36016             this.updateTitle("&#160;");
36017             this.closeBtn.hide();
36018             this.hide();
36019         }else{
36020             if(!this.isVisible()){
36021                 this.show();
36022             }
36023         }
36024     },
36025
36026     /**
36027      * Adds the passed ContentPanel(s) to this region.
36028      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36029      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36030      */
36031     add : function(panel)
36032     {
36033         if(arguments.length > 1){
36034             for(var i = 0, len = arguments.length; i < len; i++) {
36035                 this.add(arguments[i]);
36036             }
36037             return null;
36038         }
36039         
36040         // if we have not been rendered yet, then we can not really do much of this..
36041         if (!this.bodyEl) {
36042             this.unrendered_panels.push(panel);
36043             return panel;
36044         }
36045         
36046         
36047         
36048         
36049         if(this.hasPanel(panel)){
36050             this.showPanel(panel);
36051             return panel;
36052         }
36053         panel.setRegion(this);
36054         this.panels.add(panel);
36055        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36056             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36057             // and hide them... ???
36058             this.bodyEl.dom.appendChild(panel.getEl().dom);
36059             if(panel.background !== true){
36060                 this.setActivePanel(panel);
36061             }
36062             this.fireEvent("paneladded", this, panel);
36063             return panel;
36064         }
36065         */
36066         if(!this.tabs){
36067             this.initTabs();
36068         }else{
36069             this.initPanelAsTab(panel);
36070         }
36071         
36072         
36073         if(panel.background !== true){
36074             this.tabs.activate(panel.getEl().id);
36075         }
36076         this.fireEvent("paneladded", this, panel);
36077         return panel;
36078     },
36079
36080     /**
36081      * Hides the tab for the specified panel.
36082      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36083      */
36084     hidePanel : function(panel){
36085         if(this.tabs && (panel = this.getPanel(panel))){
36086             this.tabs.hideTab(panel.getEl().id);
36087         }
36088     },
36089
36090     /**
36091      * Unhides the tab for a previously hidden panel.
36092      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36093      */
36094     unhidePanel : function(panel){
36095         if(this.tabs && (panel = this.getPanel(panel))){
36096             this.tabs.unhideTab(panel.getEl().id);
36097         }
36098     },
36099
36100     clearPanels : function(){
36101         while(this.panels.getCount() > 0){
36102              this.remove(this.panels.first());
36103         }
36104     },
36105
36106     /**
36107      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36108      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36109      * @param {Boolean} preservePanel Overrides the config preservePanel option
36110      * @return {Roo.ContentPanel} The panel that was removed
36111      */
36112     remove : function(panel, preservePanel)
36113     {
36114         panel = this.getPanel(panel);
36115         if(!panel){
36116             return null;
36117         }
36118         var e = {};
36119         this.fireEvent("beforeremove", this, panel, e);
36120         if(e.cancel === true){
36121             return null;
36122         }
36123         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36124         var panelId = panel.getId();
36125         this.panels.removeKey(panelId);
36126         if(preservePanel){
36127             document.body.appendChild(panel.getEl().dom);
36128         }
36129         if(this.tabs){
36130             this.tabs.removeTab(panel.getEl().id);
36131         }else if (!preservePanel){
36132             this.bodyEl.dom.removeChild(panel.getEl().dom);
36133         }
36134         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36135             var p = this.panels.first();
36136             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36137             tempEl.appendChild(p.getEl().dom);
36138             this.bodyEl.update("");
36139             this.bodyEl.dom.appendChild(p.getEl().dom);
36140             tempEl = null;
36141             this.updateTitle(p.getTitle());
36142             this.tabs = null;
36143             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36144             this.setActivePanel(p);
36145         }
36146         panel.setRegion(null);
36147         if(this.activePanel == panel){
36148             this.activePanel = null;
36149         }
36150         if(this.config.autoDestroy !== false && preservePanel !== true){
36151             try{panel.destroy();}catch(e){}
36152         }
36153         this.fireEvent("panelremoved", this, panel);
36154         return panel;
36155     },
36156
36157     /**
36158      * Returns the TabPanel component used by this region
36159      * @return {Roo.TabPanel}
36160      */
36161     getTabs : function(){
36162         return this.tabs;
36163     },
36164
36165     createTool : function(parentEl, className){
36166         var btn = Roo.DomHelper.append(parentEl, {
36167             tag: "div",
36168             cls: "x-layout-tools-button",
36169             children: [ {
36170                 tag: "div",
36171                 cls: "roo-layout-tools-button-inner " + className,
36172                 html: "&#160;"
36173             }]
36174         }, true);
36175         btn.addClassOnOver("roo-layout-tools-button-over");
36176         return btn;
36177     }
36178 });/*
36179  * Based on:
36180  * Ext JS Library 1.1.1
36181  * Copyright(c) 2006-2007, Ext JS, LLC.
36182  *
36183  * Originally Released Under LGPL - original licence link has changed is not relivant.
36184  *
36185  * Fork - LGPL
36186  * <script type="text/javascript">
36187  */
36188  
36189
36190
36191 /**
36192  * @class Roo.SplitLayoutRegion
36193  * @extends Roo.LayoutRegion
36194  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36195  */
36196 Roo.bootstrap.layout.Split = function(config){
36197     this.cursor = config.cursor;
36198     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36199 };
36200
36201 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36202 {
36203     splitTip : "Drag to resize.",
36204     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36205     useSplitTips : false,
36206
36207     applyConfig : function(config){
36208         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36209     },
36210     
36211     onRender : function(ctr,pos) {
36212         
36213         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36214         if(!this.config.split){
36215             return;
36216         }
36217         if(!this.split){
36218             
36219             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36220                             tag: "div",
36221                             id: this.el.id + "-split",
36222                             cls: "roo-layout-split roo-layout-split-"+this.position,
36223                             html: "&#160;"
36224             });
36225             /** The SplitBar for this region 
36226             * @type Roo.SplitBar */
36227             // does not exist yet...
36228             Roo.log([this.position, this.orientation]);
36229             
36230             this.split = new Roo.bootstrap.SplitBar({
36231                 dragElement : splitEl,
36232                 resizingElement: this.el,
36233                 orientation : this.orientation
36234             });
36235             
36236             this.split.on("moved", this.onSplitMove, this);
36237             this.split.useShim = this.config.useShim === true;
36238             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36239             if(this.useSplitTips){
36240                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36241             }
36242             //if(config.collapsible){
36243             //    this.split.el.on("dblclick", this.collapse,  this);
36244             //}
36245         }
36246         if(typeof this.config.minSize != "undefined"){
36247             this.split.minSize = this.config.minSize;
36248         }
36249         if(typeof this.config.maxSize != "undefined"){
36250             this.split.maxSize = this.config.maxSize;
36251         }
36252         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36253             this.hideSplitter();
36254         }
36255         
36256     },
36257
36258     getHMaxSize : function(){
36259          var cmax = this.config.maxSize || 10000;
36260          var center = this.mgr.getRegion("center");
36261          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36262     },
36263
36264     getVMaxSize : function(){
36265          var cmax = this.config.maxSize || 10000;
36266          var center = this.mgr.getRegion("center");
36267          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36268     },
36269
36270     onSplitMove : function(split, newSize){
36271         this.fireEvent("resized", this, newSize);
36272     },
36273     
36274     /** 
36275      * Returns the {@link Roo.SplitBar} for this region.
36276      * @return {Roo.SplitBar}
36277      */
36278     getSplitBar : function(){
36279         return this.split;
36280     },
36281     
36282     hide : function(){
36283         this.hideSplitter();
36284         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36285     },
36286
36287     hideSplitter : function(){
36288         if(this.split){
36289             this.split.el.setLocation(-2000,-2000);
36290             this.split.el.hide();
36291         }
36292     },
36293
36294     show : function(){
36295         if(this.split){
36296             this.split.el.show();
36297         }
36298         Roo.bootstrap.layout.Split.superclass.show.call(this);
36299     },
36300     
36301     beforeSlide: function(){
36302         if(Roo.isGecko){// firefox overflow auto bug workaround
36303             this.bodyEl.clip();
36304             if(this.tabs) {
36305                 this.tabs.bodyEl.clip();
36306             }
36307             if(this.activePanel){
36308                 this.activePanel.getEl().clip();
36309                 
36310                 if(this.activePanel.beforeSlide){
36311                     this.activePanel.beforeSlide();
36312                 }
36313             }
36314         }
36315     },
36316     
36317     afterSlide : function(){
36318         if(Roo.isGecko){// firefox overflow auto bug workaround
36319             this.bodyEl.unclip();
36320             if(this.tabs) {
36321                 this.tabs.bodyEl.unclip();
36322             }
36323             if(this.activePanel){
36324                 this.activePanel.getEl().unclip();
36325                 if(this.activePanel.afterSlide){
36326                     this.activePanel.afterSlide();
36327                 }
36328             }
36329         }
36330     },
36331
36332     initAutoHide : function(){
36333         if(this.autoHide !== false){
36334             if(!this.autoHideHd){
36335                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36336                 this.autoHideHd = {
36337                     "mouseout": function(e){
36338                         if(!e.within(this.el, true)){
36339                             st.delay(500);
36340                         }
36341                     },
36342                     "mouseover" : function(e){
36343                         st.cancel();
36344                     },
36345                     scope : this
36346                 };
36347             }
36348             this.el.on(this.autoHideHd);
36349         }
36350     },
36351
36352     clearAutoHide : function(){
36353         if(this.autoHide !== false){
36354             this.el.un("mouseout", this.autoHideHd.mouseout);
36355             this.el.un("mouseover", this.autoHideHd.mouseover);
36356         }
36357     },
36358
36359     clearMonitor : function(){
36360         Roo.get(document).un("click", this.slideInIf, this);
36361     },
36362
36363     // these names are backwards but not changed for compat
36364     slideOut : function(){
36365         if(this.isSlid || this.el.hasActiveFx()){
36366             return;
36367         }
36368         this.isSlid = true;
36369         if(this.collapseBtn){
36370             this.collapseBtn.hide();
36371         }
36372         this.closeBtnState = this.closeBtn.getStyle('display');
36373         this.closeBtn.hide();
36374         if(this.stickBtn){
36375             this.stickBtn.show();
36376         }
36377         this.el.show();
36378         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36379         this.beforeSlide();
36380         this.el.setStyle("z-index", 10001);
36381         this.el.slideIn(this.getSlideAnchor(), {
36382             callback: function(){
36383                 this.afterSlide();
36384                 this.initAutoHide();
36385                 Roo.get(document).on("click", this.slideInIf, this);
36386                 this.fireEvent("slideshow", this);
36387             },
36388             scope: this,
36389             block: true
36390         });
36391     },
36392
36393     afterSlideIn : function(){
36394         this.clearAutoHide();
36395         this.isSlid = false;
36396         this.clearMonitor();
36397         this.el.setStyle("z-index", "");
36398         if(this.collapseBtn){
36399             this.collapseBtn.show();
36400         }
36401         this.closeBtn.setStyle('display', this.closeBtnState);
36402         if(this.stickBtn){
36403             this.stickBtn.hide();
36404         }
36405         this.fireEvent("slidehide", this);
36406     },
36407
36408     slideIn : function(cb){
36409         if(!this.isSlid || this.el.hasActiveFx()){
36410             Roo.callback(cb);
36411             return;
36412         }
36413         this.isSlid = false;
36414         this.beforeSlide();
36415         this.el.slideOut(this.getSlideAnchor(), {
36416             callback: function(){
36417                 this.el.setLeftTop(-10000, -10000);
36418                 this.afterSlide();
36419                 this.afterSlideIn();
36420                 Roo.callback(cb);
36421             },
36422             scope: this,
36423             block: true
36424         });
36425     },
36426     
36427     slideInIf : function(e){
36428         if(!e.within(this.el)){
36429             this.slideIn();
36430         }
36431     },
36432
36433     animateCollapse : function(){
36434         this.beforeSlide();
36435         this.el.setStyle("z-index", 20000);
36436         var anchor = this.getSlideAnchor();
36437         this.el.slideOut(anchor, {
36438             callback : function(){
36439                 this.el.setStyle("z-index", "");
36440                 this.collapsedEl.slideIn(anchor, {duration:.3});
36441                 this.afterSlide();
36442                 this.el.setLocation(-10000,-10000);
36443                 this.el.hide();
36444                 this.fireEvent("collapsed", this);
36445             },
36446             scope: this,
36447             block: true
36448         });
36449     },
36450
36451     animateExpand : function(){
36452         this.beforeSlide();
36453         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36454         this.el.setStyle("z-index", 20000);
36455         this.collapsedEl.hide({
36456             duration:.1
36457         });
36458         this.el.slideIn(this.getSlideAnchor(), {
36459             callback : function(){
36460                 this.el.setStyle("z-index", "");
36461                 this.afterSlide();
36462                 if(this.split){
36463                     this.split.el.show();
36464                 }
36465                 this.fireEvent("invalidated", this);
36466                 this.fireEvent("expanded", this);
36467             },
36468             scope: this,
36469             block: true
36470         });
36471     },
36472
36473     anchors : {
36474         "west" : "left",
36475         "east" : "right",
36476         "north" : "top",
36477         "south" : "bottom"
36478     },
36479
36480     sanchors : {
36481         "west" : "l",
36482         "east" : "r",
36483         "north" : "t",
36484         "south" : "b"
36485     },
36486
36487     canchors : {
36488         "west" : "tl-tr",
36489         "east" : "tr-tl",
36490         "north" : "tl-bl",
36491         "south" : "bl-tl"
36492     },
36493
36494     getAnchor : function(){
36495         return this.anchors[this.position];
36496     },
36497
36498     getCollapseAnchor : function(){
36499         return this.canchors[this.position];
36500     },
36501
36502     getSlideAnchor : function(){
36503         return this.sanchors[this.position];
36504     },
36505
36506     getAlignAdj : function(){
36507         var cm = this.cmargins;
36508         switch(this.position){
36509             case "west":
36510                 return [0, 0];
36511             break;
36512             case "east":
36513                 return [0, 0];
36514             break;
36515             case "north":
36516                 return [0, 0];
36517             break;
36518             case "south":
36519                 return [0, 0];
36520             break;
36521         }
36522     },
36523
36524     getExpandAdj : function(){
36525         var c = this.collapsedEl, cm = this.cmargins;
36526         switch(this.position){
36527             case "west":
36528                 return [-(cm.right+c.getWidth()+cm.left), 0];
36529             break;
36530             case "east":
36531                 return [cm.right+c.getWidth()+cm.left, 0];
36532             break;
36533             case "north":
36534                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36535             break;
36536             case "south":
36537                 return [0, cm.top+cm.bottom+c.getHeight()];
36538             break;
36539         }
36540     }
36541 });/*
36542  * Based on:
36543  * Ext JS Library 1.1.1
36544  * Copyright(c) 2006-2007, Ext JS, LLC.
36545  *
36546  * Originally Released Under LGPL - original licence link has changed is not relivant.
36547  *
36548  * Fork - LGPL
36549  * <script type="text/javascript">
36550  */
36551 /*
36552  * These classes are private internal classes
36553  */
36554 Roo.bootstrap.layout.Center = function(config){
36555     config.region = "center";
36556     Roo.bootstrap.layout.Region.call(this, config);
36557     this.visible = true;
36558     this.minWidth = config.minWidth || 20;
36559     this.minHeight = config.minHeight || 20;
36560 };
36561
36562 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36563     hide : function(){
36564         // center panel can't be hidden
36565     },
36566     
36567     show : function(){
36568         // center panel can't be hidden
36569     },
36570     
36571     getMinWidth: function(){
36572         return this.minWidth;
36573     },
36574     
36575     getMinHeight: function(){
36576         return this.minHeight;
36577     }
36578 });
36579
36580
36581
36582
36583  
36584
36585
36586
36587
36588
36589 Roo.bootstrap.layout.North = function(config)
36590 {
36591     config.region = 'north';
36592     config.cursor = 'n-resize';
36593     
36594     Roo.bootstrap.layout.Split.call(this, config);
36595     
36596     
36597     if(this.split){
36598         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36599         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36600         this.split.el.addClass("roo-layout-split-v");
36601     }
36602     var size = config.initialSize || config.height;
36603     if(typeof size != "undefined"){
36604         this.el.setHeight(size);
36605     }
36606 };
36607 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36608 {
36609     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36610     
36611     
36612     
36613     getBox : function(){
36614         if(this.collapsed){
36615             return this.collapsedEl.getBox();
36616         }
36617         var box = this.el.getBox();
36618         if(this.split){
36619             box.height += this.split.el.getHeight();
36620         }
36621         return box;
36622     },
36623     
36624     updateBox : function(box){
36625         if(this.split && !this.collapsed){
36626             box.height -= this.split.el.getHeight();
36627             this.split.el.setLeft(box.x);
36628             this.split.el.setTop(box.y+box.height);
36629             this.split.el.setWidth(box.width);
36630         }
36631         if(this.collapsed){
36632             this.updateBody(box.width, null);
36633         }
36634         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36635     }
36636 });
36637
36638
36639
36640
36641
36642 Roo.bootstrap.layout.South = function(config){
36643     config.region = 'south';
36644     config.cursor = 's-resize';
36645     Roo.bootstrap.layout.Split.call(this, config);
36646     if(this.split){
36647         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36648         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36649         this.split.el.addClass("roo-layout-split-v");
36650     }
36651     var size = config.initialSize || config.height;
36652     if(typeof size != "undefined"){
36653         this.el.setHeight(size);
36654     }
36655 };
36656
36657 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36658     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36659     getBox : function(){
36660         if(this.collapsed){
36661             return this.collapsedEl.getBox();
36662         }
36663         var box = this.el.getBox();
36664         if(this.split){
36665             var sh = this.split.el.getHeight();
36666             box.height += sh;
36667             box.y -= sh;
36668         }
36669         return box;
36670     },
36671     
36672     updateBox : function(box){
36673         if(this.split && !this.collapsed){
36674             var sh = this.split.el.getHeight();
36675             box.height -= sh;
36676             box.y += sh;
36677             this.split.el.setLeft(box.x);
36678             this.split.el.setTop(box.y-sh);
36679             this.split.el.setWidth(box.width);
36680         }
36681         if(this.collapsed){
36682             this.updateBody(box.width, null);
36683         }
36684         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36685     }
36686 });
36687
36688 Roo.bootstrap.layout.East = function(config){
36689     config.region = "east";
36690     config.cursor = "e-resize";
36691     Roo.bootstrap.layout.Split.call(this, config);
36692     if(this.split){
36693         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36694         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36695         this.split.el.addClass("roo-layout-split-h");
36696     }
36697     var size = config.initialSize || config.width;
36698     if(typeof size != "undefined"){
36699         this.el.setWidth(size);
36700     }
36701 };
36702 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36703     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36704     getBox : function(){
36705         if(this.collapsed){
36706             return this.collapsedEl.getBox();
36707         }
36708         var box = this.el.getBox();
36709         if(this.split){
36710             var sw = this.split.el.getWidth();
36711             box.width += sw;
36712             box.x -= sw;
36713         }
36714         return box;
36715     },
36716
36717     updateBox : function(box){
36718         if(this.split && !this.collapsed){
36719             var sw = this.split.el.getWidth();
36720             box.width -= sw;
36721             this.split.el.setLeft(box.x);
36722             this.split.el.setTop(box.y);
36723             this.split.el.setHeight(box.height);
36724             box.x += sw;
36725         }
36726         if(this.collapsed){
36727             this.updateBody(null, box.height);
36728         }
36729         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36730     }
36731 });
36732
36733 Roo.bootstrap.layout.West = function(config){
36734     config.region = "west";
36735     config.cursor = "w-resize";
36736     
36737     Roo.bootstrap.layout.Split.call(this, config);
36738     if(this.split){
36739         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36740         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36741         this.split.el.addClass("roo-layout-split-h");
36742     }
36743     
36744 };
36745 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36746     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36747     
36748     onRender: function(ctr, pos)
36749     {
36750         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36751         var size = this.config.initialSize || this.config.width;
36752         if(typeof size != "undefined"){
36753             this.el.setWidth(size);
36754         }
36755     },
36756     
36757     getBox : function(){
36758         if(this.collapsed){
36759             return this.collapsedEl.getBox();
36760         }
36761         var box = this.el.getBox();
36762         if(this.split){
36763             box.width += this.split.el.getWidth();
36764         }
36765         return box;
36766     },
36767     
36768     updateBox : function(box){
36769         if(this.split && !this.collapsed){
36770             var sw = this.split.el.getWidth();
36771             box.width -= sw;
36772             this.split.el.setLeft(box.x+box.width);
36773             this.split.el.setTop(box.y);
36774             this.split.el.setHeight(box.height);
36775         }
36776         if(this.collapsed){
36777             this.updateBody(null, box.height);
36778         }
36779         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36780     }
36781 });
36782 Roo.namespace("Roo.bootstrap.panel");/*
36783  * Based on:
36784  * Ext JS Library 1.1.1
36785  * Copyright(c) 2006-2007, Ext JS, LLC.
36786  *
36787  * Originally Released Under LGPL - original licence link has changed is not relivant.
36788  *
36789  * Fork - LGPL
36790  * <script type="text/javascript">
36791  */
36792 /**
36793  * @class Roo.ContentPanel
36794  * @extends Roo.util.Observable
36795  * A basic ContentPanel element.
36796  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36797  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36798  * @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
36799  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36800  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36801  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36802  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36803  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36804  * @cfg {String} title          The title for this panel
36805  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36806  * @cfg {String} url            Calls {@link #setUrl} with this value
36807  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36808  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36809  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36810  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36811  * @cfg {Boolean} badges render the badges
36812
36813  * @constructor
36814  * Create a new ContentPanel.
36815  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36816  * @param {String/Object} config A string to set only the title or a config object
36817  * @param {String} content (optional) Set the HTML content for this panel
36818  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36819  */
36820 Roo.bootstrap.panel.Content = function( config){
36821     
36822     this.tpl = config.tpl || false;
36823     
36824     var el = config.el;
36825     var content = config.content;
36826
36827     if(config.autoCreate){ // xtype is available if this is called from factory
36828         el = Roo.id();
36829     }
36830     this.el = Roo.get(el);
36831     if(!this.el && config && config.autoCreate){
36832         if(typeof config.autoCreate == "object"){
36833             if(!config.autoCreate.id){
36834                 config.autoCreate.id = config.id||el;
36835             }
36836             this.el = Roo.DomHelper.append(document.body,
36837                         config.autoCreate, true);
36838         }else{
36839             var elcfg =  {   tag: "div",
36840                             cls: "roo-layout-inactive-content",
36841                             id: config.id||el
36842                             };
36843             if (config.html) {
36844                 elcfg.html = config.html;
36845                 
36846             }
36847                         
36848             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36849         }
36850     } 
36851     this.closable = false;
36852     this.loaded = false;
36853     this.active = false;
36854    
36855       
36856     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36857         
36858         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36859         
36860         this.wrapEl = this.el; //this.el.wrap();
36861         var ti = [];
36862         if (config.toolbar.items) {
36863             ti = config.toolbar.items ;
36864             delete config.toolbar.items ;
36865         }
36866         
36867         var nitems = [];
36868         this.toolbar.render(this.wrapEl, 'before');
36869         for(var i =0;i < ti.length;i++) {
36870           //  Roo.log(['add child', items[i]]);
36871             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36872         }
36873         this.toolbar.items = nitems;
36874         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36875         delete config.toolbar;
36876         
36877     }
36878     /*
36879     // xtype created footer. - not sure if will work as we normally have to render first..
36880     if (this.footer && !this.footer.el && this.footer.xtype) {
36881         if (!this.wrapEl) {
36882             this.wrapEl = this.el.wrap();
36883         }
36884     
36885         this.footer.container = this.wrapEl.createChild();
36886          
36887         this.footer = Roo.factory(this.footer, Roo);
36888         
36889     }
36890     */
36891     
36892      if(typeof config == "string"){
36893         this.title = config;
36894     }else{
36895         Roo.apply(this, config);
36896     }
36897     
36898     if(this.resizeEl){
36899         this.resizeEl = Roo.get(this.resizeEl, true);
36900     }else{
36901         this.resizeEl = this.el;
36902     }
36903     // handle view.xtype
36904     
36905  
36906     
36907     
36908     this.addEvents({
36909         /**
36910          * @event activate
36911          * Fires when this panel is activated. 
36912          * @param {Roo.ContentPanel} this
36913          */
36914         "activate" : true,
36915         /**
36916          * @event deactivate
36917          * Fires when this panel is activated. 
36918          * @param {Roo.ContentPanel} this
36919          */
36920         "deactivate" : true,
36921
36922         /**
36923          * @event resize
36924          * Fires when this panel is resized if fitToFrame is true.
36925          * @param {Roo.ContentPanel} this
36926          * @param {Number} width The width after any component adjustments
36927          * @param {Number} height The height after any component adjustments
36928          */
36929         "resize" : true,
36930         
36931          /**
36932          * @event render
36933          * Fires when this tab is created
36934          * @param {Roo.ContentPanel} this
36935          */
36936         "render" : true
36937         
36938         
36939         
36940     });
36941     
36942
36943     
36944     
36945     if(this.autoScroll){
36946         this.resizeEl.setStyle("overflow", "auto");
36947     } else {
36948         // fix randome scrolling
36949         //this.el.on('scroll', function() {
36950         //    Roo.log('fix random scolling');
36951         //    this.scrollTo('top',0); 
36952         //});
36953     }
36954     content = content || this.content;
36955     if(content){
36956         this.setContent(content);
36957     }
36958     if(config && config.url){
36959         this.setUrl(this.url, this.params, this.loadOnce);
36960     }
36961     
36962     
36963     
36964     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36965     
36966     if (this.view && typeof(this.view.xtype) != 'undefined') {
36967         this.view.el = this.el.appendChild(document.createElement("div"));
36968         this.view = Roo.factory(this.view); 
36969         this.view.render  &&  this.view.render(false, '');  
36970     }
36971     
36972     
36973     this.fireEvent('render', this);
36974 };
36975
36976 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36977     
36978     tabTip : '',
36979     
36980     setRegion : function(region){
36981         this.region = region;
36982         this.setActiveClass(region && !this.background);
36983     },
36984     
36985     
36986     setActiveClass: function(state)
36987     {
36988         if(state){
36989            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36990            this.el.setStyle('position','relative');
36991         }else{
36992            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36993            this.el.setStyle('position', 'absolute');
36994         } 
36995     },
36996     
36997     /**
36998      * Returns the toolbar for this Panel if one was configured. 
36999      * @return {Roo.Toolbar} 
37000      */
37001     getToolbar : function(){
37002         return this.toolbar;
37003     },
37004     
37005     setActiveState : function(active)
37006     {
37007         this.active = active;
37008         this.setActiveClass(active);
37009         if(!active){
37010             if(this.fireEvent("deactivate", this) === false){
37011                 return false;
37012             }
37013             return true;
37014         }
37015         this.fireEvent("activate", this);
37016         return true;
37017     },
37018     /**
37019      * Updates this panel's element
37020      * @param {String} content The new content
37021      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37022     */
37023     setContent : function(content, loadScripts){
37024         this.el.update(content, loadScripts);
37025     },
37026
37027     ignoreResize : function(w, h){
37028         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37029             return true;
37030         }else{
37031             this.lastSize = {width: w, height: h};
37032             return false;
37033         }
37034     },
37035     /**
37036      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37037      * @return {Roo.UpdateManager} The UpdateManager
37038      */
37039     getUpdateManager : function(){
37040         return this.el.getUpdateManager();
37041     },
37042      /**
37043      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37044      * @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:
37045 <pre><code>
37046 panel.load({
37047     url: "your-url.php",
37048     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37049     callback: yourFunction,
37050     scope: yourObject, //(optional scope)
37051     discardUrl: false,
37052     nocache: false,
37053     text: "Loading...",
37054     timeout: 30,
37055     scripts: false
37056 });
37057 </code></pre>
37058      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37059      * 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.
37060      * @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}
37061      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37062      * @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.
37063      * @return {Roo.ContentPanel} this
37064      */
37065     load : function(){
37066         var um = this.el.getUpdateManager();
37067         um.update.apply(um, arguments);
37068         return this;
37069     },
37070
37071
37072     /**
37073      * 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.
37074      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37075      * @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)
37076      * @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)
37077      * @return {Roo.UpdateManager} The UpdateManager
37078      */
37079     setUrl : function(url, params, loadOnce){
37080         if(this.refreshDelegate){
37081             this.removeListener("activate", this.refreshDelegate);
37082         }
37083         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37084         this.on("activate", this.refreshDelegate);
37085         return this.el.getUpdateManager();
37086     },
37087     
37088     _handleRefresh : function(url, params, loadOnce){
37089         if(!loadOnce || !this.loaded){
37090             var updater = this.el.getUpdateManager();
37091             updater.update(url, params, this._setLoaded.createDelegate(this));
37092         }
37093     },
37094     
37095     _setLoaded : function(){
37096         this.loaded = true;
37097     }, 
37098     
37099     /**
37100      * Returns this panel's id
37101      * @return {String} 
37102      */
37103     getId : function(){
37104         return this.el.id;
37105     },
37106     
37107     /** 
37108      * Returns this panel's element - used by regiosn to add.
37109      * @return {Roo.Element} 
37110      */
37111     getEl : function(){
37112         return this.wrapEl || this.el;
37113     },
37114     
37115    
37116     
37117     adjustForComponents : function(width, height)
37118     {
37119         //Roo.log('adjustForComponents ');
37120         if(this.resizeEl != this.el){
37121             width -= this.el.getFrameWidth('lr');
37122             height -= this.el.getFrameWidth('tb');
37123         }
37124         if(this.toolbar){
37125             var te = this.toolbar.getEl();
37126             te.setWidth(width);
37127             height -= te.getHeight();
37128         }
37129         if(this.footer){
37130             var te = this.footer.getEl();
37131             te.setWidth(width);
37132             height -= te.getHeight();
37133         }
37134         
37135         
37136         if(this.adjustments){
37137             width += this.adjustments[0];
37138             height += this.adjustments[1];
37139         }
37140         return {"width": width, "height": height};
37141     },
37142     
37143     setSize : function(width, height){
37144         if(this.fitToFrame && !this.ignoreResize(width, height)){
37145             if(this.fitContainer && this.resizeEl != this.el){
37146                 this.el.setSize(width, height);
37147             }
37148             var size = this.adjustForComponents(width, height);
37149             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37150             this.fireEvent('resize', this, size.width, size.height);
37151         }
37152     },
37153     
37154     /**
37155      * Returns this panel's title
37156      * @return {String} 
37157      */
37158     getTitle : function(){
37159         
37160         if (typeof(this.title) != 'object') {
37161             return this.title;
37162         }
37163         
37164         var t = '';
37165         for (var k in this.title) {
37166             if (!this.title.hasOwnProperty(k)) {
37167                 continue;
37168             }
37169             
37170             if (k.indexOf('-') >= 0) {
37171                 var s = k.split('-');
37172                 for (var i = 0; i<s.length; i++) {
37173                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37174                 }
37175             } else {
37176                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37177             }
37178         }
37179         return t;
37180     },
37181     
37182     /**
37183      * Set this panel's title
37184      * @param {String} title
37185      */
37186     setTitle : function(title){
37187         this.title = title;
37188         if(this.region){
37189             this.region.updatePanelTitle(this, title);
37190         }
37191     },
37192     
37193     /**
37194      * Returns true is this panel was configured to be closable
37195      * @return {Boolean} 
37196      */
37197     isClosable : function(){
37198         return this.closable;
37199     },
37200     
37201     beforeSlide : function(){
37202         this.el.clip();
37203         this.resizeEl.clip();
37204     },
37205     
37206     afterSlide : function(){
37207         this.el.unclip();
37208         this.resizeEl.unclip();
37209     },
37210     
37211     /**
37212      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37213      *   Will fail silently if the {@link #setUrl} method has not been called.
37214      *   This does not activate the panel, just updates its content.
37215      */
37216     refresh : function(){
37217         if(this.refreshDelegate){
37218            this.loaded = false;
37219            this.refreshDelegate();
37220         }
37221     },
37222     
37223     /**
37224      * Destroys this panel
37225      */
37226     destroy : function(){
37227         this.el.removeAllListeners();
37228         var tempEl = document.createElement("span");
37229         tempEl.appendChild(this.el.dom);
37230         tempEl.innerHTML = "";
37231         this.el.remove();
37232         this.el = null;
37233     },
37234     
37235     /**
37236      * form - if the content panel contains a form - this is a reference to it.
37237      * @type {Roo.form.Form}
37238      */
37239     form : false,
37240     /**
37241      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37242      *    This contains a reference to it.
37243      * @type {Roo.View}
37244      */
37245     view : false,
37246     
37247       /**
37248      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37249      * <pre><code>
37250
37251 layout.addxtype({
37252        xtype : 'Form',
37253        items: [ .... ]
37254    }
37255 );
37256
37257 </code></pre>
37258      * @param {Object} cfg Xtype definition of item to add.
37259      */
37260     
37261     
37262     getChildContainer: function () {
37263         return this.getEl();
37264     }
37265     
37266     
37267     /*
37268         var  ret = new Roo.factory(cfg);
37269         return ret;
37270         
37271         
37272         // add form..
37273         if (cfg.xtype.match(/^Form$/)) {
37274             
37275             var el;
37276             //if (this.footer) {
37277             //    el = this.footer.container.insertSibling(false, 'before');
37278             //} else {
37279                 el = this.el.createChild();
37280             //}
37281
37282             this.form = new  Roo.form.Form(cfg);
37283             
37284             
37285             if ( this.form.allItems.length) {
37286                 this.form.render(el.dom);
37287             }
37288             return this.form;
37289         }
37290         // should only have one of theses..
37291         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37292             // views.. should not be just added - used named prop 'view''
37293             
37294             cfg.el = this.el.appendChild(document.createElement("div"));
37295             // factory?
37296             
37297             var ret = new Roo.factory(cfg);
37298              
37299              ret.render && ret.render(false, ''); // render blank..
37300             this.view = ret;
37301             return ret;
37302         }
37303         return false;
37304     }
37305     \*/
37306 });
37307  
37308 /**
37309  * @class Roo.bootstrap.panel.Grid
37310  * @extends Roo.bootstrap.panel.Content
37311  * @constructor
37312  * Create a new GridPanel.
37313  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37314  * @param {Object} config A the config object
37315   
37316  */
37317
37318
37319
37320 Roo.bootstrap.panel.Grid = function(config)
37321 {
37322     
37323       
37324     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37325         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37326
37327     config.el = this.wrapper;
37328     //this.el = this.wrapper;
37329     
37330       if (config.container) {
37331         // ctor'ed from a Border/panel.grid
37332         
37333         
37334         this.wrapper.setStyle("overflow", "hidden");
37335         this.wrapper.addClass('roo-grid-container');
37336
37337     }
37338     
37339     
37340     if(config.toolbar){
37341         var tool_el = this.wrapper.createChild();    
37342         this.toolbar = Roo.factory(config.toolbar);
37343         var ti = [];
37344         if (config.toolbar.items) {
37345             ti = config.toolbar.items ;
37346             delete config.toolbar.items ;
37347         }
37348         
37349         var nitems = [];
37350         this.toolbar.render(tool_el);
37351         for(var i =0;i < ti.length;i++) {
37352           //  Roo.log(['add child', items[i]]);
37353             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37354         }
37355         this.toolbar.items = nitems;
37356         
37357         delete config.toolbar;
37358     }
37359     
37360     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37361     config.grid.scrollBody = true;;
37362     config.grid.monitorWindowResize = false; // turn off autosizing
37363     config.grid.autoHeight = false;
37364     config.grid.autoWidth = false;
37365     
37366     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37367     
37368     if (config.background) {
37369         // render grid on panel activation (if panel background)
37370         this.on('activate', function(gp) {
37371             if (!gp.grid.rendered) {
37372                 gp.grid.render(this.wrapper);
37373                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37374             }
37375         });
37376             
37377     } else {
37378         this.grid.render(this.wrapper);
37379         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37380
37381     }
37382     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37383     // ??? needed ??? config.el = this.wrapper;
37384     
37385     
37386     
37387   
37388     // xtype created footer. - not sure if will work as we normally have to render first..
37389     if (this.footer && !this.footer.el && this.footer.xtype) {
37390         
37391         var ctr = this.grid.getView().getFooterPanel(true);
37392         this.footer.dataSource = this.grid.dataSource;
37393         this.footer = Roo.factory(this.footer, Roo);
37394         this.footer.render(ctr);
37395         
37396     }
37397     
37398     
37399     
37400     
37401      
37402 };
37403
37404 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37405     getId : function(){
37406         return this.grid.id;
37407     },
37408     
37409     /**
37410      * Returns the grid for this panel
37411      * @return {Roo.bootstrap.Table} 
37412      */
37413     getGrid : function(){
37414         return this.grid;    
37415     },
37416     
37417     setSize : function(width, height){
37418         if(!this.ignoreResize(width, height)){
37419             var grid = this.grid;
37420             var size = this.adjustForComponents(width, height);
37421             var gridel = grid.getGridEl();
37422             gridel.setSize(size.width, size.height);
37423             /*
37424             var thd = grid.getGridEl().select('thead',true).first();
37425             var tbd = grid.getGridEl().select('tbody', true).first();
37426             if (tbd) {
37427                 tbd.setSize(width, height - thd.getHeight());
37428             }
37429             */
37430             grid.autoSize();
37431         }
37432     },
37433      
37434     
37435     
37436     beforeSlide : function(){
37437         this.grid.getView().scroller.clip();
37438     },
37439     
37440     afterSlide : function(){
37441         this.grid.getView().scroller.unclip();
37442     },
37443     
37444     destroy : function(){
37445         this.grid.destroy();
37446         delete this.grid;
37447         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37448     }
37449 });
37450
37451 /**
37452  * @class Roo.bootstrap.panel.Nest
37453  * @extends Roo.bootstrap.panel.Content
37454  * @constructor
37455  * Create a new Panel, that can contain a layout.Border.
37456  * 
37457  * 
37458  * @param {Roo.BorderLayout} layout The layout for this panel
37459  * @param {String/Object} config A string to set only the title or a config object
37460  */
37461 Roo.bootstrap.panel.Nest = function(config)
37462 {
37463     // construct with only one argument..
37464     /* FIXME - implement nicer consturctors
37465     if (layout.layout) {
37466         config = layout;
37467         layout = config.layout;
37468         delete config.layout;
37469     }
37470     if (layout.xtype && !layout.getEl) {
37471         // then layout needs constructing..
37472         layout = Roo.factory(layout, Roo);
37473     }
37474     */
37475     
37476     config.el =  config.layout.getEl();
37477     
37478     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37479     
37480     config.layout.monitorWindowResize = false; // turn off autosizing
37481     this.layout = config.layout;
37482     this.layout.getEl().addClass("roo-layout-nested-layout");
37483     
37484     
37485     
37486     
37487 };
37488
37489 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37490
37491     setSize : function(width, height){
37492         if(!this.ignoreResize(width, height)){
37493             var size = this.adjustForComponents(width, height);
37494             var el = this.layout.getEl();
37495             if (size.height < 1) {
37496                 el.setWidth(size.width);   
37497             } else {
37498                 el.setSize(size.width, size.height);
37499             }
37500             var touch = el.dom.offsetWidth;
37501             this.layout.layout();
37502             // ie requires a double layout on the first pass
37503             if(Roo.isIE && !this.initialized){
37504                 this.initialized = true;
37505                 this.layout.layout();
37506             }
37507         }
37508     },
37509     
37510     // activate all subpanels if not currently active..
37511     
37512     setActiveState : function(active){
37513         this.active = active;
37514         this.setActiveClass(active);
37515         
37516         if(!active){
37517             this.fireEvent("deactivate", this);
37518             return;
37519         }
37520         
37521         this.fireEvent("activate", this);
37522         // not sure if this should happen before or after..
37523         if (!this.layout) {
37524             return; // should not happen..
37525         }
37526         var reg = false;
37527         for (var r in this.layout.regions) {
37528             reg = this.layout.getRegion(r);
37529             if (reg.getActivePanel()) {
37530                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37531                 reg.setActivePanel(reg.getActivePanel());
37532                 continue;
37533             }
37534             if (!reg.panels.length) {
37535                 continue;
37536             }
37537             reg.showPanel(reg.getPanel(0));
37538         }
37539         
37540         
37541         
37542         
37543     },
37544     
37545     /**
37546      * Returns the nested BorderLayout for this panel
37547      * @return {Roo.BorderLayout} 
37548      */
37549     getLayout : function(){
37550         return this.layout;
37551     },
37552     
37553      /**
37554      * Adds a xtype elements to the layout of the nested panel
37555      * <pre><code>
37556
37557 panel.addxtype({
37558        xtype : 'ContentPanel',
37559        region: 'west',
37560        items: [ .... ]
37561    }
37562 );
37563
37564 panel.addxtype({
37565         xtype : 'NestedLayoutPanel',
37566         region: 'west',
37567         layout: {
37568            center: { },
37569            west: { }   
37570         },
37571         items : [ ... list of content panels or nested layout panels.. ]
37572    }
37573 );
37574 </code></pre>
37575      * @param {Object} cfg Xtype definition of item to add.
37576      */
37577     addxtype : function(cfg) {
37578         return this.layout.addxtype(cfg);
37579     
37580     }
37581 });        /*
37582  * Based on:
37583  * Ext JS Library 1.1.1
37584  * Copyright(c) 2006-2007, Ext JS, LLC.
37585  *
37586  * Originally Released Under LGPL - original licence link has changed is not relivant.
37587  *
37588  * Fork - LGPL
37589  * <script type="text/javascript">
37590  */
37591 /**
37592  * @class Roo.TabPanel
37593  * @extends Roo.util.Observable
37594  * A lightweight tab container.
37595  * <br><br>
37596  * Usage:
37597  * <pre><code>
37598 // basic tabs 1, built from existing content
37599 var tabs = new Roo.TabPanel("tabs1");
37600 tabs.addTab("script", "View Script");
37601 tabs.addTab("markup", "View Markup");
37602 tabs.activate("script");
37603
37604 // more advanced tabs, built from javascript
37605 var jtabs = new Roo.TabPanel("jtabs");
37606 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37607
37608 // set up the UpdateManager
37609 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37610 var updater = tab2.getUpdateManager();
37611 updater.setDefaultUrl("ajax1.htm");
37612 tab2.on('activate', updater.refresh, updater, true);
37613
37614 // Use setUrl for Ajax loading
37615 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37616 tab3.setUrl("ajax2.htm", null, true);
37617
37618 // Disabled tab
37619 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37620 tab4.disable();
37621
37622 jtabs.activate("jtabs-1");
37623  * </code></pre>
37624  * @constructor
37625  * Create a new TabPanel.
37626  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37627  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37628  */
37629 Roo.bootstrap.panel.Tabs = function(config){
37630     /**
37631     * The container element for this TabPanel.
37632     * @type Roo.Element
37633     */
37634     this.el = Roo.get(config.el);
37635     delete config.el;
37636     if(config){
37637         if(typeof config == "boolean"){
37638             this.tabPosition = config ? "bottom" : "top";
37639         }else{
37640             Roo.apply(this, config);
37641         }
37642     }
37643     
37644     if(this.tabPosition == "bottom"){
37645         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37646         this.el.addClass("roo-tabs-bottom");
37647     }
37648     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37649     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37650     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37651     if(Roo.isIE){
37652         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37653     }
37654     if(this.tabPosition != "bottom"){
37655         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37656          * @type Roo.Element
37657          */
37658         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37659         this.el.addClass("roo-tabs-top");
37660     }
37661     this.items = [];
37662
37663     this.bodyEl.setStyle("position", "relative");
37664
37665     this.active = null;
37666     this.activateDelegate = this.activate.createDelegate(this);
37667
37668     this.addEvents({
37669         /**
37670          * @event tabchange
37671          * Fires when the active tab changes
37672          * @param {Roo.TabPanel} this
37673          * @param {Roo.TabPanelItem} activePanel The new active tab
37674          */
37675         "tabchange": true,
37676         /**
37677          * @event beforetabchange
37678          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37679          * @param {Roo.TabPanel} this
37680          * @param {Object} e Set cancel to true on this object to cancel the tab change
37681          * @param {Roo.TabPanelItem} tab The tab being changed to
37682          */
37683         "beforetabchange" : true
37684     });
37685
37686     Roo.EventManager.onWindowResize(this.onResize, this);
37687     this.cpad = this.el.getPadding("lr");
37688     this.hiddenCount = 0;
37689
37690
37691     // toolbar on the tabbar support...
37692     if (this.toolbar) {
37693         alert("no toolbar support yet");
37694         this.toolbar  = false;
37695         /*
37696         var tcfg = this.toolbar;
37697         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37698         this.toolbar = new Roo.Toolbar(tcfg);
37699         if (Roo.isSafari) {
37700             var tbl = tcfg.container.child('table', true);
37701             tbl.setAttribute('width', '100%');
37702         }
37703         */
37704         
37705     }
37706    
37707
37708
37709     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37710 };
37711
37712 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37713     /*
37714      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37715      */
37716     tabPosition : "top",
37717     /*
37718      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37719      */
37720     currentTabWidth : 0,
37721     /*
37722      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37723      */
37724     minTabWidth : 40,
37725     /*
37726      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37727      */
37728     maxTabWidth : 250,
37729     /*
37730      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37731      */
37732     preferredTabWidth : 175,
37733     /*
37734      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37735      */
37736     resizeTabs : false,
37737     /*
37738      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37739      */
37740     monitorResize : true,
37741     /*
37742      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37743      */
37744     toolbar : false,
37745
37746     /**
37747      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37748      * @param {String} id The id of the div to use <b>or create</b>
37749      * @param {String} text The text for the tab
37750      * @param {String} content (optional) Content to put in the TabPanelItem body
37751      * @param {Boolean} closable (optional) True to create a close icon on the tab
37752      * @return {Roo.TabPanelItem} The created TabPanelItem
37753      */
37754     addTab : function(id, text, content, closable, tpl)
37755     {
37756         var item = new Roo.bootstrap.panel.TabItem({
37757             panel: this,
37758             id : id,
37759             text : text,
37760             closable : closable,
37761             tpl : tpl
37762         });
37763         this.addTabItem(item);
37764         if(content){
37765             item.setContent(content);
37766         }
37767         return item;
37768     },
37769
37770     /**
37771      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37772      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37773      * @return {Roo.TabPanelItem}
37774      */
37775     getTab : function(id){
37776         return this.items[id];
37777     },
37778
37779     /**
37780      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37781      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37782      */
37783     hideTab : function(id){
37784         var t = this.items[id];
37785         if(!t.isHidden()){
37786            t.setHidden(true);
37787            this.hiddenCount++;
37788            this.autoSizeTabs();
37789         }
37790     },
37791
37792     /**
37793      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37794      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37795      */
37796     unhideTab : function(id){
37797         var t = this.items[id];
37798         if(t.isHidden()){
37799            t.setHidden(false);
37800            this.hiddenCount--;
37801            this.autoSizeTabs();
37802         }
37803     },
37804
37805     /**
37806      * Adds an existing {@link Roo.TabPanelItem}.
37807      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37808      */
37809     addTabItem : function(item){
37810         this.items[item.id] = item;
37811         this.items.push(item);
37812       //  if(this.resizeTabs){
37813     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37814   //         this.autoSizeTabs();
37815 //        }else{
37816 //            item.autoSize();
37817        // }
37818     },
37819
37820     /**
37821      * Removes a {@link Roo.TabPanelItem}.
37822      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37823      */
37824     removeTab : function(id){
37825         var items = this.items;
37826         var tab = items[id];
37827         if(!tab) { return; }
37828         var index = items.indexOf(tab);
37829         if(this.active == tab && items.length > 1){
37830             var newTab = this.getNextAvailable(index);
37831             if(newTab) {
37832                 newTab.activate();
37833             }
37834         }
37835         this.stripEl.dom.removeChild(tab.pnode.dom);
37836         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37837             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37838         }
37839         items.splice(index, 1);
37840         delete this.items[tab.id];
37841         tab.fireEvent("close", tab);
37842         tab.purgeListeners();
37843         this.autoSizeTabs();
37844     },
37845
37846     getNextAvailable : function(start){
37847         var items = this.items;
37848         var index = start;
37849         // look for a next tab that will slide over to
37850         // replace the one being removed
37851         while(index < items.length){
37852             var item = items[++index];
37853             if(item && !item.isHidden()){
37854                 return item;
37855             }
37856         }
37857         // if one isn't found select the previous tab (on the left)
37858         index = start;
37859         while(index >= 0){
37860             var item = items[--index];
37861             if(item && !item.isHidden()){
37862                 return item;
37863             }
37864         }
37865         return null;
37866     },
37867
37868     /**
37869      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37870      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37871      */
37872     disableTab : function(id){
37873         var tab = this.items[id];
37874         if(tab && this.active != tab){
37875             tab.disable();
37876         }
37877     },
37878
37879     /**
37880      * Enables a {@link Roo.TabPanelItem} that is disabled.
37881      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37882      */
37883     enableTab : function(id){
37884         var tab = this.items[id];
37885         tab.enable();
37886     },
37887
37888     /**
37889      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37890      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37891      * @return {Roo.TabPanelItem} The TabPanelItem.
37892      */
37893     activate : function(id){
37894         var tab = this.items[id];
37895         if(!tab){
37896             return null;
37897         }
37898         if(tab == this.active || tab.disabled){
37899             return tab;
37900         }
37901         var e = {};
37902         this.fireEvent("beforetabchange", this, e, tab);
37903         if(e.cancel !== true && !tab.disabled){
37904             if(this.active){
37905                 this.active.hide();
37906             }
37907             this.active = this.items[id];
37908             this.active.show();
37909             this.fireEvent("tabchange", this, this.active);
37910         }
37911         return tab;
37912     },
37913
37914     /**
37915      * Gets the active {@link Roo.TabPanelItem}.
37916      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37917      */
37918     getActiveTab : function(){
37919         return this.active;
37920     },
37921
37922     /**
37923      * Updates the tab body element to fit the height of the container element
37924      * for overflow scrolling
37925      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37926      */
37927     syncHeight : function(targetHeight){
37928         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37929         var bm = this.bodyEl.getMargins();
37930         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37931         this.bodyEl.setHeight(newHeight);
37932         return newHeight;
37933     },
37934
37935     onResize : function(){
37936         if(this.monitorResize){
37937             this.autoSizeTabs();
37938         }
37939     },
37940
37941     /**
37942      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37943      */
37944     beginUpdate : function(){
37945         this.updating = true;
37946     },
37947
37948     /**
37949      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37950      */
37951     endUpdate : function(){
37952         this.updating = false;
37953         this.autoSizeTabs();
37954     },
37955
37956     /**
37957      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37958      */
37959     autoSizeTabs : function(){
37960         var count = this.items.length;
37961         var vcount = count - this.hiddenCount;
37962         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37963             return;
37964         }
37965         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37966         var availWidth = Math.floor(w / vcount);
37967         var b = this.stripBody;
37968         if(b.getWidth() > w){
37969             var tabs = this.items;
37970             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37971             if(availWidth < this.minTabWidth){
37972                 /*if(!this.sleft){    // incomplete scrolling code
37973                     this.createScrollButtons();
37974                 }
37975                 this.showScroll();
37976                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37977             }
37978         }else{
37979             if(this.currentTabWidth < this.preferredTabWidth){
37980                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37981             }
37982         }
37983     },
37984
37985     /**
37986      * Returns the number of tabs in this TabPanel.
37987      * @return {Number}
37988      */
37989      getCount : function(){
37990          return this.items.length;
37991      },
37992
37993     /**
37994      * Resizes all the tabs to the passed width
37995      * @param {Number} The new width
37996      */
37997     setTabWidth : function(width){
37998         this.currentTabWidth = width;
37999         for(var i = 0, len = this.items.length; i < len; i++) {
38000                 if(!this.items[i].isHidden()) {
38001                 this.items[i].setWidth(width);
38002             }
38003         }
38004     },
38005
38006     /**
38007      * Destroys this TabPanel
38008      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38009      */
38010     destroy : function(removeEl){
38011         Roo.EventManager.removeResizeListener(this.onResize, this);
38012         for(var i = 0, len = this.items.length; i < len; i++){
38013             this.items[i].purgeListeners();
38014         }
38015         if(removeEl === true){
38016             this.el.update("");
38017             this.el.remove();
38018         }
38019     },
38020     
38021     createStrip : function(container)
38022     {
38023         var strip = document.createElement("nav");
38024         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38025         container.appendChild(strip);
38026         return strip;
38027     },
38028     
38029     createStripList : function(strip)
38030     {
38031         // div wrapper for retard IE
38032         // returns the "tr" element.
38033         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38034         //'<div class="x-tabs-strip-wrap">'+
38035           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38036           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38037         return strip.firstChild; //.firstChild.firstChild.firstChild;
38038     },
38039     createBody : function(container)
38040     {
38041         var body = document.createElement("div");
38042         Roo.id(body, "tab-body");
38043         //Roo.fly(body).addClass("x-tabs-body");
38044         Roo.fly(body).addClass("tab-content");
38045         container.appendChild(body);
38046         return body;
38047     },
38048     createItemBody :function(bodyEl, id){
38049         var body = Roo.getDom(id);
38050         if(!body){
38051             body = document.createElement("div");
38052             body.id = id;
38053         }
38054         //Roo.fly(body).addClass("x-tabs-item-body");
38055         Roo.fly(body).addClass("tab-pane");
38056          bodyEl.insertBefore(body, bodyEl.firstChild);
38057         return body;
38058     },
38059     /** @private */
38060     createStripElements :  function(stripEl, text, closable, tpl)
38061     {
38062         var td = document.createElement("li"); // was td..
38063         
38064         
38065         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38066         
38067         
38068         stripEl.appendChild(td);
38069         /*if(closable){
38070             td.className = "x-tabs-closable";
38071             if(!this.closeTpl){
38072                 this.closeTpl = new Roo.Template(
38073                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38074                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38075                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38076                 );
38077             }
38078             var el = this.closeTpl.overwrite(td, {"text": text});
38079             var close = el.getElementsByTagName("div")[0];
38080             var inner = el.getElementsByTagName("em")[0];
38081             return {"el": el, "close": close, "inner": inner};
38082         } else {
38083         */
38084         // not sure what this is..
38085 //            if(!this.tabTpl){
38086                 //this.tabTpl = new Roo.Template(
38087                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38088                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38089                 //);
38090 //                this.tabTpl = new Roo.Template(
38091 //                   '<a href="#">' +
38092 //                   '<span unselectable="on"' +
38093 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38094 //                            ' >{text}</span></a>'
38095 //                );
38096 //                
38097 //            }
38098
38099
38100             var template = tpl || this.tabTpl || false;
38101             
38102             if(!template){
38103                 
38104                 template = new Roo.Template(
38105                    '<a href="#">' +
38106                    '<span unselectable="on"' +
38107                             (this.disableTooltips ? '' : ' title="{text}"') +
38108                             ' >{text}</span></a>'
38109                 );
38110             }
38111             
38112             switch (typeof(template)) {
38113                 case 'object' :
38114                     break;
38115                 case 'string' :
38116                     template = new Roo.Template(template);
38117                     break;
38118                 default :
38119                     break;
38120             }
38121             
38122             var el = template.overwrite(td, {"text": text});
38123             
38124             var inner = el.getElementsByTagName("span")[0];
38125             
38126             return {"el": el, "inner": inner};
38127             
38128     }
38129         
38130     
38131 });
38132
38133 /**
38134  * @class Roo.TabPanelItem
38135  * @extends Roo.util.Observable
38136  * Represents an individual item (tab plus body) in a TabPanel.
38137  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38138  * @param {String} id The id of this TabPanelItem
38139  * @param {String} text The text for the tab of this TabPanelItem
38140  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38141  */
38142 Roo.bootstrap.panel.TabItem = function(config){
38143     /**
38144      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38145      * @type Roo.TabPanel
38146      */
38147     this.tabPanel = config.panel;
38148     /**
38149      * The id for this TabPanelItem
38150      * @type String
38151      */
38152     this.id = config.id;
38153     /** @private */
38154     this.disabled = false;
38155     /** @private */
38156     this.text = config.text;
38157     /** @private */
38158     this.loaded = false;
38159     this.closable = config.closable;
38160
38161     /**
38162      * The body element for this TabPanelItem.
38163      * @type Roo.Element
38164      */
38165     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38166     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38167     this.bodyEl.setStyle("display", "block");
38168     this.bodyEl.setStyle("zoom", "1");
38169     //this.hideAction();
38170
38171     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38172     /** @private */
38173     this.el = Roo.get(els.el);
38174     this.inner = Roo.get(els.inner, true);
38175     this.textEl = Roo.get(this.el.dom.firstChild, true);
38176     this.pnode = Roo.get(els.el.parentNode, true);
38177 //    this.el.on("mousedown", this.onTabMouseDown, this);
38178     this.el.on("click", this.onTabClick, this);
38179     /** @private */
38180     if(config.closable){
38181         var c = Roo.get(els.close, true);
38182         c.dom.title = this.closeText;
38183         c.addClassOnOver("close-over");
38184         c.on("click", this.closeClick, this);
38185      }
38186
38187     this.addEvents({
38188          /**
38189          * @event activate
38190          * Fires when this tab becomes the active tab.
38191          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38192          * @param {Roo.TabPanelItem} this
38193          */
38194         "activate": true,
38195         /**
38196          * @event beforeclose
38197          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38198          * @param {Roo.TabPanelItem} this
38199          * @param {Object} e Set cancel to true on this object to cancel the close.
38200          */
38201         "beforeclose": true,
38202         /**
38203          * @event close
38204          * Fires when this tab is closed.
38205          * @param {Roo.TabPanelItem} this
38206          */
38207          "close": true,
38208         /**
38209          * @event deactivate
38210          * Fires when this tab is no longer the active tab.
38211          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38212          * @param {Roo.TabPanelItem} this
38213          */
38214          "deactivate" : true
38215     });
38216     this.hidden = false;
38217
38218     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38219 };
38220
38221 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38222            {
38223     purgeListeners : function(){
38224        Roo.util.Observable.prototype.purgeListeners.call(this);
38225        this.el.removeAllListeners();
38226     },
38227     /**
38228      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38229      */
38230     show : function(){
38231         this.pnode.addClass("active");
38232         this.showAction();
38233         if(Roo.isOpera){
38234             this.tabPanel.stripWrap.repaint();
38235         }
38236         this.fireEvent("activate", this.tabPanel, this);
38237     },
38238
38239     /**
38240      * Returns true if this tab is the active tab.
38241      * @return {Boolean}
38242      */
38243     isActive : function(){
38244         return this.tabPanel.getActiveTab() == this;
38245     },
38246
38247     /**
38248      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38249      */
38250     hide : function(){
38251         this.pnode.removeClass("active");
38252         this.hideAction();
38253         this.fireEvent("deactivate", this.tabPanel, this);
38254     },
38255
38256     hideAction : function(){
38257         this.bodyEl.hide();
38258         this.bodyEl.setStyle("position", "absolute");
38259         this.bodyEl.setLeft("-20000px");
38260         this.bodyEl.setTop("-20000px");
38261     },
38262
38263     showAction : function(){
38264         this.bodyEl.setStyle("position", "relative");
38265         this.bodyEl.setTop("");
38266         this.bodyEl.setLeft("");
38267         this.bodyEl.show();
38268     },
38269
38270     /**
38271      * Set the tooltip for the tab.
38272      * @param {String} tooltip The tab's tooltip
38273      */
38274     setTooltip : function(text){
38275         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38276             this.textEl.dom.qtip = text;
38277             this.textEl.dom.removeAttribute('title');
38278         }else{
38279             this.textEl.dom.title = text;
38280         }
38281     },
38282
38283     onTabClick : function(e){
38284         e.preventDefault();
38285         this.tabPanel.activate(this.id);
38286     },
38287
38288     onTabMouseDown : function(e){
38289         e.preventDefault();
38290         this.tabPanel.activate(this.id);
38291     },
38292 /*
38293     getWidth : function(){
38294         return this.inner.getWidth();
38295     },
38296
38297     setWidth : function(width){
38298         var iwidth = width - this.pnode.getPadding("lr");
38299         this.inner.setWidth(iwidth);
38300         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38301         this.pnode.setWidth(width);
38302     },
38303 */
38304     /**
38305      * Show or hide the tab
38306      * @param {Boolean} hidden True to hide or false to show.
38307      */
38308     setHidden : function(hidden){
38309         this.hidden = hidden;
38310         this.pnode.setStyle("display", hidden ? "none" : "");
38311     },
38312
38313     /**
38314      * Returns true if this tab is "hidden"
38315      * @return {Boolean}
38316      */
38317     isHidden : function(){
38318         return this.hidden;
38319     },
38320
38321     /**
38322      * Returns the text for this tab
38323      * @return {String}
38324      */
38325     getText : function(){
38326         return this.text;
38327     },
38328     /*
38329     autoSize : function(){
38330         //this.el.beginMeasure();
38331         this.textEl.setWidth(1);
38332         /*
38333          *  #2804 [new] Tabs in Roojs
38334          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38335          */
38336         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38337         //this.el.endMeasure();
38338     //},
38339
38340     /**
38341      * Sets the text for the tab (Note: this also sets the tooltip text)
38342      * @param {String} text The tab's text and tooltip
38343      */
38344     setText : function(text){
38345         this.text = text;
38346         this.textEl.update(text);
38347         this.setTooltip(text);
38348         //if(!this.tabPanel.resizeTabs){
38349         //    this.autoSize();
38350         //}
38351     },
38352     /**
38353      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38354      */
38355     activate : function(){
38356         this.tabPanel.activate(this.id);
38357     },
38358
38359     /**
38360      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38361      */
38362     disable : function(){
38363         if(this.tabPanel.active != this){
38364             this.disabled = true;
38365             this.pnode.addClass("disabled");
38366         }
38367     },
38368
38369     /**
38370      * Enables this TabPanelItem if it was previously disabled.
38371      */
38372     enable : function(){
38373         this.disabled = false;
38374         this.pnode.removeClass("disabled");
38375     },
38376
38377     /**
38378      * Sets the content for this TabPanelItem.
38379      * @param {String} content The content
38380      * @param {Boolean} loadScripts true to look for and load scripts
38381      */
38382     setContent : function(content, loadScripts){
38383         this.bodyEl.update(content, loadScripts);
38384     },
38385
38386     /**
38387      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38388      * @return {Roo.UpdateManager} The UpdateManager
38389      */
38390     getUpdateManager : function(){
38391         return this.bodyEl.getUpdateManager();
38392     },
38393
38394     /**
38395      * Set a URL to be used to load the content for this TabPanelItem.
38396      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38397      * @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)
38398      * @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)
38399      * @return {Roo.UpdateManager} The UpdateManager
38400      */
38401     setUrl : function(url, params, loadOnce){
38402         if(this.refreshDelegate){
38403             this.un('activate', this.refreshDelegate);
38404         }
38405         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38406         this.on("activate", this.refreshDelegate);
38407         return this.bodyEl.getUpdateManager();
38408     },
38409
38410     /** @private */
38411     _handleRefresh : function(url, params, loadOnce){
38412         if(!loadOnce || !this.loaded){
38413             var updater = this.bodyEl.getUpdateManager();
38414             updater.update(url, params, this._setLoaded.createDelegate(this));
38415         }
38416     },
38417
38418     /**
38419      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38420      *   Will fail silently if the setUrl method has not been called.
38421      *   This does not activate the panel, just updates its content.
38422      */
38423     refresh : function(){
38424         if(this.refreshDelegate){
38425            this.loaded = false;
38426            this.refreshDelegate();
38427         }
38428     },
38429
38430     /** @private */
38431     _setLoaded : function(){
38432         this.loaded = true;
38433     },
38434
38435     /** @private */
38436     closeClick : function(e){
38437         var o = {};
38438         e.stopEvent();
38439         this.fireEvent("beforeclose", this, o);
38440         if(o.cancel !== true){
38441             this.tabPanel.removeTab(this.id);
38442         }
38443     },
38444     /**
38445      * The text displayed in the tooltip for the close icon.
38446      * @type String
38447      */
38448     closeText : "Close this tab"
38449 });
38450 /**
38451 *    This script refer to:
38452 *    Title: International Telephone Input
38453 *    Author: Jack O'Connor
38454 *    Code version:  v12.1.12
38455 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38456 **/
38457
38458 Roo.bootstrap.PhoneInputData = function() {
38459     var d = [
38460       [
38461         "Afghanistan (‫افغانستان‬‎)",
38462         "af",
38463         "93"
38464       ],
38465       [
38466         "Albania (Shqipëri)",
38467         "al",
38468         "355"
38469       ],
38470       [
38471         "Algeria (‫الجزائر‬‎)",
38472         "dz",
38473         "213"
38474       ],
38475       [
38476         "American Samoa",
38477         "as",
38478         "1684"
38479       ],
38480       [
38481         "Andorra",
38482         "ad",
38483         "376"
38484       ],
38485       [
38486         "Angola",
38487         "ao",
38488         "244"
38489       ],
38490       [
38491         "Anguilla",
38492         "ai",
38493         "1264"
38494       ],
38495       [
38496         "Antigua and Barbuda",
38497         "ag",
38498         "1268"
38499       ],
38500       [
38501         "Argentina",
38502         "ar",
38503         "54"
38504       ],
38505       [
38506         "Armenia (Հայաստան)",
38507         "am",
38508         "374"
38509       ],
38510       [
38511         "Aruba",
38512         "aw",
38513         "297"
38514       ],
38515       [
38516         "Australia",
38517         "au",
38518         "61",
38519         0
38520       ],
38521       [
38522         "Austria (Österreich)",
38523         "at",
38524         "43"
38525       ],
38526       [
38527         "Azerbaijan (Azərbaycan)",
38528         "az",
38529         "994"
38530       ],
38531       [
38532         "Bahamas",
38533         "bs",
38534         "1242"
38535       ],
38536       [
38537         "Bahrain (‫البحرين‬‎)",
38538         "bh",
38539         "973"
38540       ],
38541       [
38542         "Bangladesh (বাংলাদেশ)",
38543         "bd",
38544         "880"
38545       ],
38546       [
38547         "Barbados",
38548         "bb",
38549         "1246"
38550       ],
38551       [
38552         "Belarus (Беларусь)",
38553         "by",
38554         "375"
38555       ],
38556       [
38557         "Belgium (België)",
38558         "be",
38559         "32"
38560       ],
38561       [
38562         "Belize",
38563         "bz",
38564         "501"
38565       ],
38566       [
38567         "Benin (Bénin)",
38568         "bj",
38569         "229"
38570       ],
38571       [
38572         "Bermuda",
38573         "bm",
38574         "1441"
38575       ],
38576       [
38577         "Bhutan (འབྲུག)",
38578         "bt",
38579         "975"
38580       ],
38581       [
38582         "Bolivia",
38583         "bo",
38584         "591"
38585       ],
38586       [
38587         "Bosnia and Herzegovina (Босна и Херцеговина)",
38588         "ba",
38589         "387"
38590       ],
38591       [
38592         "Botswana",
38593         "bw",
38594         "267"
38595       ],
38596       [
38597         "Brazil (Brasil)",
38598         "br",
38599         "55"
38600       ],
38601       [
38602         "British Indian Ocean Territory",
38603         "io",
38604         "246"
38605       ],
38606       [
38607         "British Virgin Islands",
38608         "vg",
38609         "1284"
38610       ],
38611       [
38612         "Brunei",
38613         "bn",
38614         "673"
38615       ],
38616       [
38617         "Bulgaria (България)",
38618         "bg",
38619         "359"
38620       ],
38621       [
38622         "Burkina Faso",
38623         "bf",
38624         "226"
38625       ],
38626       [
38627         "Burundi (Uburundi)",
38628         "bi",
38629         "257"
38630       ],
38631       [
38632         "Cambodia (កម្ពុជា)",
38633         "kh",
38634         "855"
38635       ],
38636       [
38637         "Cameroon (Cameroun)",
38638         "cm",
38639         "237"
38640       ],
38641       [
38642         "Canada",
38643         "ca",
38644         "1",
38645         1,
38646         ["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"]
38647       ],
38648       [
38649         "Cape Verde (Kabu Verdi)",
38650         "cv",
38651         "238"
38652       ],
38653       [
38654         "Caribbean Netherlands",
38655         "bq",
38656         "599",
38657         1
38658       ],
38659       [
38660         "Cayman Islands",
38661         "ky",
38662         "1345"
38663       ],
38664       [
38665         "Central African Republic (République centrafricaine)",
38666         "cf",
38667         "236"
38668       ],
38669       [
38670         "Chad (Tchad)",
38671         "td",
38672         "235"
38673       ],
38674       [
38675         "Chile",
38676         "cl",
38677         "56"
38678       ],
38679       [
38680         "China (中国)",
38681         "cn",
38682         "86"
38683       ],
38684       [
38685         "Christmas Island",
38686         "cx",
38687         "61",
38688         2
38689       ],
38690       [
38691         "Cocos (Keeling) Islands",
38692         "cc",
38693         "61",
38694         1
38695       ],
38696       [
38697         "Colombia",
38698         "co",
38699         "57"
38700       ],
38701       [
38702         "Comoros (‫جزر القمر‬‎)",
38703         "km",
38704         "269"
38705       ],
38706       [
38707         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38708         "cd",
38709         "243"
38710       ],
38711       [
38712         "Congo (Republic) (Congo-Brazzaville)",
38713         "cg",
38714         "242"
38715       ],
38716       [
38717         "Cook Islands",
38718         "ck",
38719         "682"
38720       ],
38721       [
38722         "Costa Rica",
38723         "cr",
38724         "506"
38725       ],
38726       [
38727         "Côte d’Ivoire",
38728         "ci",
38729         "225"
38730       ],
38731       [
38732         "Croatia (Hrvatska)",
38733         "hr",
38734         "385"
38735       ],
38736       [
38737         "Cuba",
38738         "cu",
38739         "53"
38740       ],
38741       [
38742         "Curaçao",
38743         "cw",
38744         "599",
38745         0
38746       ],
38747       [
38748         "Cyprus (Κύπρος)",
38749         "cy",
38750         "357"
38751       ],
38752       [
38753         "Czech Republic (Česká republika)",
38754         "cz",
38755         "420"
38756       ],
38757       [
38758         "Denmark (Danmark)",
38759         "dk",
38760         "45"
38761       ],
38762       [
38763         "Djibouti",
38764         "dj",
38765         "253"
38766       ],
38767       [
38768         "Dominica",
38769         "dm",
38770         "1767"
38771       ],
38772       [
38773         "Dominican Republic (República Dominicana)",
38774         "do",
38775         "1",
38776         2,
38777         ["809", "829", "849"]
38778       ],
38779       [
38780         "Ecuador",
38781         "ec",
38782         "593"
38783       ],
38784       [
38785         "Egypt (‫مصر‬‎)",
38786         "eg",
38787         "20"
38788       ],
38789       [
38790         "El Salvador",
38791         "sv",
38792         "503"
38793       ],
38794       [
38795         "Equatorial Guinea (Guinea Ecuatorial)",
38796         "gq",
38797         "240"
38798       ],
38799       [
38800         "Eritrea",
38801         "er",
38802         "291"
38803       ],
38804       [
38805         "Estonia (Eesti)",
38806         "ee",
38807         "372"
38808       ],
38809       [
38810         "Ethiopia",
38811         "et",
38812         "251"
38813       ],
38814       [
38815         "Falkland Islands (Islas Malvinas)",
38816         "fk",
38817         "500"
38818       ],
38819       [
38820         "Faroe Islands (Føroyar)",
38821         "fo",
38822         "298"
38823       ],
38824       [
38825         "Fiji",
38826         "fj",
38827         "679"
38828       ],
38829       [
38830         "Finland (Suomi)",
38831         "fi",
38832         "358",
38833         0
38834       ],
38835       [
38836         "France",
38837         "fr",
38838         "33"
38839       ],
38840       [
38841         "French Guiana (Guyane française)",
38842         "gf",
38843         "594"
38844       ],
38845       [
38846         "French Polynesia (Polynésie française)",
38847         "pf",
38848         "689"
38849       ],
38850       [
38851         "Gabon",
38852         "ga",
38853         "241"
38854       ],
38855       [
38856         "Gambia",
38857         "gm",
38858         "220"
38859       ],
38860       [
38861         "Georgia (საქართველო)",
38862         "ge",
38863         "995"
38864       ],
38865       [
38866         "Germany (Deutschland)",
38867         "de",
38868         "49"
38869       ],
38870       [
38871         "Ghana (Gaana)",
38872         "gh",
38873         "233"
38874       ],
38875       [
38876         "Gibraltar",
38877         "gi",
38878         "350"
38879       ],
38880       [
38881         "Greece (Ελλάδα)",
38882         "gr",
38883         "30"
38884       ],
38885       [
38886         "Greenland (Kalaallit Nunaat)",
38887         "gl",
38888         "299"
38889       ],
38890       [
38891         "Grenada",
38892         "gd",
38893         "1473"
38894       ],
38895       [
38896         "Guadeloupe",
38897         "gp",
38898         "590",
38899         0
38900       ],
38901       [
38902         "Guam",
38903         "gu",
38904         "1671"
38905       ],
38906       [
38907         "Guatemala",
38908         "gt",
38909         "502"
38910       ],
38911       [
38912         "Guernsey",
38913         "gg",
38914         "44",
38915         1
38916       ],
38917       [
38918         "Guinea (Guinée)",
38919         "gn",
38920         "224"
38921       ],
38922       [
38923         "Guinea-Bissau (Guiné Bissau)",
38924         "gw",
38925         "245"
38926       ],
38927       [
38928         "Guyana",
38929         "gy",
38930         "592"
38931       ],
38932       [
38933         "Haiti",
38934         "ht",
38935         "509"
38936       ],
38937       [
38938         "Honduras",
38939         "hn",
38940         "504"
38941       ],
38942       [
38943         "Hong Kong (香港)",
38944         "hk",
38945         "852"
38946       ],
38947       [
38948         "Hungary (Magyarország)",
38949         "hu",
38950         "36"
38951       ],
38952       [
38953         "Iceland (Ísland)",
38954         "is",
38955         "354"
38956       ],
38957       [
38958         "India (भारत)",
38959         "in",
38960         "91"
38961       ],
38962       [
38963         "Indonesia",
38964         "id",
38965         "62"
38966       ],
38967       [
38968         "Iran (‫ایران‬‎)",
38969         "ir",
38970         "98"
38971       ],
38972       [
38973         "Iraq (‫العراق‬‎)",
38974         "iq",
38975         "964"
38976       ],
38977       [
38978         "Ireland",
38979         "ie",
38980         "353"
38981       ],
38982       [
38983         "Isle of Man",
38984         "im",
38985         "44",
38986         2
38987       ],
38988       [
38989         "Israel (‫ישראל‬‎)",
38990         "il",
38991         "972"
38992       ],
38993       [
38994         "Italy (Italia)",
38995         "it",
38996         "39",
38997         0
38998       ],
38999       [
39000         "Jamaica",
39001         "jm",
39002         "1876"
39003       ],
39004       [
39005         "Japan (日本)",
39006         "jp",
39007         "81"
39008       ],
39009       [
39010         "Jersey",
39011         "je",
39012         "44",
39013         3
39014       ],
39015       [
39016         "Jordan (‫الأردن‬‎)",
39017         "jo",
39018         "962"
39019       ],
39020       [
39021         "Kazakhstan (Казахстан)",
39022         "kz",
39023         "7",
39024         1
39025       ],
39026       [
39027         "Kenya",
39028         "ke",
39029         "254"
39030       ],
39031       [
39032         "Kiribati",
39033         "ki",
39034         "686"
39035       ],
39036       [
39037         "Kosovo",
39038         "xk",
39039         "383"
39040       ],
39041       [
39042         "Kuwait (‫الكويت‬‎)",
39043         "kw",
39044         "965"
39045       ],
39046       [
39047         "Kyrgyzstan (Кыргызстан)",
39048         "kg",
39049         "996"
39050       ],
39051       [
39052         "Laos (ລາວ)",
39053         "la",
39054         "856"
39055       ],
39056       [
39057         "Latvia (Latvija)",
39058         "lv",
39059         "371"
39060       ],
39061       [
39062         "Lebanon (‫لبنان‬‎)",
39063         "lb",
39064         "961"
39065       ],
39066       [
39067         "Lesotho",
39068         "ls",
39069         "266"
39070       ],
39071       [
39072         "Liberia",
39073         "lr",
39074         "231"
39075       ],
39076       [
39077         "Libya (‫ليبيا‬‎)",
39078         "ly",
39079         "218"
39080       ],
39081       [
39082         "Liechtenstein",
39083         "li",
39084         "423"
39085       ],
39086       [
39087         "Lithuania (Lietuva)",
39088         "lt",
39089         "370"
39090       ],
39091       [
39092         "Luxembourg",
39093         "lu",
39094         "352"
39095       ],
39096       [
39097         "Macau (澳門)",
39098         "mo",
39099         "853"
39100       ],
39101       [
39102         "Macedonia (FYROM) (Македонија)",
39103         "mk",
39104         "389"
39105       ],
39106       [
39107         "Madagascar (Madagasikara)",
39108         "mg",
39109         "261"
39110       ],
39111       [
39112         "Malawi",
39113         "mw",
39114         "265"
39115       ],
39116       [
39117         "Malaysia",
39118         "my",
39119         "60"
39120       ],
39121       [
39122         "Maldives",
39123         "mv",
39124         "960"
39125       ],
39126       [
39127         "Mali",
39128         "ml",
39129         "223"
39130       ],
39131       [
39132         "Malta",
39133         "mt",
39134         "356"
39135       ],
39136       [
39137         "Marshall Islands",
39138         "mh",
39139         "692"
39140       ],
39141       [
39142         "Martinique",
39143         "mq",
39144         "596"
39145       ],
39146       [
39147         "Mauritania (‫موريتانيا‬‎)",
39148         "mr",
39149         "222"
39150       ],
39151       [
39152         "Mauritius (Moris)",
39153         "mu",
39154         "230"
39155       ],
39156       [
39157         "Mayotte",
39158         "yt",
39159         "262",
39160         1
39161       ],
39162       [
39163         "Mexico (México)",
39164         "mx",
39165         "52"
39166       ],
39167       [
39168         "Micronesia",
39169         "fm",
39170         "691"
39171       ],
39172       [
39173         "Moldova (Republica Moldova)",
39174         "md",
39175         "373"
39176       ],
39177       [
39178         "Monaco",
39179         "mc",
39180         "377"
39181       ],
39182       [
39183         "Mongolia (Монгол)",
39184         "mn",
39185         "976"
39186       ],
39187       [
39188         "Montenegro (Crna Gora)",
39189         "me",
39190         "382"
39191       ],
39192       [
39193         "Montserrat",
39194         "ms",
39195         "1664"
39196       ],
39197       [
39198         "Morocco (‫المغرب‬‎)",
39199         "ma",
39200         "212",
39201         0
39202       ],
39203       [
39204         "Mozambique (Moçambique)",
39205         "mz",
39206         "258"
39207       ],
39208       [
39209         "Myanmar (Burma) (မြန်မာ)",
39210         "mm",
39211         "95"
39212       ],
39213       [
39214         "Namibia (Namibië)",
39215         "na",
39216         "264"
39217       ],
39218       [
39219         "Nauru",
39220         "nr",
39221         "674"
39222       ],
39223       [
39224         "Nepal (नेपाल)",
39225         "np",
39226         "977"
39227       ],
39228       [
39229         "Netherlands (Nederland)",
39230         "nl",
39231         "31"
39232       ],
39233       [
39234         "New Caledonia (Nouvelle-Calédonie)",
39235         "nc",
39236         "687"
39237       ],
39238       [
39239         "New Zealand",
39240         "nz",
39241         "64"
39242       ],
39243       [
39244         "Nicaragua",
39245         "ni",
39246         "505"
39247       ],
39248       [
39249         "Niger (Nijar)",
39250         "ne",
39251         "227"
39252       ],
39253       [
39254         "Nigeria",
39255         "ng",
39256         "234"
39257       ],
39258       [
39259         "Niue",
39260         "nu",
39261         "683"
39262       ],
39263       [
39264         "Norfolk Island",
39265         "nf",
39266         "672"
39267       ],
39268       [
39269         "North Korea (조선 민주주의 인민 공화국)",
39270         "kp",
39271         "850"
39272       ],
39273       [
39274         "Northern Mariana Islands",
39275         "mp",
39276         "1670"
39277       ],
39278       [
39279         "Norway (Norge)",
39280         "no",
39281         "47",
39282         0
39283       ],
39284       [
39285         "Oman (‫عُمان‬‎)",
39286         "om",
39287         "968"
39288       ],
39289       [
39290         "Pakistan (‫پاکستان‬‎)",
39291         "pk",
39292         "92"
39293       ],
39294       [
39295         "Palau",
39296         "pw",
39297         "680"
39298       ],
39299       [
39300         "Palestine (‫فلسطين‬‎)",
39301         "ps",
39302         "970"
39303       ],
39304       [
39305         "Panama (Panamá)",
39306         "pa",
39307         "507"
39308       ],
39309       [
39310         "Papua New Guinea",
39311         "pg",
39312         "675"
39313       ],
39314       [
39315         "Paraguay",
39316         "py",
39317         "595"
39318       ],
39319       [
39320         "Peru (Perú)",
39321         "pe",
39322         "51"
39323       ],
39324       [
39325         "Philippines",
39326         "ph",
39327         "63"
39328       ],
39329       [
39330         "Poland (Polska)",
39331         "pl",
39332         "48"
39333       ],
39334       [
39335         "Portugal",
39336         "pt",
39337         "351"
39338       ],
39339       [
39340         "Puerto Rico",
39341         "pr",
39342         "1",
39343         3,
39344         ["787", "939"]
39345       ],
39346       [
39347         "Qatar (‫قطر‬‎)",
39348         "qa",
39349         "974"
39350       ],
39351       [
39352         "Réunion (La Réunion)",
39353         "re",
39354         "262",
39355         0
39356       ],
39357       [
39358         "Romania (România)",
39359         "ro",
39360         "40"
39361       ],
39362       [
39363         "Russia (Россия)",
39364         "ru",
39365         "7",
39366         0
39367       ],
39368       [
39369         "Rwanda",
39370         "rw",
39371         "250"
39372       ],
39373       [
39374         "Saint Barthélemy",
39375         "bl",
39376         "590",
39377         1
39378       ],
39379       [
39380         "Saint Helena",
39381         "sh",
39382         "290"
39383       ],
39384       [
39385         "Saint Kitts and Nevis",
39386         "kn",
39387         "1869"
39388       ],
39389       [
39390         "Saint Lucia",
39391         "lc",
39392         "1758"
39393       ],
39394       [
39395         "Saint Martin (Saint-Martin (partie française))",
39396         "mf",
39397         "590",
39398         2
39399       ],
39400       [
39401         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39402         "pm",
39403         "508"
39404       ],
39405       [
39406         "Saint Vincent and the Grenadines",
39407         "vc",
39408         "1784"
39409       ],
39410       [
39411         "Samoa",
39412         "ws",
39413         "685"
39414       ],
39415       [
39416         "San Marino",
39417         "sm",
39418         "378"
39419       ],
39420       [
39421         "São Tomé and Príncipe (São Tomé e Príncipe)",
39422         "st",
39423         "239"
39424       ],
39425       [
39426         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39427         "sa",
39428         "966"
39429       ],
39430       [
39431         "Senegal (Sénégal)",
39432         "sn",
39433         "221"
39434       ],
39435       [
39436         "Serbia (Србија)",
39437         "rs",
39438         "381"
39439       ],
39440       [
39441         "Seychelles",
39442         "sc",
39443         "248"
39444       ],
39445       [
39446         "Sierra Leone",
39447         "sl",
39448         "232"
39449       ],
39450       [
39451         "Singapore",
39452         "sg",
39453         "65"
39454       ],
39455       [
39456         "Sint Maarten",
39457         "sx",
39458         "1721"
39459       ],
39460       [
39461         "Slovakia (Slovensko)",
39462         "sk",
39463         "421"
39464       ],
39465       [
39466         "Slovenia (Slovenija)",
39467         "si",
39468         "386"
39469       ],
39470       [
39471         "Solomon Islands",
39472         "sb",
39473         "677"
39474       ],
39475       [
39476         "Somalia (Soomaaliya)",
39477         "so",
39478         "252"
39479       ],
39480       [
39481         "South Africa",
39482         "za",
39483         "27"
39484       ],
39485       [
39486         "South Korea (대한민국)",
39487         "kr",
39488         "82"
39489       ],
39490       [
39491         "South Sudan (‫جنوب السودان‬‎)",
39492         "ss",
39493         "211"
39494       ],
39495       [
39496         "Spain (España)",
39497         "es",
39498         "34"
39499       ],
39500       [
39501         "Sri Lanka (ශ්‍රී ලංකාව)",
39502         "lk",
39503         "94"
39504       ],
39505       [
39506         "Sudan (‫السودان‬‎)",
39507         "sd",
39508         "249"
39509       ],
39510       [
39511         "Suriname",
39512         "sr",
39513         "597"
39514       ],
39515       [
39516         "Svalbard and Jan Mayen",
39517         "sj",
39518         "47",
39519         1
39520       ],
39521       [
39522         "Swaziland",
39523         "sz",
39524         "268"
39525       ],
39526       [
39527         "Sweden (Sverige)",
39528         "se",
39529         "46"
39530       ],
39531       [
39532         "Switzerland (Schweiz)",
39533         "ch",
39534         "41"
39535       ],
39536       [
39537         "Syria (‫سوريا‬‎)",
39538         "sy",
39539         "963"
39540       ],
39541       [
39542         "Taiwan (台灣)",
39543         "tw",
39544         "886"
39545       ],
39546       [
39547         "Tajikistan",
39548         "tj",
39549         "992"
39550       ],
39551       [
39552         "Tanzania",
39553         "tz",
39554         "255"
39555       ],
39556       [
39557         "Thailand (ไทย)",
39558         "th",
39559         "66"
39560       ],
39561       [
39562         "Timor-Leste",
39563         "tl",
39564         "670"
39565       ],
39566       [
39567         "Togo",
39568         "tg",
39569         "228"
39570       ],
39571       [
39572         "Tokelau",
39573         "tk",
39574         "690"
39575       ],
39576       [
39577         "Tonga",
39578         "to",
39579         "676"
39580       ],
39581       [
39582         "Trinidad and Tobago",
39583         "tt",
39584         "1868"
39585       ],
39586       [
39587         "Tunisia (‫تونس‬‎)",
39588         "tn",
39589         "216"
39590       ],
39591       [
39592         "Turkey (Türkiye)",
39593         "tr",
39594         "90"
39595       ],
39596       [
39597         "Turkmenistan",
39598         "tm",
39599         "993"
39600       ],
39601       [
39602         "Turks and Caicos Islands",
39603         "tc",
39604         "1649"
39605       ],
39606       [
39607         "Tuvalu",
39608         "tv",
39609         "688"
39610       ],
39611       [
39612         "U.S. Virgin Islands",
39613         "vi",
39614         "1340"
39615       ],
39616       [
39617         "Uganda",
39618         "ug",
39619         "256"
39620       ],
39621       [
39622         "Ukraine (Україна)",
39623         "ua",
39624         "380"
39625       ],
39626       [
39627         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39628         "ae",
39629         "971"
39630       ],
39631       [
39632         "United Kingdom",
39633         "gb",
39634         "44",
39635         0
39636       ],
39637       [
39638         "United States",
39639         "us",
39640         "1",
39641         0
39642       ],
39643       [
39644         "Uruguay",
39645         "uy",
39646         "598"
39647       ],
39648       [
39649         "Uzbekistan (Oʻzbekiston)",
39650         "uz",
39651         "998"
39652       ],
39653       [
39654         "Vanuatu",
39655         "vu",
39656         "678"
39657       ],
39658       [
39659         "Vatican City (Città del Vaticano)",
39660         "va",
39661         "39",
39662         1
39663       ],
39664       [
39665         "Venezuela",
39666         "ve",
39667         "58"
39668       ],
39669       [
39670         "Vietnam (Việt Nam)",
39671         "vn",
39672         "84"
39673       ],
39674       [
39675         "Wallis and Futuna (Wallis-et-Futuna)",
39676         "wf",
39677         "681"
39678       ],
39679       [
39680         "Western Sahara (‫الصحراء الغربية‬‎)",
39681         "eh",
39682         "212",
39683         1
39684       ],
39685       [
39686         "Yemen (‫اليمن‬‎)",
39687         "ye",
39688         "967"
39689       ],
39690       [
39691         "Zambia",
39692         "zm",
39693         "260"
39694       ],
39695       [
39696         "Zimbabwe",
39697         "zw",
39698         "263"
39699       ],
39700       [
39701         "Åland Islands",
39702         "ax",
39703         "358",
39704         1
39705       ]
39706   ];
39707   
39708   return d;
39709 }/**
39710 *    This script refer to:
39711 *    Title: International Telephone Input
39712 *    Author: Jack O'Connor
39713 *    Code version:  v12.1.12
39714 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39715 **/
39716
39717 /**
39718  * @class Roo.bootstrap.PhoneInput
39719  * @extends Roo.bootstrap.TriggerField
39720  * An input with International dial-code selection
39721  
39722  * @cfg {String} defaultDialCode default '+852'
39723  * @cfg {Array} preferedCountries default []
39724   
39725  * @constructor
39726  * Create a new PhoneInput.
39727  * @param {Object} config Configuration options
39728  */
39729
39730 Roo.bootstrap.PhoneInput = function(config) {
39731     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39732 };
39733
39734 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39735         
39736         listWidth: undefined,
39737         
39738         selectedClass: 'active',
39739         
39740         invalidClass : "has-warning",
39741         
39742         validClass: 'has-success',
39743         
39744         allowed: '0123456789',
39745         
39746         /**
39747          * @cfg {String} defaultDialCode The default dial code when initializing the input
39748          */
39749         defaultDialCode: '+852',
39750         
39751         /**
39752          * @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
39753          */
39754         preferedCountries: false,
39755         
39756         getAutoCreate : function()
39757         {
39758             var data = Roo.bootstrap.PhoneInputData();
39759             var align = this.labelAlign || this.parentLabelAlign();
39760             var id = Roo.id();
39761             
39762             this.allCountries = [];
39763             this.dialCodeMapping = [];
39764             
39765             for (var i = 0; i < data.length; i++) {
39766               var c = data[i];
39767               this.allCountries[i] = {
39768                 name: c[0],
39769                 iso2: c[1],
39770                 dialCode: c[2],
39771                 priority: c[3] || 0,
39772                 areaCodes: c[4] || null
39773               };
39774               this.dialCodeMapping[c[2]] = {
39775                   name: c[0],
39776                   iso2: c[1],
39777                   priority: c[3] || 0,
39778                   areaCodes: c[4] || null
39779               };
39780             }
39781             
39782             var cfg = {
39783                 cls: 'form-group',
39784                 cn: []
39785             };
39786             
39787             var input =  {
39788                 tag: 'input',
39789                 id : id,
39790                 cls : 'form-control tel-input',
39791                 autocomplete: 'new-password'
39792             };
39793             
39794             var hiddenInput = {
39795                 tag: 'input',
39796                 type: 'hidden',
39797                 cls: 'hidden-tel-input'
39798             };
39799             
39800             if (this.name) {
39801                 hiddenInput.name = this.name;
39802             }
39803             
39804             if (this.disabled) {
39805                 input.disabled = true;
39806             }
39807             
39808             var flag_container = {
39809                 tag: 'div',
39810                 cls: 'flag-box',
39811                 cn: [
39812                     {
39813                         tag: 'div',
39814                         cls: 'flag'
39815                     },
39816                     {
39817                         tag: 'div',
39818                         cls: 'caret'
39819                     }
39820                 ]
39821             };
39822             
39823             var box = {
39824                 tag: 'div',
39825                 cls: this.hasFeedback ? 'has-feedback' : '',
39826                 cn: [
39827                     hiddenInput,
39828                     input,
39829                     {
39830                         tag: 'input',
39831                         cls: 'dial-code-holder',
39832                         disabled: true
39833                     }
39834                 ]
39835             };
39836             
39837             var container = {
39838                 cls: 'roo-select2-container input-group',
39839                 cn: [
39840                     flag_container,
39841                     box
39842                 ]
39843             };
39844             
39845             if (this.fieldLabel.length) {
39846                 var indicator = {
39847                     tag: 'i',
39848                     tooltip: 'This field is required'
39849                 };
39850                 
39851                 var label = {
39852                     tag: 'label',
39853                     'for':  id,
39854                     cls: 'control-label',
39855                     cn: []
39856                 };
39857                 
39858                 var label_text = {
39859                     tag: 'span',
39860                     html: this.fieldLabel
39861                 };
39862                 
39863                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39864                 label.cn = [
39865                     indicator,
39866                     label_text
39867                 ];
39868                 
39869                 if(this.indicatorpos == 'right') {
39870                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39871                     label.cn = [
39872                         label_text,
39873                         indicator
39874                     ];
39875                 }
39876                 
39877                 if(align == 'left') {
39878                     container = {
39879                         tag: 'div',
39880                         cn: [
39881                             container
39882                         ]
39883                     };
39884                     
39885                     if(this.labelWidth > 12){
39886                         label.style = "width: " + this.labelWidth + 'px';
39887                     }
39888                     if(this.labelWidth < 13 && this.labelmd == 0){
39889                         this.labelmd = this.labelWidth;
39890                     }
39891                     if(this.labellg > 0){
39892                         label.cls += ' col-lg-' + this.labellg;
39893                         input.cls += ' col-lg-' + (12 - this.labellg);
39894                     }
39895                     if(this.labelmd > 0){
39896                         label.cls += ' col-md-' + this.labelmd;
39897                         container.cls += ' col-md-' + (12 - this.labelmd);
39898                     }
39899                     if(this.labelsm > 0){
39900                         label.cls += ' col-sm-' + this.labelsm;
39901                         container.cls += ' col-sm-' + (12 - this.labelsm);
39902                     }
39903                     if(this.labelxs > 0){
39904                         label.cls += ' col-xs-' + this.labelxs;
39905                         container.cls += ' col-xs-' + (12 - this.labelxs);
39906                     }
39907                 }
39908             }
39909             
39910             cfg.cn = [
39911                 label,
39912                 container
39913             ];
39914             
39915             var settings = this;
39916             
39917             ['xs','sm','md','lg'].map(function(size){
39918                 if (settings[size]) {
39919                     cfg.cls += ' col-' + size + '-' + settings[size];
39920                 }
39921             });
39922             
39923             this.store = new Roo.data.Store({
39924                 proxy : new Roo.data.MemoryProxy({}),
39925                 reader : new Roo.data.JsonReader({
39926                     fields : [
39927                         {
39928                             'name' : 'name',
39929                             'type' : 'string'
39930                         },
39931                         {
39932                             'name' : 'iso2',
39933                             'type' : 'string'
39934                         },
39935                         {
39936                             'name' : 'dialCode',
39937                             'type' : 'string'
39938                         },
39939                         {
39940                             'name' : 'priority',
39941                             'type' : 'string'
39942                         },
39943                         {
39944                             'name' : 'areaCodes',
39945                             'type' : 'string'
39946                         }
39947                     ]
39948                 })
39949             });
39950             
39951             if(!this.preferedCountries) {
39952                 this.preferedCountries = [
39953                     'hk',
39954                     'gb',
39955                     'us'
39956                 ];
39957             }
39958             
39959             var p = this.preferedCountries.reverse();
39960             
39961             if(p) {
39962                 for (var i = 0; i < p.length; i++) {
39963                     for (var j = 0; j < this.allCountries.length; j++) {
39964                         if(this.allCountries[j].iso2 == p[i]) {
39965                             var t = this.allCountries[j];
39966                             this.allCountries.splice(j,1);
39967                             this.allCountries.unshift(t);
39968                         }
39969                     } 
39970                 }
39971             }
39972             
39973             this.store.proxy.data = {
39974                 success: true,
39975                 data: this.allCountries
39976             };
39977             
39978             return cfg;
39979         },
39980         
39981         initEvents : function()
39982         {
39983             this.createList();
39984             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39985             
39986             this.indicator = this.indicatorEl();
39987             this.flag = this.flagEl();
39988             this.dialCodeHolder = this.dialCodeHolderEl();
39989             
39990             this.trigger = this.el.select('div.flag-box',true).first();
39991             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39992             
39993             var _this = this;
39994             
39995             (function(){
39996                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39997                 _this.list.setWidth(lw);
39998             }).defer(100);
39999             
40000             this.list.on('mouseover', this.onViewOver, this);
40001             this.list.on('mousemove', this.onViewMove, this);
40002             this.inputEl().on("keyup", this.onKeyUp, this);
40003             
40004             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40005
40006             this.view = new Roo.View(this.list, this.tpl, {
40007                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40008             });
40009             
40010             this.view.on('click', this.onViewClick, this);
40011             this.setValue(this.defaultDialCode);
40012         },
40013         
40014         onTriggerClick : function(e)
40015         {
40016             Roo.log('trigger click');
40017             if(this.disabled){
40018                 return;
40019             }
40020             
40021             if(this.isExpanded()){
40022                 this.collapse();
40023                 this.hasFocus = false;
40024             }else {
40025                 this.store.load({});
40026                 this.hasFocus = true;
40027                 this.expand();
40028             }
40029         },
40030         
40031         isExpanded : function()
40032         {
40033             return this.list.isVisible();
40034         },
40035         
40036         collapse : function()
40037         {
40038             if(!this.isExpanded()){
40039                 return;
40040             }
40041             this.list.hide();
40042             Roo.get(document).un('mousedown', this.collapseIf, this);
40043             Roo.get(document).un('mousewheel', this.collapseIf, this);
40044             this.fireEvent('collapse', this);
40045             this.validate();
40046         },
40047         
40048         expand : function()
40049         {
40050             Roo.log('expand');
40051
40052             if(this.isExpanded() || !this.hasFocus){
40053                 return;
40054             }
40055             
40056             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40057             this.list.setWidth(lw);
40058             
40059             this.list.show();
40060             this.restrictHeight();
40061             
40062             Roo.get(document).on('mousedown', this.collapseIf, this);
40063             Roo.get(document).on('mousewheel', this.collapseIf, this);
40064             
40065             this.fireEvent('expand', this);
40066         },
40067         
40068         restrictHeight : function()
40069         {
40070             this.list.alignTo(this.inputEl(), this.listAlign);
40071             this.list.alignTo(this.inputEl(), this.listAlign);
40072         },
40073         
40074         onViewOver : function(e, t)
40075         {
40076             if(this.inKeyMode){
40077                 return;
40078             }
40079             var item = this.view.findItemFromChild(t);
40080             
40081             if(item){
40082                 var index = this.view.indexOf(item);
40083                 this.select(index, false);
40084             }
40085         },
40086
40087         // private
40088         onViewClick : function(view, doFocus, el, e)
40089         {
40090             var index = this.view.getSelectedIndexes()[0];
40091             
40092             var r = this.store.getAt(index);
40093             
40094             if(r){
40095                 this.onSelect(r, index);
40096             }
40097             if(doFocus !== false && !this.blockFocus){
40098                 this.inputEl().focus();
40099             }
40100         },
40101         
40102         onViewMove : function(e, t)
40103         {
40104             this.inKeyMode = false;
40105         },
40106         
40107         select : function(index, scrollIntoView)
40108         {
40109             this.selectedIndex = index;
40110             this.view.select(index);
40111             if(scrollIntoView !== false){
40112                 var el = this.view.getNode(index);
40113                 if(el){
40114                     this.list.scrollChildIntoView(el, false);
40115                 }
40116             }
40117         },
40118         
40119         createList : function()
40120         {
40121             this.list = Roo.get(document.body).createChild({
40122                 tag: 'ul',
40123                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40124                 style: 'display:none'
40125             });
40126             
40127             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40128         },
40129         
40130         collapseIf : function(e)
40131         {
40132             var in_combo  = e.within(this.el);
40133             var in_list =  e.within(this.list);
40134             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40135             
40136             if (in_combo || in_list || is_list) {
40137                 return;
40138             }
40139             this.collapse();
40140         },
40141         
40142         onSelect : function(record, index)
40143         {
40144             if(this.fireEvent('beforeselect', this, record, index) !== false){
40145                 
40146                 this.setFlagClass(record.data.iso2);
40147                 this.setDialCode(record.data.dialCode);
40148                 this.hasFocus = false;
40149                 this.collapse();
40150                 this.fireEvent('select', this, record, index);
40151             }
40152         },
40153         
40154         flagEl : function()
40155         {
40156             var flag = this.el.select('div.flag',true).first();
40157             if(!flag){
40158                 return false;
40159             }
40160             return flag;
40161         },
40162         
40163         dialCodeHolderEl : function()
40164         {
40165             var d = this.el.select('input.dial-code-holder',true).first();
40166             if(!d){
40167                 return false;
40168             }
40169             return d;
40170         },
40171         
40172         setDialCode : function(v)
40173         {
40174             this.dialCodeHolder.dom.value = '+'+v;
40175         },
40176         
40177         setFlagClass : function(n)
40178         {
40179             this.flag.dom.className = 'flag '+n;
40180         },
40181         
40182         getValue : function()
40183         {
40184             var v = this.inputEl().getValue();
40185             if(this.dialCodeHolder) {
40186                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40187             }
40188             return v;
40189         },
40190         
40191         setValue : function(v)
40192         {
40193             var d = this.getDialCode(v);
40194             
40195             //invalid dial code
40196             if(v.length == 0 || !d || d.length == 0) {
40197                 if(this.rendered){
40198                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40199                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40200                 }
40201                 return;
40202             }
40203             
40204             //valid dial code
40205             this.setFlagClass(this.dialCodeMapping[d].iso2);
40206             this.setDialCode(d);
40207             this.inputEl().dom.value = v.replace('+'+d,'');
40208             this.hiddenEl().dom.value = this.getValue();
40209             
40210             this.validate();
40211         },
40212         
40213         getDialCode : function(v)
40214         {
40215             v = v ||  '';
40216             
40217             if (v.length == 0) {
40218                 return this.dialCodeHolder.dom.value;
40219             }
40220             
40221             var dialCode = "";
40222             if (v.charAt(0) != "+") {
40223                 return false;
40224             }
40225             var numericChars = "";
40226             for (var i = 1; i < v.length; i++) {
40227               var c = v.charAt(i);
40228               if (!isNaN(c)) {
40229                 numericChars += c;
40230                 if (this.dialCodeMapping[numericChars]) {
40231                   dialCode = v.substr(1, i);
40232                 }
40233                 if (numericChars.length == 4) {
40234                   break;
40235                 }
40236               }
40237             }
40238             return dialCode;
40239         },
40240         
40241         reset : function()
40242         {
40243             this.setValue(this.defaultDialCode);
40244             this.validate();
40245         },
40246         
40247         hiddenEl : function()
40248         {
40249             return this.el.select('input.hidden-tel-input',true).first();
40250         },
40251         
40252         onKeyUp : function(e){
40253             
40254             var k = e.getKey();
40255             var c = e.getCharCode();
40256             
40257             if(
40258                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40259                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40260             ){
40261                 e.stopEvent();
40262             }
40263             
40264             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40265             //     return;
40266             // }
40267             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40268                 e.stopEvent();
40269             }
40270             
40271             this.setValue(this.getValue());
40272         }
40273         
40274 });
40275 /**
40276  * @class Roo.bootstrap.MoneyField
40277  * @extends Roo.bootstrap.ComboBox
40278  * Bootstrap MoneyField class
40279  * 
40280  * @constructor
40281  * Create a new MoneyField.
40282  * @param {Object} config Configuration options
40283  */
40284
40285 Roo.bootstrap.MoneyField = function(config) {
40286     
40287     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40288     
40289 };
40290
40291 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40292     
40293     /**
40294      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40295      */
40296     allowDecimals : true,
40297     /**
40298      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40299      */
40300     decimalSeparator : ".",
40301     /**
40302      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40303      */
40304     decimalPrecision : 0,
40305     /**
40306      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40307      */
40308     allowNegative : true,
40309     /**
40310      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40311      */
40312     allowZero: true,
40313     /**
40314      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40315      */
40316     minValue : Number.NEGATIVE_INFINITY,
40317     /**
40318      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40319      */
40320     maxValue : Number.MAX_VALUE,
40321     /**
40322      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40323      */
40324     minText : "The minimum value for this field is {0}",
40325     /**
40326      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40327      */
40328     maxText : "The maximum value for this field is {0}",
40329     /**
40330      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40331      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40332      */
40333     nanText : "{0} is not a valid number",
40334     /**
40335      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40336      */
40337     castInt : true,
40338     /**
40339      * @cfg {String} defaults currency of the MoneyField
40340      * value should be in lkey
40341      */
40342     defaultCurrency : false,
40343     /**
40344      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40345      */
40346     thousandsDelimiter : false,
40347     
40348     
40349     inputlg : 9,
40350     inputmd : 9,
40351     inputsm : 9,
40352     inputxs : 6,
40353     
40354     store : false,
40355     
40356     getAutoCreate : function()
40357     {
40358         var align = this.labelAlign || this.parentLabelAlign();
40359         
40360         var id = Roo.id();
40361
40362         var cfg = {
40363             cls: 'form-group',
40364             cn: []
40365         };
40366
40367         var input =  {
40368             tag: 'input',
40369             id : id,
40370             cls : 'form-control roo-money-amount-input',
40371             autocomplete: 'new-password'
40372         };
40373         
40374         var hiddenInput = {
40375             tag: 'input',
40376             type: 'hidden',
40377             id: Roo.id(),
40378             cls: 'hidden-number-input'
40379         };
40380         
40381         if (this.name) {
40382             hiddenInput.name = this.name;
40383         }
40384
40385         if (this.disabled) {
40386             input.disabled = true;
40387         }
40388
40389         var clg = 12 - this.inputlg;
40390         var cmd = 12 - this.inputmd;
40391         var csm = 12 - this.inputsm;
40392         var cxs = 12 - this.inputxs;
40393         
40394         var container = {
40395             tag : 'div',
40396             cls : 'row roo-money-field',
40397             cn : [
40398                 {
40399                     tag : 'div',
40400                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40401                     cn : [
40402                         {
40403                             tag : 'div',
40404                             cls: 'roo-select2-container input-group',
40405                             cn: [
40406                                 {
40407                                     tag : 'input',
40408                                     cls : 'form-control roo-money-currency-input',
40409                                     autocomplete: 'new-password',
40410                                     readOnly : 1,
40411                                     name : this.currencyName
40412                                 },
40413                                 {
40414                                     tag :'span',
40415                                     cls : 'input-group-addon',
40416                                     cn : [
40417                                         {
40418                                             tag: 'span',
40419                                             cls: 'caret'
40420                                         }
40421                                     ]
40422                                 }
40423                             ]
40424                         }
40425                     ]
40426                 },
40427                 {
40428                     tag : 'div',
40429                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40430                     cn : [
40431                         {
40432                             tag: 'div',
40433                             cls: this.hasFeedback ? 'has-feedback' : '',
40434                             cn: [
40435                                 input
40436                             ]
40437                         }
40438                     ]
40439                 }
40440             ]
40441             
40442         };
40443         
40444         if (this.fieldLabel.length) {
40445             var indicator = {
40446                 tag: 'i',
40447                 tooltip: 'This field is required'
40448             };
40449
40450             var label = {
40451                 tag: 'label',
40452                 'for':  id,
40453                 cls: 'control-label',
40454                 cn: []
40455             };
40456
40457             var label_text = {
40458                 tag: 'span',
40459                 html: this.fieldLabel
40460             };
40461
40462             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40463             label.cn = [
40464                 indicator,
40465                 label_text
40466             ];
40467
40468             if(this.indicatorpos == 'right') {
40469                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40470                 label.cn = [
40471                     label_text,
40472                     indicator
40473                 ];
40474             }
40475
40476             if(align == 'left') {
40477                 container = {
40478                     tag: 'div',
40479                     cn: [
40480                         container
40481                     ]
40482                 };
40483
40484                 if(this.labelWidth > 12){
40485                     label.style = "width: " + this.labelWidth + 'px';
40486                 }
40487                 if(this.labelWidth < 13 && this.labelmd == 0){
40488                     this.labelmd = this.labelWidth;
40489                 }
40490                 if(this.labellg > 0){
40491                     label.cls += ' col-lg-' + this.labellg;
40492                     input.cls += ' col-lg-' + (12 - this.labellg);
40493                 }
40494                 if(this.labelmd > 0){
40495                     label.cls += ' col-md-' + this.labelmd;
40496                     container.cls += ' col-md-' + (12 - this.labelmd);
40497                 }
40498                 if(this.labelsm > 0){
40499                     label.cls += ' col-sm-' + this.labelsm;
40500                     container.cls += ' col-sm-' + (12 - this.labelsm);
40501                 }
40502                 if(this.labelxs > 0){
40503                     label.cls += ' col-xs-' + this.labelxs;
40504                     container.cls += ' col-xs-' + (12 - this.labelxs);
40505                 }
40506             }
40507         }
40508
40509         cfg.cn = [
40510             label,
40511             container,
40512             hiddenInput
40513         ];
40514         
40515         var settings = this;
40516
40517         ['xs','sm','md','lg'].map(function(size){
40518             if (settings[size]) {
40519                 cfg.cls += ' col-' + size + '-' + settings[size];
40520             }
40521         });
40522         
40523         return cfg;
40524     },
40525     
40526     initEvents : function()
40527     {
40528         this.indicator = this.indicatorEl();
40529         
40530         this.initCurrencyEvent();
40531         
40532         this.initNumberEvent();
40533     },
40534     
40535     initCurrencyEvent : function()
40536     {
40537         if (!this.store) {
40538             throw "can not find store for combo";
40539         }
40540         
40541         this.store = Roo.factory(this.store, Roo.data);
40542         this.store.parent = this;
40543         
40544         this.createList();
40545         
40546         this.triggerEl = this.el.select('.input-group-addon', true).first();
40547         
40548         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40549         
40550         var _this = this;
40551         
40552         (function(){
40553             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40554             _this.list.setWidth(lw);
40555         }).defer(100);
40556         
40557         this.list.on('mouseover', this.onViewOver, this);
40558         this.list.on('mousemove', this.onViewMove, this);
40559         this.list.on('scroll', this.onViewScroll, this);
40560         
40561         if(!this.tpl){
40562             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40563         }
40564         
40565         this.view = new Roo.View(this.list, this.tpl, {
40566             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40567         });
40568         
40569         this.view.on('click', this.onViewClick, this);
40570         
40571         this.store.on('beforeload', this.onBeforeLoad, this);
40572         this.store.on('load', this.onLoad, this);
40573         this.store.on('loadexception', this.onLoadException, this);
40574         
40575         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40576             "up" : function(e){
40577                 this.inKeyMode = true;
40578                 this.selectPrev();
40579             },
40580
40581             "down" : function(e){
40582                 if(!this.isExpanded()){
40583                     this.onTriggerClick();
40584                 }else{
40585                     this.inKeyMode = true;
40586                     this.selectNext();
40587                 }
40588             },
40589
40590             "enter" : function(e){
40591                 this.collapse();
40592                 
40593                 if(this.fireEvent("specialkey", this, e)){
40594                     this.onViewClick(false);
40595                 }
40596                 
40597                 return true;
40598             },
40599
40600             "esc" : function(e){
40601                 this.collapse();
40602             },
40603
40604             "tab" : function(e){
40605                 this.collapse();
40606                 
40607                 if(this.fireEvent("specialkey", this, e)){
40608                     this.onViewClick(false);
40609                 }
40610                 
40611                 return true;
40612             },
40613
40614             scope : this,
40615
40616             doRelay : function(foo, bar, hname){
40617                 if(hname == 'down' || this.scope.isExpanded()){
40618                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40619                 }
40620                 return true;
40621             },
40622
40623             forceKeyDown: true
40624         });
40625         
40626         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40627         
40628     },
40629     
40630     initNumberEvent : function(e)
40631     {
40632         this.inputEl().on("keydown" , this.fireKey,  this);
40633         this.inputEl().on("focus", this.onFocus,  this);
40634         this.inputEl().on("blur", this.onBlur,  this);
40635         
40636         this.inputEl().relayEvent('keyup', this);
40637         
40638         if(this.indicator){
40639             this.indicator.addClass('invisible');
40640         }
40641  
40642         this.originalValue = this.getValue();
40643         
40644         if(this.validationEvent == 'keyup'){
40645             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40646             this.inputEl().on('keyup', this.filterValidation, this);
40647         }
40648         else if(this.validationEvent !== false){
40649             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40650         }
40651         
40652         if(this.selectOnFocus){
40653             this.on("focus", this.preFocus, this);
40654             
40655         }
40656         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40657             this.inputEl().on("keypress", this.filterKeys, this);
40658         } else {
40659             this.inputEl().relayEvent('keypress', this);
40660         }
40661         
40662         var allowed = "0123456789";
40663         
40664         if(this.allowDecimals){
40665             allowed += this.decimalSeparator;
40666         }
40667         
40668         if(this.allowNegative){
40669             allowed += "-";
40670         }
40671         
40672         if(this.thousandsDelimiter) {
40673             allowed += ",";
40674         }
40675         
40676         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40677         
40678         var keyPress = function(e){
40679             
40680             var k = e.getKey();
40681             
40682             var c = e.getCharCode();
40683             
40684             if(
40685                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40686                     allowed.indexOf(String.fromCharCode(c)) === -1
40687             ){
40688                 e.stopEvent();
40689                 return;
40690             }
40691             
40692             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40693                 return;
40694             }
40695             
40696             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40697                 e.stopEvent();
40698             }
40699         };
40700         
40701         this.inputEl().on("keypress", keyPress, this);
40702         
40703     },
40704     
40705     onTriggerClick : function(e)
40706     {   
40707         if(this.disabled){
40708             return;
40709         }
40710         
40711         this.page = 0;
40712         this.loadNext = false;
40713         
40714         if(this.isExpanded()){
40715             this.collapse();
40716             return;
40717         }
40718         
40719         this.hasFocus = true;
40720         
40721         if(this.triggerAction == 'all') {
40722             this.doQuery(this.allQuery, true);
40723             return;
40724         }
40725         
40726         this.doQuery(this.getRawValue());
40727     },
40728     
40729     getCurrency : function()
40730     {   
40731         var v = this.currencyEl().getValue();
40732         
40733         return v;
40734     },
40735     
40736     restrictHeight : function()
40737     {
40738         this.list.alignTo(this.currencyEl(), this.listAlign);
40739         this.list.alignTo(this.currencyEl(), this.listAlign);
40740     },
40741     
40742     onViewClick : function(view, doFocus, el, e)
40743     {
40744         var index = this.view.getSelectedIndexes()[0];
40745         
40746         var r = this.store.getAt(index);
40747         
40748         if(r){
40749             this.onSelect(r, index);
40750         }
40751     },
40752     
40753     onSelect : function(record, index){
40754         
40755         if(this.fireEvent('beforeselect', this, record, index) !== false){
40756         
40757             this.setFromCurrencyData(index > -1 ? record.data : false);
40758             
40759             this.collapse();
40760             
40761             this.fireEvent('select', this, record, index);
40762         }
40763     },
40764     
40765     setFromCurrencyData : function(o)
40766     {
40767         var currency = '';
40768         
40769         this.lastCurrency = o;
40770         
40771         if (this.currencyField) {
40772             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40773         } else {
40774             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40775         }
40776         
40777         this.lastSelectionText = currency;
40778         
40779         //setting default currency
40780         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40781             this.setCurrency(this.defaultCurrency);
40782             return;
40783         }
40784         
40785         this.setCurrency(currency);
40786     },
40787     
40788     setFromData : function(o)
40789     {
40790         var c = {};
40791         
40792         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40793         
40794         this.setFromCurrencyData(c);
40795         
40796         var value = '';
40797         
40798         if (this.name) {
40799             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40800         } else {
40801             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40802         }
40803         
40804         this.setValue(value);
40805         
40806     },
40807     
40808     setCurrency : function(v)
40809     {   
40810         this.currencyValue = v;
40811         
40812         if(this.rendered){
40813             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40814             this.validate();
40815         }
40816     },
40817     
40818     setValue : function(v)
40819     {
40820         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40821         
40822         this.value = v;
40823         
40824         if(this.rendered){
40825             
40826             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40827             
40828             this.inputEl().dom.value = (v == '') ? '' :
40829                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40830             
40831             if(!this.allowZero && v === '0') {
40832                 this.hiddenEl().dom.value = '';
40833                 this.inputEl().dom.value = '';
40834             }
40835             
40836             this.validate();
40837         }
40838     },
40839     
40840     getRawValue : function()
40841     {
40842         var v = this.inputEl().getValue();
40843         
40844         return v;
40845     },
40846     
40847     getValue : function()
40848     {
40849         return this.fixPrecision(this.parseValue(this.getRawValue()));
40850     },
40851     
40852     parseValue : function(value)
40853     {
40854         if(this.thousandsDelimiter) {
40855             value += "";
40856             r = new RegExp(",", "g");
40857             value = value.replace(r, "");
40858         }
40859         
40860         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40861         return isNaN(value) ? '' : value;
40862         
40863     },
40864     
40865     fixPrecision : function(value)
40866     {
40867         if(this.thousandsDelimiter) {
40868             value += "";
40869             r = new RegExp(",", "g");
40870             value = value.replace(r, "");
40871         }
40872         
40873         var nan = isNaN(value);
40874         
40875         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40876             return nan ? '' : value;
40877         }
40878         return parseFloat(value).toFixed(this.decimalPrecision);
40879     },
40880     
40881     decimalPrecisionFcn : function(v)
40882     {
40883         return Math.floor(v);
40884     },
40885     
40886     validateValue : function(value)
40887     {
40888         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40889             return false;
40890         }
40891         
40892         var num = this.parseValue(value);
40893         
40894         if(isNaN(num)){
40895             this.markInvalid(String.format(this.nanText, value));
40896             return false;
40897         }
40898         
40899         if(num < this.minValue){
40900             this.markInvalid(String.format(this.minText, this.minValue));
40901             return false;
40902         }
40903         
40904         if(num > this.maxValue){
40905             this.markInvalid(String.format(this.maxText, this.maxValue));
40906             return false;
40907         }
40908         
40909         return true;
40910     },
40911     
40912     validate : function()
40913     {
40914         if(this.disabled || this.allowBlank){
40915             this.markValid();
40916             return true;
40917         }
40918         
40919         var currency = this.getCurrency();
40920         
40921         if(this.validateValue(this.getRawValue()) && currency.length){
40922             this.markValid();
40923             return true;
40924         }
40925         
40926         this.markInvalid();
40927         return false;
40928     },
40929     
40930     getName: function()
40931     {
40932         return this.name;
40933     },
40934     
40935     beforeBlur : function()
40936     {
40937         if(!this.castInt){
40938             return;
40939         }
40940         
40941         var v = this.parseValue(this.getRawValue());
40942         
40943         if(v || v == 0){
40944             this.setValue(v);
40945         }
40946     },
40947     
40948     onBlur : function()
40949     {
40950         this.beforeBlur();
40951         
40952         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40953             //this.el.removeClass(this.focusClass);
40954         }
40955         
40956         this.hasFocus = false;
40957         
40958         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40959             this.validate();
40960         }
40961         
40962         var v = this.getValue();
40963         
40964         if(String(v) !== String(this.startValue)){
40965             this.fireEvent('change', this, v, this.startValue);
40966         }
40967         
40968         this.fireEvent("blur", this);
40969     },
40970     
40971     inputEl : function()
40972     {
40973         return this.el.select('.roo-money-amount-input', true).first();
40974     },
40975     
40976     currencyEl : function()
40977     {
40978         return this.el.select('.roo-money-currency-input', true).first();
40979     },
40980     
40981     hiddenEl : function()
40982     {
40983         return this.el.select('input.hidden-number-input',true).first();
40984     }
40985     
40986 });